snix_eval/builtins/
impure.rs

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