snix/
main.rs

1use clap::Parser;
2use mimalloc::MiMalloc;
3use snix_cli::args::Args;
4use snix_cli::repl::Repl;
5use snix_cli::{AllowIncomplete, init_io_handle, interpret};
6use snix_eval::EvalMode;
7use snix_eval::observer::DisassemblingObserver;
8use snix_glue::snix_store_io::SnixStoreIO;
9use std::io::Write;
10use std::rc::Rc;
11use std::{fs, path::PathBuf};
12
13#[global_allocator]
14static GLOBAL: MiMalloc = MiMalloc;
15
16/// Interpret the given code snippet, but only run the Svix compiler
17/// on it and return errors and warnings.
18fn lint<E: Write + Clone + Send>(
19    stderr: &mut E,
20    code: &str,
21    path: Option<PathBuf>,
22    args: &Args,
23) -> bool {
24    let mut eval_builder = snix_eval::Evaluation::builder_impure();
25
26    if args.strict {
27        eval_builder = eval_builder.mode(EvalMode::Strict);
28    }
29
30    let source_map = eval_builder.source_map().clone();
31
32    let mut compiler_observer = DisassemblingObserver::new(source_map.clone(), stderr.clone());
33
34    if args.dump_bytecode {
35        eval_builder.set_compiler_observer(Some(&mut compiler_observer));
36    }
37
38    if args.trace_runtime {
39        writeln!(
40            stderr,
41            "warning: --trace-runtime has no effect with --compile-only"
42        )
43        .unwrap();
44    }
45
46    let eval = eval_builder.build();
47    let result = eval.compile_only(code, path);
48
49    if args.display_ast {
50        if let Some(ref expr) = result.expr {
51            writeln!(stderr, "AST: {}", snix_eval::pretty_print_expr(expr)).unwrap();
52        }
53    }
54
55    for error in &result.errors {
56        error.fancy_format_write(stderr);
57    }
58
59    for warning in &result.warnings {
60        warning.fancy_format_write(stderr, &source_map);
61    }
62
63    // inform the caller about any errors
64    result.errors.is_empty()
65}
66
67fn main() {
68    let args = Args::parse();
69
70    let tracing_handle = snix_tracing::TracingBuilder::default()
71        .enable_progressbar()
72        .build()
73        .expect("unable to set up tracing subscriber");
74    let mut stdout = tracing_handle.get_stdout_writer();
75    let mut stderr = tracing_handle.get_stderr_writer();
76
77    let tokio_runtime = tokio::runtime::Runtime::new().expect("failed to setup tokio runtime");
78
79    let io_handle = init_io_handle(&tokio_runtime, &args);
80
81    if let Some(file) = &args.script {
82        run_file(&mut stdout, &mut stderr, io_handle, file.clone(), &args)
83    } else if let Some(expr) = &args.expr {
84        if !interpret(
85            &mut stderr,
86            io_handle,
87            expr,
88            None,
89            &args,
90            false,
91            AllowIncomplete::RequireComplete,
92            None, // TODO(aspen): Pass in --arg/--argstr here
93            None,
94            None,
95        )
96        .unwrap()
97        .finalize(&mut stdout)
98        {
99            std::process::exit(1);
100        }
101    } else {
102        let mut repl = Repl::new(io_handle, &args);
103        repl.run(&mut stdout, &mut stderr)
104    }
105}
106
107fn run_file<O: Write, E: Write + Clone + Send>(
108    stdout: &mut O,
109    stderr: &mut E,
110    io_handle: Rc<SnixStoreIO>,
111    mut path: PathBuf,
112    args: &Args,
113) {
114    if path.is_dir() {
115        path.push("default.nix");
116    }
117    let contents = fs::read_to_string(&path).expect("failed to read the input file");
118
119    let success = if args.compile_only {
120        lint(stderr, &contents, Some(path), args)
121    } else {
122        interpret(
123            stderr,
124            io_handle,
125            &contents,
126            Some(path),
127            args,
128            false,
129            AllowIncomplete::RequireComplete,
130            None,
131            None,
132            None,
133        )
134        .unwrap()
135        .finalize(stdout)
136    };
137
138    if !success {
139        std::process::exit(1);
140    }
141}