jrsonnet_cli/
lib.rs

1mod manifest;
2mod stdlib;
3mod tla;
4mod trace;
5
6use std::{env, marker::PhantomData, path::PathBuf};
7
8use clap::Parser;
9use jrsonnet_evaluator::{
10	stack::{limit_stack_depth, StackDepthLimitOverrideGuard},
11	FileImportResolver,
12};
13use jrsonnet_gcmodule::{with_thread_object_space, ObjectSpace};
14pub use manifest::*;
15pub use stdlib::*;
16pub use tla::*;
17pub use trace::*;
18
19#[derive(Parser)]
20#[clap(next_help_heading = "INPUT")]
21pub struct InputOpts {
22	/// Treat input as code, evaluate them instead of reading file
23	#[clap(long, short = 'e')]
24	pub exec: bool,
25
26	/// Path to the file to be compiled if `--evaluate` is unset, otherwise code itself
27	pub input: String,
28}
29
30#[derive(Parser)]
31#[clap(next_help_heading = "OPTIONS")]
32pub struct MiscOpts {
33	/// Maximal allowed number of stack frames,
34	/// stack overflow error will be raised if this number gets exceeded.
35	#[clap(long, short = 's', default_value = "512")]
36	max_stack: usize,
37
38	/// Library search dirs. (right-most wins)
39	/// Any not found `imported` file will be searched in these.
40	/// This can also be specified via `JSONNET_PATH` variable,
41	/// which should contain a colon-separated (semicolon-separated on Windows) list of directories.
42	#[clap(long, short = 'J')]
43	jpath: Vec<PathBuf>,
44}
45impl MiscOpts {
46	pub fn import_resolver(&self) -> FileImportResolver {
47		let mut library_paths = self.jpath.clone();
48		library_paths.reverse();
49		if let Some(path) = env::var_os("JSONNET_PATH") {
50			library_paths.extend(env::split_paths(path.as_os_str()));
51		}
52
53		FileImportResolver::new(library_paths)
54	}
55	pub fn stack_size_override(&self) -> StackDepthLimitOverrideGuard {
56		limit_stack_depth(self.max_stack)
57	}
58}
59
60#[derive(Parser)]
61#[clap(next_help_heading = "GARBAGE COLLECTION")]
62pub struct GcOpts {
63	/// Do not skip gc on exit
64	#[clap(long)]
65	gc_collect_on_exit: bool,
66	/// Print gc stats before exit
67	#[clap(long)]
68	gc_print_stats: bool,
69	/// Force garbage collection before printing stats
70	/// Useful for checking for memory leaks
71	/// Does nothing useless --gc-print-stats is specified
72	#[clap(long)]
73	gc_collect_before_printing_stats: bool,
74}
75impl GcOpts {
76	pub fn stats_printer(&self) -> Option<GcStatsPrinter> {
77		#[allow(clippy::unnecessary_lazy_evaluations/*, reason = "GcStatsPrinter has side-effect on Drop"*/)]
78		self.gc_print_stats.then(|| GcStatsPrinter {
79			collect_before_printing_stats: self.gc_collect_before_printing_stats,
80		})
81	}
82	pub fn leak_on_exit(&self) -> Option<LeakSpace> {
83		(!self.gc_collect_on_exit).then(|| LeakSpace(PhantomData))
84	}
85}
86
87pub struct LeakSpace(PhantomData<()>);
88
89impl Drop for LeakSpace {
90	fn drop(&mut self) {
91		with_thread_object_space(ObjectSpace::leak);
92	}
93}
94
95pub struct GcStatsPrinter {
96	collect_before_printing_stats: bool,
97}
98impl Drop for GcStatsPrinter {
99	fn drop(&mut self) {
100		eprintln!("=== GC STATS ===");
101		if self.collect_before_printing_stats {
102			let collected = jrsonnet_gcmodule::collect_thread_cycles();
103			eprintln!("Collected: {collected}");
104		}
105		eprintln!("Tracked: {}", jrsonnet_gcmodule::count_thread_tracked());
106	}
107}