Skip to main content

snix_cli_eval/
args.rs

1use std::path::PathBuf;
2
3use clap::{Parser, crate_version};
4use snix_store::utils::ServiceUrlsMemory;
5use url::Url;
6
7fn make_version() -> String {
8    #[allow(unused_mut)]
9    let mut features = String::new();
10    #[cfg(feature = "otlp")]
11    {
12        features.push_str("+otlp");
13    }
14    #[cfg(feature = "tracing-chrome")]
15    {
16        features.push_str("+tracing-chrome");
17    }
18    #[cfg(feature = "tracing-tracy")]
19    {
20        features.push_str("+tracing-tracy");
21    }
22    #[cfg(feature = "xp-store-composition-cli")]
23    {
24        features.push_str("+xp-store-composition-cli");
25    }
26    if features.is_empty() {
27        String::from(crate_version!())
28    } else {
29        format!("{} ({features})", crate_version!())
30    }
31}
32
33/// Provides a CLI interface to trigger evaluation using snix-eval.
34///
35/// Uses configured snix-{ca,}store and snix-build components,
36/// and by default a set of builtins similar to these present in Nix.
37///
38/// None of the stores available add to the local `/nix/store` location.
39///
40/// The CLI interface is not stable and subject to change.
41#[derive(Parser, Clone)]
42#[command(version = make_version())]
43pub struct Args {
44    /// Path to a script to evaluate
45    pub script: Option<PathBuf>,
46
47    #[clap(long, short = 'E')]
48    pub expr: Option<String>,
49
50    /// Dump the raw AST to stdout before interpreting
51    #[clap(long, env = "SNIX_DISPLAY_AST")]
52    pub display_ast: bool,
53
54    /// Dump the bytecode to stdout before evaluating
55    #[clap(long, env = "SNIX_DUMP_BYTECODE")]
56    pub dump_bytecode: bool,
57
58    /// Trace the runtime of the VM
59    #[clap(long, env = "SNIX_TRACE_RUNTIME")]
60    pub trace_runtime: bool,
61
62    /// Capture the time (relative to the start time of evaluation) of all events traced with
63    /// `--trace-runtime`
64    #[clap(long, env = "SNIX_TRACE_RUNTIME_TIMING", requires("trace_runtime"))]
65    pub trace_runtime_timing: bool,
66
67    /// Only compile, but do not execute code. This will make Snix act
68    /// sort of like a linter.
69    #[clap(long)]
70    pub compile_only: bool,
71
72    /// Don't print warnings.
73    #[clap(long)]
74    pub no_warnings: bool,
75
76    /// Additional entries to the Nix expression search path, a colon-separated list of directories
77    /// used to resolve `<...>`-style lookup paths.
78    ///
79    /// This option may be given multiple times. Paths added through -I take precedence over
80    /// NIX_PATH.
81    #[clap(long = "extra-nix-path", short = 'I', action = clap::ArgAction::Append)]
82    pub extra_nix_paths: Option<Vec<String>>,
83
84    /// Print "raw" (unquoted) output.
85    #[clap(long)]
86    pub raw: bool,
87
88    /// Strictly evaluate values, traversing them and forcing e.g.
89    /// elements of lists and attribute sets before printing the
90    /// return value.
91    #[clap(long)]
92    pub strict: bool,
93
94    #[clap(flatten)]
95    pub service_addrs: ServiceUrlsMemory,
96
97    #[arg(long, env, default_value = "dummy:")]
98    pub build_service_addr: String,
99
100    #[clap(flatten)]
101    pub tracing_args: snix_tracing::TracingArgs,
102
103    /// An optional path in which Derivations encountered during evaluation
104    /// are dumped into, after evaluation. If it doesn't exist, the directory is created.
105    ///
106    /// Files dumped there are named like they would show up in `/nix/store`,
107    /// if produced by Nix. Existing files are not overwritten.
108    ///
109    /// This is only for debugging and diffing purposes for post-eval inspection;
110    /// Snix does not read from these.
111    #[clap(long)]
112    pub drv_dumpdir: Option<PathBuf>,
113
114    /// A list of web servers used by builtins.fetchurl to obtain files by hash.
115    /// Given a hash algorithm ha and a base-16 hash h, Nix will try to download the file
116    /// from hashed-mirror/ha/h. This allows files to be downloaded even if they have
117    /// disappeared from their original URI.
118    #[clap(long, default_values_t = [Url::parse("https://tarballs.nixos.org/").unwrap()])]
119    pub hashed_mirrors: Vec<Url>,
120}
121
122impl Args {
123    pub fn nix_path(&self) -> Option<String> {
124        resolve_nix_path(std::env::var("NIX_PATH"), &self.extra_nix_paths)
125    }
126}
127
128fn resolve_nix_path(
129    nix_path: Result<String, std::env::VarError>,
130    extra_nix_paths: &Option<Vec<String>>,
131) -> Option<String> {
132    let nix_path_option = nix_path.ok().filter(|string| !string.is_empty());
133    let extra_nix_paths_option = extra_nix_paths.to_owned().map(|vec| vec.join(":"));
134    match (nix_path_option, extra_nix_paths_option) {
135        (Some(nix_path), Some(mut extra_nix_paths)) => {
136            extra_nix_paths.push(':');
137            Some(extra_nix_paths + &nix_path)
138        }
139        (nix_path_option, extra_nix_paths_option) => nix_path_option.or(extra_nix_paths_option),
140    }
141}
142
143#[cfg(test)]
144mod tests {
145    use super::resolve_nix_path;
146
147    #[test]
148    fn test_resolve_nix_path() {
149        let nix_path = Ok("/nixpath1:nixpath2=/nixpath2".to_owned());
150        let extra_nix_paths = Some(vec!["/extra1".to_owned(), "extra2=/extra2".to_owned()]);
151        let expected = Some("/extra1:extra2=/extra2:/nixpath1:nixpath2=/nixpath2".to_owned());
152        let actual = resolve_nix_path(nix_path, &extra_nix_paths);
153        assert!(actual == expected);
154        let nix_path = Err(std::env::VarError::NotPresent);
155        let extra_nix_paths = Some(vec!["/extra1".to_owned(), "extra2=/extra2".to_owned()]);
156        let expected = Some("/extra1:extra2=/extra2".to_owned());
157        let actual = resolve_nix_path(nix_path, &extra_nix_paths);
158        assert!(actual == expected);
159        let nix_path = Ok("/nixpath1:nixpath2=/nixpath2".to_owned());
160        let extra_nix_paths = None;
161        let expected = Some("/nixpath1:nixpath2=/nixpath2".to_owned());
162        let actual = resolve_nix_path(nix_path, &extra_nix_paths);
163        assert!(actual == expected);
164        let nix_path = Err(std::env::VarError::NotPresent);
165        let extra_nix_paths = None;
166        let expected = None;
167        let actual = resolve_nix_path(nix_path, &extra_nix_paths);
168        assert!(actual == expected);
169    }
170}