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