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
16fn 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 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, 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}