builtins/fs_temp.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230
use crate::rand::rand_alphanumeric_str;
use bridge_macros::sl_sh_fn;
use compile_state::state::SloshVm;
use rand;
use shell::builtins::expand_tilde;
use slvm::{VMError, VMResult};
use std::ffi::OsStr;
use std::fs::File;
use std::path::{Path, PathBuf};
use std::{env, fs};
const LEN: i64 = 5;
/// Usage: (get-temp ["/path/to/directory/to/use/as/base" "optional-prefix" "optional-suffix" length])
///
/// Creates a directory inside of an OS specific temporary directory. See [temp-dir](root::temp-dir)
/// for OS specific notes. Also accepts an optional prefix, an optional suffix, and an optional
/// length for the random number of characters in the temporary file created. Defaults to prefix of
/// ".tmp", no suffix, and five random characters.
///
/// Section: file
///
/// Example:
/// (test::assert-true (str-contains (get-temp) (temp-dir)))
///
/// (with-temp (fn (tmp)
/// (let (tmp-dir (get-temp tmp))
/// (test::assert-true (str-contains tmp-dir tmp)))))
///
/// (with-temp (fn (tmp)
/// (let (tmp-dir (get-temp tmp "some-prefix"))
/// (test::assert-true (str-contains tmp-dir tmp))
/// (test::assert-true (str-contains tmp-dir "some-prefix")))))
///
/// (with-temp (fn (tmp)
/// (let (tmp-dir (get-temp tmp "some-prefix" "some-suffix"))
/// (test::assert-true (str-contains tmp-dir tmp))
/// (test::assert-true (str-contains tmp-dir "some-prefix"))
/// (test::assert-true (str-contains tmp-dir "some-suffix")))))
///
/// (with-temp (fn (tmp)
/// (let (tmp-dir (get-temp tmp "some-prefix" "some-suffix" 6))
/// (test::assert-true (str-contains tmp-dir tmp))
/// (test::assert-true (str-contains tmp-dir "some-prefix"))
/// (test::assert-true (str-contains tmp-dir "some-suffix"))
/// (test::assert-equal (len "some-prefix012345some-suffix") (len (fs-base tmp-dir))))))
#[sl_sh_fn(fn_name = "get-temp")]
fn get_temp(
path: Option<String>,
prefix: Option<String>,
suffix: Option<String>,
len: Option<i64>,
) -> VMResult<String> {
let fn_name = "get-temp";
let dir = get_provided_or_default_temp(path, fn_name)?;
let prefix = prefix.as_deref().unwrap_or_default();
let suffix = suffix.as_deref().unwrap_or_default();
let len = len.unwrap_or(LEN);
let dir = create_temp_dir(dir.as_path(), prefix, suffix, len, fn_name)?;
if let Some(path) = dir.to_str() {
let path = path.to_string();
Ok(path)
} else {
let msg = format!("{} unable to provide temporary directory", fn_name);
Err(VMError::new("io", msg))
}
}
/// Usage: (temp-dir)
///
/// Returns a string representing the temporary directory. See [get-temp](root::get-temp) for higher
/// level temporary directory creation mechanism.
///
/// On Unix:
/// Returns the value of the TMPDIR environment variable if it is set, otherwise for non-Android it
/// returns /tmp. If Android, since there is no global temporary folder (it is usually allocated
/// per-app), it returns /data/local/tmp.
///
/// On Windows:
/// Returns the value of, in order, the TMP, TEMP, USERPROFILE environment variable if any are set and
/// not the empty string. Otherwise, temp_dir returns the path of the Windows directory. This behavior
/// is identical to that of GetTempPath, which this function uses internally.
///
/// Section: file
///
/// Example:
/// (test::assert-true (fs-dir? (temp-dir)))
#[sl_sh_fn(fn_name = "temp-dir")]
fn builtin_temp_dir() -> VMResult<String> {
if let Some(path) = temp_dir().to_str() {
Ok(path.to_string())
} else {
Err(VMError::new(
"io",
"temp-dir: unable to provide temporary directory".to_string(),
))
}
}
fn get_provided_or_default_temp(path: Option<String>, fn_name: &str) -> VMResult<PathBuf> {
match path {
None => Ok(temp_dir()),
Some(path) => {
let dir = expand_tilde(path.into());
let p = dir.as_path();
if p.exists() && p.is_dir() {
Ok(dir)
} else {
let msg = format!(
"{} unable to provide temporary file in provided directory",
fn_name
);
Err(VMError::new("io", msg))
}
}
}
}
fn create_temp_dir(
path: &Path,
prefix: &str,
suffix: &str,
len: i64,
fn_name: &str,
) -> VMResult<PathBuf> {
if path.exists() && path.is_dir() {
let dir_name = random_name(prefix, suffix, len);
let dir = Path::new::<OsStr>(dir_name.as_ref());
let dir = path.join(dir);
fs::create_dir(dir.as_path()).map_err(|err| {
let msg = format!("{} unable to create temporary directory inside default temporary directory ({:?}), reason: {:?}", fn_name, dir.as_path(), err);
VMError::new("io", msg)
})?;
Ok(dir)
} else {
let msg = format!("{} unable to provide temporary directory", fn_name);
Err(VMError::new("io", msg))
}
}
fn random_name(prefix: &str, suffix: &str, len: i64) -> String {
let prefix = if prefix.is_empty() { ".tmp" } else { prefix };
let mut rng = rand::thread_rng();
let name = rand_alphanumeric_str(len.unsigned_abs(), &mut rng);
format!("{}{}{}", prefix, name, suffix)
}
fn temp_dir() -> PathBuf {
env::temp_dir()
}
fn create_temp_file(
dir: PathBuf,
prefix: &Option<String>,
suffix: &Option<String>,
len: Option<i64>,
fn_name: &str,
) -> VMResult<PathBuf> {
let prefix = prefix.as_ref().map(|x| x.as_str()).unwrap_or_default();
let suffix = suffix.as_ref().map(|x| x.as_str()).unwrap_or_default();
let len = len.unwrap_or(LEN);
let filename = random_name(prefix, suffix, len);
let p = Path::new::<OsStr>(filename.as_ref());
let file = dir.join(p);
let p = file.as_path();
File::create(p).map_err(|err| {
let msg = format!(
"{} unable to create temporary file inside temporary directory ({:?}), reason: {:?}",
fn_name,
dir.as_path(),
err
);
VMError::new("io", msg)
})?;
Ok(file)
}
/// Usage: (get-temp-file ["/path/to/directory/to/use/as/base" "optional-prefix" "optional-suffix" length])
///
/// Returns name of file created inside temporary directory. Optionally takes a directory to use as
/// the parent directory of the temporary file. Also accepts an optional prefix, an optional suffix,
/// and an optional length for the random number of characters in the temporary files created. Defaults
/// to prefix of ".tmp", no suffix, and five random characters.
///
/// Section: file
///
/// Example:
/// (test::assert-true (str-contains (get-temp-file) (temp-dir)))
///
/// (with-temp (fn (tmp)
/// (let (tmp-file (get-temp-file tmp))
/// (test::assert-true (str-contains tmp-file tmp)))))
///
/// (with-temp (fn (tmp)
/// (let (tmp-file (get-temp-file tmp "some-prefix"))
/// (test::assert-true (str-contains tmp-file "some-prefix")))))
///
/// (with-temp (fn (tmp)
/// (let (tmp-file (get-temp-file tmp "some-prefix" "some-suffix"))
/// (test::assert-true (str-contains tmp-file "some-prefix"))
/// (test::assert-true (str-contains tmp-file "some-suffix")))))
///
/// (with-temp (fn (tmp)
/// (let (tmp-file (get-temp-file tmp "some-prefix" "some-suffix" 10))
/// (test::assert-true (str-contains tmp-file "some-prefix"))
/// (test::assert-true (str-contains tmp-file "some-suffix"))
/// (test::assert-equal (len "some-prefix0123456789some-suffix") (len (fs-base tmp-file))))))
#[sl_sh_fn(fn_name = "get-temp-file")]
fn get_temp_file(
path: Option<String>,
prefix: Option<String>,
suffix: Option<String>,
len: Option<i64>,
) -> VMResult<String> {
let fn_name = "get-temp-file";
let dir = get_provided_or_default_temp(path, fn_name)?;
let file = create_temp_file(dir, &prefix, &suffix, len, fn_name)?;
if let Some(path) = file.as_path().to_str() {
Ok(path.to_string())
} else {
let msg = format!("{} unable to provide temporary file", fn_name);
Err(VMError::new("io", msg))
}
}
pub fn add_fs_temp_builtins(env: &mut SloshVm) {
intern_get_temp(env);
intern_get_temp_file(env);
intern_builtin_temp_dir(env);
}