git.delta.rocks / jrsonnet / refs/commits / 2cecfb0ba471

difftreelog

refactor remove GeneralOpts/ConfigureState

Yaroslav Bolyukin2023-01-20parent: #1f5d87f.patch.diff
in: master
Because many options were removed from global state, everything should
be configured manually now

6 files changed

modifiedcmds/jrsonnet/src/main.rsdiffbeforeafterboth
before · cmds/jrsonnet/src/main.rs
1use std::{2	fs::{create_dir_all, File},3	io::{Read, Write},4};56use clap::{CommandFactory, Parser};7use clap_complete::Shell;8use jrsonnet_cli::{ConfigureState, GeneralOpts, ManifestOpts, OutputOpts, TraceOpts};9use jrsonnet_evaluator::{10	apply_tla,11	error::{Error as JrError, ErrorKind},12	throw, ResultExt, State, Val,13};1415#[cfg(feature = "mimalloc")]16#[global_allocator]17static GLOBAL: mimallocator::Mimalloc = mimallocator::Mimalloc;1819#[derive(Parser)]20enum SubOpts {21	/// Generate completions for specified shell22	Generate {23		/// Target shell name24		shell: Shell,25	},26}2728#[derive(Parser)]29#[clap(next_help_heading = "DEBUG")]30struct DebugOpts {31	/// Required OS stack size.32	/// This shouldn't be changed unless jrsonnet is failing with stack overflow error.33	#[clap(long, name = "size")]34	pub os_stack: Option<usize>,35}3637#[derive(Parser)]38#[clap(next_help_heading = "INPUT")]39struct InputOpts {40	/// Treat input as code, evaluate them instead of reading file41	#[clap(long, short = 'e')]42	pub exec: bool,4344	/// Path to the file to be compiled if `--evaluate` is unset, otherwise code itself45	pub input: Option<String>,46}4748/// Jsonnet commandline interpreter (Rust implementation)49#[derive(Parser)]50#[clap(51	args_conflicts_with_subcommands = true,52	disable_version_flag = true,53	version,54	author55)]56struct Opts {57	#[clap(subcommand)]58	sub: Option<SubOpts>,5960	#[clap(flatten)]61	input: InputOpts,62	#[clap(flatten)]63	general: GeneralOpts,6465	#[clap(flatten)]66	trace: TraceOpts,67	#[clap(flatten)]68	manifest: ManifestOpts,69	#[clap(flatten)]70	output: OutputOpts,71	#[clap(flatten)]72	debug: DebugOpts,73}7475fn main() {76	let opts: Opts = Opts::parse();7778	if let Some(sub) = opts.sub {79		match sub {80			SubOpts::Generate { shell } => {81				use clap_complete::generate;82				let app = &mut Opts::command();83				let buf = &mut std::io::stdout();84				generate(shell, app, "jrsonnet", buf);85				std::process::exit(0)86			}87		}88	}8990	let success = if let Some(size) = opts.debug.os_stack {91		std::thread::Builder::new()92			.stack_size(size * 1024 * 1024)93			.spawn(|| main_catch(opts))94			.expect("new thread spawned")95			.join()96			.expect("thread finished successfully")97	} else {98		main_catch(opts)99	};100	if !success {101		std::process::exit(1);102	}103}104105#[derive(thiserror::Error, Debug)]106enum Error {107	// Handled differently108	#[error("evaluation error")]109	Evaluation(JrError),110	#[error("io error")]111	Io(#[from] std::io::Error),112	#[error("input is not utf8 encoded")]113	Utf8(#[from] std::str::Utf8Error),114	#[error("missing input argument")]115	MissingInputArgument,116}117impl From<JrError> for Error {118	fn from(e: JrError) -> Self {119		Self::Evaluation(e)120	}121}122impl From<ErrorKind> for Error {123	fn from(e: ErrorKind) -> Self {124		Self::from(JrError::from(e))125	}126}127128fn main_catch(opts: Opts) -> bool {129	let s = State::default();130	let trace = opts131		.trace132		.configure(&s)133		.expect("this configurator doesn't fail");134	if let Err(e) = main_real(&s, opts) {135		if let Error::Evaluation(e) = e {136			let mut out = String::new();137			trace.write_trace(&mut out, &e).expect("format error");138			eprintln!("{out}")139		} else {140			eprintln!("{}", e);141		}142		return false;143	}144	true145}146147fn main_real(s: &State, opts: Opts) -> Result<(), Error> {148	let (tla, _gc_guard) = opts.general.configure(s)?;149	let manifest_format = opts.manifest.configure(s)?;150151	let input = opts.input.input.ok_or(Error::MissingInputArgument)?;152	let val = if opts.input.exec {153		s.evaluate_snippet("<cmdline>".to_owned(), &input as &str)?154	} else if input == "-" {155		let mut input = Vec::new();156		std::io::stdin().read_to_end(&mut input)?;157		let input_str = std::str::from_utf8(&input)?;158		s.evaluate_snippet("<stdin>".to_owned(), input_str)?159	} else {160		s.import(&input)?161	};162163	let val = apply_tla(s.clone(), &tla, val)?;164165	if let Some(multi) = opts.output.multi {166		if opts.output.create_output_dirs {167			let mut dir = multi.clone();168			dir.pop();169			create_dir_all(dir)?;170		}171		let Val::Obj(obj) = val else {172			throw!("value should be object for --multi manifest, got {}", val.value_type())173		};174		for (field, data) in obj.iter(175			#[cfg(feature = "exp-preserve-order")]176			opts.manifest.preserve_order,177		) {178			let data = data.with_description(|| format!("getting field {field} for manifest"))?;179180			let mut path = multi.clone();181			path.push(&field as &str);182			if opts.output.create_output_dirs {183				let mut dir = path.clone();184				dir.pop();185				create_dir_all(dir)?;186			}187			println!("{}", path.to_str().expect("path"));188			let mut file = File::create(path)?;189			writeln!(190				file,191				"{}",192				data.manifest(&manifest_format)193					.with_description(|| format!("manifesting {field}"))?194			)?;195		}196	} else if let Some(path) = opts.output.output_file {197		if opts.output.create_output_dirs {198			let mut dir = path.clone();199			dir.pop();200			create_dir_all(dir)?;201		}202		let mut file = File::create(path)?;203		writeln!(file, "{}", val.manifest(manifest_format)?)?;204	} else {205		let output = val.manifest(manifest_format)?;206		if !output.is_empty() {207			println!("{}", output);208		}209	}210211	Ok(())212}
after · cmds/jrsonnet/src/main.rs
1use std::{2	fs::{create_dir_all, File},3	io::{Read, Write},4};56use clap::{CommandFactory, Parser};7use clap_complete::Shell;8use jrsonnet_cli::{ManifestOpts, OutputOpts, TraceOpts, MiscOpts, TlaOpts, StdOpts, GcOpts};9use jrsonnet_evaluator::{10	apply_tla,11	error::{Error as JrError, ErrorKind},12	throw, ResultExt, State, Val,13};1415#[cfg(feature = "mimalloc")]16#[global_allocator]17static GLOBAL: mimallocator::Mimalloc = mimallocator::Mimalloc;1819#[derive(Parser)]20enum SubOpts {21	/// Generate completions for specified shell22	Generate {23		/// Target shell name24		shell: Shell,25	},26}2728#[derive(Parser)]29#[clap(next_help_heading = "DEBUG")]30struct DebugOpts {31	/// Required OS stack size.32	/// This shouldn't be changed unless jrsonnet is failing with stack overflow error.33	#[clap(long, name = "size")]34	pub os_stack: Option<usize>,35}3637#[derive(Parser)]38#[clap(next_help_heading = "INPUT")]39struct InputOpts {40	/// Treat input as code, evaluate them instead of reading file41	#[clap(long, short = 'e')]42	pub exec: bool,4344	/// Path to the file to be compiled if `--evaluate` is unset, otherwise code itself45	pub input: Option<String>,46}4748/// Jsonnet commandline interpreter (Rust implementation)49#[derive(Parser)]50#[clap(51	args_conflicts_with_subcommands = true,52	disable_version_flag = true,53	version,54	author55)]56struct Opts {57	#[clap(subcommand)]58	sub: Option<SubOpts>,5960	#[clap(flatten)]61	input: InputOpts,62	#[clap(flatten)]63	misc: MiscOpts,64	#[clap(flatten)]65	tla: TlaOpts,66	#[clap(flatten)]67	std: StdOpts,68	#[clap(flatten)]69	gc: GcOpts,7071	#[clap(flatten)]72	trace: TraceOpts,73	#[clap(flatten)]74	manifest: ManifestOpts,75	#[clap(flatten)]76	output: OutputOpts,77	#[clap(flatten)]78	debug: DebugOpts,79}8081fn main() {82	let opts: Opts = Opts::parse();8384	if let Some(sub) = opts.sub {85		match sub {86			SubOpts::Generate { shell } => {87				use clap_complete::generate;88				let app = &mut Opts::command();89				let buf = &mut std::io::stdout();90				generate(shell, app, "jrsonnet", buf);91				std::process::exit(0)92			}93		}94	}9596	let success = if let Some(size) = opts.debug.os_stack {97		std::thread::Builder::new()98			.stack_size(size * 1024 * 1024)99			.spawn(|| main_catch(opts))100			.expect("new thread spawned")101			.join()102			.expect("thread finished successfully")103	} else {104		main_catch(opts)105	};106	if !success {107		std::process::exit(1);108	}109}110111#[derive(thiserror::Error, Debug)]112enum Error {113	// Handled differently114	#[error("evaluation error")]115	Evaluation(JrError),116	#[error("io error")]117	Io(#[from] std::io::Error),118	#[error("input is not utf8 encoded")]119	Utf8(#[from] std::str::Utf8Error),120	#[error("missing input argument")]121	MissingInputArgument,122}123impl From<JrError> for Error {124	fn from(e: JrError) -> Self {125		Self::Evaluation(e)126	}127}128impl From<ErrorKind> for Error {129	fn from(e: ErrorKind) -> Self {130		Self::from(JrError::from(e))131	}132}133134fn main_catch(opts: Opts) -> bool {135	let s = State::default();136	let trace = opts137		.trace138		.trace_format();139	if let Err(e) = main_real(&s, opts) {140		if let Error::Evaluation(e) = e {141			let mut out = String::new();142			trace.write_trace(&mut out, &e).expect("format error");143			eprintln!("{out}")144		} else {145			eprintln!("{}", e);146		}147		return false;148	}149	true150}151152fn main_real(s: &State, opts: Opts) -> Result<(), Error> {153	let _gc_leak_guard= opts.gc.leak_on_exit();154	let _gc_print_stats = opts.gc.stats_printer();155	let _stack_depth_override = opts.misc.stack_size_override();156157	let import_resolver = opts.misc.import_resolver();158	s.set_import_resolver(import_resolver);159160	let std = opts.std.context_initializer(s)?;161	if let Some(std) = std {162		s.set_context_initializer(std);163	}164165	let input = opts.input.input.ok_or(Error::MissingInputArgument)?;166	let val = if opts.input.exec {167		s.evaluate_snippet("<cmdline>".to_owned(), &input as &str)?168	} else if input == "-" {169		let mut input = Vec::new();170		std::io::stdin().read_to_end(&mut input)?;171		let input_str = std::str::from_utf8(&input)?;172		s.evaluate_snippet("<stdin>".to_owned(), input_str)?173	} else {174		s.import(&input)?175	};176177	let tla = opts.tla.tla_opts()?;178	let val = apply_tla(s.clone(), &tla, val)?;179180	let manifest_format = opts.manifest.manifest_format();181	if let Some(multi) = opts.output.multi {182		if opts.output.create_output_dirs {183			let mut dir = multi.clone();184			dir.pop();185			create_dir_all(dir)?;186		}187		let Val::Obj(obj) = val else {188			throw!("value should be object for --multi manifest, got {}", val.value_type())189		};190		for (field, data) in obj.iter(191			#[cfg(feature = "exp-preserve-order")]192			opts.manifest.preserve_order,193		) {194			let data = data.with_description(|| format!("getting field {field} for manifest"))?;195196			let mut path = multi.clone();197			path.push(&field as &str);198			if opts.output.create_output_dirs {199				let mut dir = path.clone();200				dir.pop();201				create_dir_all(dir)?;202			}203			println!("{}", path.to_str().expect("path"));204			let mut file = File::create(path)?;205			writeln!(206				file,207				"{}",208				data.manifest(&manifest_format)209					.with_description(|| format!("manifesting {field}"))?210			)?;211		}212	} else if let Some(path) = opts.output.output_file {213		if opts.output.create_output_dirs {214			let mut dir = path.clone();215			dir.pop();216			create_dir_all(dir)?;217		}218		let mut file = File::create(path)?;219		writeln!(file, "{}", val.manifest(manifest_format)?)?;220	} else {221		let output = val.manifest(manifest_format)?;222		if !output.is_empty() {223			println!("{}", output);224		}225	}226227	Ok(())228}
modifiedcrates/jrsonnet-cli/src/lib.rsdiffbeforeafterboth
--- a/crates/jrsonnet-cli/src/lib.rs
+++ b/crates/jrsonnet-cli/src/lib.rs
@@ -6,19 +6,13 @@
 use std::{env, marker::PhantomData, path::PathBuf};
 
 use clap::Parser;
