snix_eval/builtins/
impure.rs

1use builtin_macros::builtins;
2use genawaiter::rc::Gen;
3
4use std::{
5    env,
6    time::{SystemTime, UNIX_EPOCH},
7};
8
9use crate::{
10    self as snix_eval,
11    errors::ErrorKind,
12    value::NixAttrs,
13    vm::generators::{self, GenCo},
14    NixString, Value,
15};
16
17#[builtins]
18mod impure_builtins {
19    use std::ffi::OsStr;
20    use std::os::unix::ffi::OsStrExt;
21
22    use super::*;
23    use crate::builtins::{coerce_value_to_path, hash::hash_nix_string};
24
25    #[builtin("getEnv")]
26    async fn builtin_get_env(co: GenCo, var: Value) -> Result<Value, ErrorKind> {
27        Ok(env::var(OsStr::from_bytes(&var.to_str()?))
28            .unwrap_or_else(|_| "".into())
29            .into())
30    }
31
32    #[builtin("hashFile")]
33    async fn builtin_hash_file(co: GenCo, algo: Value, path: Value) -> Result<Value, ErrorKind> {
34        let path = match coerce_value_to_path(&co, path).await? {
35            Err(cek) => return Ok(Value::from(cek)),
36            Ok(p) => p,
37        };
38        let r = generators::request_open_file(&co, path).await;
39        hash_nix_string(algo.to_str()?, r).map(Value::from)
40    }
41
42    #[builtin("pathExists")]
43    async fn builtin_path_exists(co: GenCo, path: Value) -> Result<Value, ErrorKind> {
44        match coerce_value_to_path(&co, path).await? {
45            Err(cek) => Ok(Value::from(cek)),
46            Ok(path) => Ok(generators::request_path_exists(&co, path).await),
47        }
48    }
49
50    #[builtin("readDir")]
51    async fn builtin_read_dir(co: GenCo, path: Value) -> Result<Value, ErrorKind> {
52        match coerce_value_to_path(&co, path).await? {
53            Err(cek) => Ok(Value::from(cek)),
54            Ok(path) => {
55                let dir = generators::request_read_dir(&co, path).await;
56                let res = dir.into_iter().map(|(name, ftype)| {
57                    (
58                        // TODO: propagate Vec<u8> or bytes::Bytes into NixString.
59                        NixString::from(
60                            String::from_utf8(name.to_vec()).expect("parsing file name as string"),
61                        ),
62                        Value::from(ftype.to_string()),
63                    )
64                });
65
66                Ok(Value::attrs(NixAttrs::from_iter(res)))
67            }
68        }
69    }
70
71    #[builtin("readFile")]
72    async fn builtin_read_file(co: GenCo, path: Value) -> Result<Value, ErrorKind> {
73        match coerce_value_to_path(&co, path).await? {
74            Err(cek) => Ok(Value::from(cek)),
75            Ok(path) => {
76                let mut buf = Vec::new();
77                generators::request_open_file(&co, path)
78                    .await
79                    .read_to_end(&mut buf)?;
80                Ok(Value::from(buf))
81            }
82        }
83    }
84
85    #[builtin("readFileType")]
86    async fn builtin_read_file_type(co: GenCo, path: Value) -> Result<Value, ErrorKind> {
87        match coerce_value_to_path(&co, path).await? {
88            Err(cek) => Ok(Value::from(cek)),
89            Ok(path) => Ok(Value::from(
90                generators::request_read_file_type(&co, path)
91                    .await
92                    .to_string(),
93            )),
94        }
95    }
96}
97
98/// Return all impure builtins, that is all builtins which may perform I/O
99/// outside of the VM and so cannot be used in all contexts (e.g. WASM).
100pub fn impure_builtins() -> Vec<(&'static str, Value)> {
101    let mut result = impure_builtins::builtins();
102
103    // currentTime pins the time at which evaluation was started
104    {
105        let seconds = match SystemTime::now().duration_since(UNIX_EPOCH) {
106            Ok(dur) => dur.as_secs() as i64,
107
108            // This case is hit if the system time is *before* epoch.
109            Err(err) => -(err.duration().as_secs() as i64),
110        };
111
112        result.push(("currentTime", Value::Integer(seconds)));
113    }
114
115    result
116}