-use jrsonnet_evaluator::{error::Result, stack::set_stack_depth_limit, FileImportResolver, State};
+use jrsonnet_evaluator::{error::Result, stack::{set_stack_depth_limit, StackDepthLimitOverrideGuard, limit_stack_depth}, FileImportResolver, State, ImportResolver};
 use jrsonnet_gcmodule::with_thread_object_space;
 pub use manifest::*;
 pub use stdlib::*;
 pub use tla::*;
 pub use trace::*;
-
-pub trait ConfigureState {
-	type Guards;
 
-	fn configure(&self, s: &State) -> Result<Self::Guards>;
-}
-
 #[derive(Parser)]
 #[clap(next_help_heading = "INPUT")]
 pub struct InputOpts {
@@ -45,50 +39,18 @@
 	#[clap(long, short = 'J')]
 	jpath: Vec<PathBuf>,
 }
-impl ConfigureState for MiscOpts {
-	type Guards = ();
-	fn configure(&self, s: &State) -> Result<Self::Guards> {
+impl MiscOpts {
+	pub fn import_resolver(&self) -> FileImportResolver {
 		let mut library_paths = self.jpath.clone();
 		library_paths.reverse();
 		if let Some(path) = env::var_os("JSONNET_PATH") {
 			library_paths.extend(env::split_paths(path.as_os_str()));
 		}
 
-		s.set_import_resolver(FileImportResolver::new(library_paths));
-
-		set_stack_depth_limit(self.max_stack);
-		Ok(())
+		FileImportResolver::new(library_paths)
 	}
-}
-
-/// General configuration of jsonnet
-#[derive(Parser)]
-#[clap(name = "jrsonnet", version, author)]
-pub struct GeneralOpts {
-	#[clap(flatten)]
-	misc: MiscOpts,
-
-	#[clap(flatten)]
-	tla: TlaOpts,
-	#[clap(flatten)]
-	std: StdOpts,
-
-	#[clap(flatten)]
-	gc: GcOpts,
-}
-
-impl ConfigureState for GeneralOpts {
-	type Guards = (
-		<TlaOpts as ConfigureState>::Guards,
-		<GcOpts as ConfigureState>::Guards,
-	);
-	fn configure(&self, s: &State) -> Result<Self::Guards> {
-		// Configure trace first, because tla-code/ext-code can throw
-		self.misc.configure(s)?;
-		let tla_guards = self.tla.configure(s)?;
-		self.std.configure(s)?;
-		let gc_guards = self.gc.configure(s)?;
-		Ok((tla_guards, gc_guards))
+	pub fn stack_size_override(&self) -> StackDepthLimitOverrideGuard {
+		limit_stack_depth(self.max_stack)
 	}
 }
 
@@ -107,18 +69,14 @@
 	#[clap(long)]
 	gc_collect_before_printing_stats: bool,
 }
-impl ConfigureState for GcOpts {
-	type Guards = (Option<GcStatsPrinter>, Option<LeakSpace>);
-
-	fn configure(&self, _s: &State) -> Result<Self::Guards> {
-		// Constructed structs have side-effects in Drop impl
-		#[allow(clippy::unnecessary_lazy_evaluations)]
-		Ok((
-			self.gc_print_stats.then(|| GcStatsPrinter {
-				collect_before_printing_stats: self.gc_collect_before_printing_stats,
-			}),
-			(!self.gc_collect_on_exit).then(|| LeakSpace(PhantomData)),
-		))
+impl GcOpts {
+	pub fn stats_printer(&self) -> Option<GcStatsPrinter> {
+		self.gc_print_stats.then(|| GcStatsPrinter {
+			collect_before_printing_stats: self.gc_collect_before_printing_stats,
+		})
+	}
+	pub fn leak_on_exit(&self) -> Option<LeakSpace> {
+		(!self.gc_collect_on_exit).then(|| LeakSpace(PhantomData))
 	}
 }
 
modifiedcrates/jrsonnet-cli/src/manifest.rsdiffbeforeafterboth
--- a/crates/jrsonnet-cli/src/manifest.rs
+++ b/crates/jrsonnet-cli/src/manifest.rs
@@ -8,8 +8,6 @@
 };
 use jrsonnet_stdlib::{TomlFormat, YamlFormat};
 
-use crate::ConfigureState;
-
 #[derive(Clone, ValueEnum)]
 pub enum ManifestFormatName {
 	/// Expect string as output, and write them directly
@@ -41,9 +39,8 @@
 	#[clap(long)]
 	pub preserve_order: bool,
 }
-impl ConfigureState for ManifestOpts {
-	type Guards = Box<dyn ManifestFormat>;
-	fn configure(&self, _s: &State) -> Result<Self::Guards> {
+impl ManifestOpts {
+	pub fn manifest_format(&self) -> Box<dyn ManifestFormat> {
 		let format: Box<dyn ManifestFormat> = if self.string {
 			Box::new(StringFormat)
 		} else {
@@ -68,11 +65,11 @@
 				)),
 			}
 		};
-		Ok(if self.yaml_stream {
+		if self.yaml_stream {
 			Box::new(YamlStreamFormat(format))
 		} else {
 			format
-		})
+		}
 	}
 }
 
modifiedcrates/jrsonnet-cli/src/stdlib.rsdiffbeforeafterboth
--- a/crates/jrsonnet-cli/src/stdlib.rs
+++ b/crates/jrsonnet-cli/src/stdlib.rs
@@ -2,8 +2,7 @@
 
 use clap::Parser;
 use jrsonnet_evaluator::{error::Result, tb, trace::PathResolver, State};
-
-use crate::ConfigureState;
+use jrsonnet_stdlib::ContextInitializer;
 
 #[derive(Clone)]
 pub struct ExtStr {
@@ -82,14 +81,13 @@
 	#[clap(long, name = "name=var code path", number_of_values = 1)]
 	ext_code_file: Vec<ExtFile>,
 }
-impl ConfigureState for StdOpts {
-	type Guards = ();
-	fn configure(&self, s: &State) -> Result<()> {
+impl StdOpts {
+	pub fn context_initializer(&self, s: &State) -> Result<Option<ContextInitializer>> {
 		if self.no_stdlib {
-			return Ok(());
+			return Ok(None);
 		}
 		let ctx =
-			jrsonnet_stdlib::ContextInitializer::new(s.clone(), PathResolver::new_cwd_fallback());
+			ContextInitializer::new(s.clone(), PathResolver::new_cwd_fallback());
 		for ext in self.ext_str.iter() {
 			ctx.add_ext_str((&ext.name as &str).into(), (&ext.value as &str).into());
 		}
@@ -102,7 +100,6 @@
 		for ext in self.ext_code_file.iter() {
 			ctx.add_ext_code(&ext.name as &str, &ext.value as &str)?;
 		}
-		s.settings_mut().context_initializer = tb!(ctx);
-		Ok(())
+		Ok(Some(ctx))
 	}
 }
modifiedcrates/jrsonnet-cli/src/tla.rsdiffbeforeafterboth
--- a/crates/jrsonnet-cli/src/tla.rs
+++ b/crates/jrsonnet-cli/src/tla.rs
@@ -7,7 +7,7 @@
 };
 use jrsonnet_parser::{ParserSettings, Source};
 
-use crate::{ConfigureState, ExtFile, ExtStr};
+use crate::{ExtFile, ExtStr};
 
 #[derive(Parser)]
 #[clap(next_help_heading = "TOP LEVEL ARGUMENTS")]
@@ -31,9 +31,8 @@
 	#[clap(long, name = "name=tla code path", number_of_values = 1)]
 	tla_code_file: Vec<ExtFile>,
 }
-impl ConfigureState for TlaOpts {
-	type Guards = GcHashMap<IStr, TlaArg>;
-	fn configure(&self, _s: &State) -> Result<Self::Guards> {
+impl TlaOpts {
+	pub fn tla_opts(&self) -> Result<GcHashMap<IStr, TlaArg>> {
 		let mut out = GcHashMap::new();
 		for (name, value) in self
 			.tla_str
modifiedcrates/jrsonnet-cli/src/trace.rsdiffbeforeafterboth
--- a/crates/jrsonnet-cli/src/trace.rs
+++ b/crates/jrsonnet-cli/src/trace.rs
@@ -5,8 +5,6 @@
 	State,
 };
 
-use crate::ConfigureState;
-
 #[derive(PartialEq, Eq, ValueEnum, Clone)]
 pub enum TraceFormatName {
 	/// Only show `filename:line:column`
@@ -26,9 +24,8 @@
 	#[clap(long, short = 't', default_value = "20")]
 	max_trace: usize,
 }
-impl ConfigureState for TraceOpts {
-	type Guards = Box<dyn TraceFormat>;
-	fn configure(&self, _s: &State) -> Result<Self::Guards> {
+impl TraceOpts {
+	pub fn trace_format(&self) -> Box<dyn TraceFormat> {
 		let resolver = PathResolver::new_cwd_fallback();
 		let max_trace = self.max_trace;
 		let format: Box<dyn TraceFormat> = match self
@@ -46,6 +43,6 @@
 				max_trace,
 			}),
 		};
-		Ok(format)
+		format
 	}
 }