git.delta.rocks / jrsonnet / refs/commits / ec474dc646c0

difftreelog

feat add --version argumnet

Yaroslav Bolyukin2023-05-08parent: #15b5e67.patch.diff
in: master

1 file 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::{GcOpts, ManifestOpts, MiscOpts, OutputOpts, StdOpts, TlaOpts, 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	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 = opts.trace.trace_format();137	if let Err(e) = main_real(&s, opts) {138		if let Error::Evaluation(e) = e {139			let mut out = String::new();140			trace.write_trace(&mut out, &e).expect("format error");141			eprintln!("{out}")142		} else {143			eprintln!("{e}");144		}145		return false;146	}147	true148}149150fn main_real(s: &State, opts: Opts) -> Result<(), Error> {151	let _gc_leak_guard = opts.gc.leak_on_exit();152	let _gc_print_stats = opts.gc.stats_printer();153	let _stack_depth_override = opts.misc.stack_size_override();154155	let import_resolver = opts.misc.import_resolver();156	s.set_import_resolver(import_resolver);157158	let std = opts.std.context_initializer(s)?;159	if let Some(std) = std {160		s.set_context_initializer(std);161	}162163	let input = opts.input.input.ok_or(Error::MissingInputArgument)?;164	let val = if opts.input.exec {165		s.evaluate_snippet("<cmdline>".to_owned(), &input as &str)?166	} else if input == "-" {167		let mut input = Vec::new();168		std::io::stdin().read_to_end(&mut input)?;169		let input_str = std::str::from_utf8(&input)?;170		s.evaluate_snippet("<stdin>".to_owned(), input_str)?171	} else {172		s.import(&input)?173	};174175	let tla = opts.tla.tla_opts()?;176	let val = apply_tla(s.clone(), &tla, val)?;177178	let manifest_format = opts.manifest.manifest_format();179	if let Some(multi) = opts.output.multi {180		if opts.output.create_output_dirs {181			let mut dir = multi.clone();182			dir.pop();183			create_dir_all(dir)?;184		}185		let Val::Obj(obj) = val else {186			throw!("value should be object for --multi manifest, got {}", val.value_type())187		};188		for (field, data) in obj.iter(189			#[cfg(feature = "exp-preserve-order")]190			opts.manifest.preserve_order,191		) {192			let data = data.with_description(|| format!("getting field {field} for manifest"))?;193194			let mut path = multi.clone();195			path.push(&field as &str);196			if opts.output.create_output_dirs {197				let mut dir = path.clone();198				dir.pop();199				create_dir_all(dir)?;200			}201			println!("{}", path.to_str().expect("path"));202			let mut file = File::create(path)?;203			write!(204				file,205				"{}",206				data.manifest(&manifest_format)207					.with_description(|| format!("manifesting {field}"))?,208			)?;209			if manifest_format.file_trailing_newline() {210				writeln!(file)?;211			}212			file.flush()?;213		}214	} else if let Some(path) = opts.output.output_file {215		if opts.output.create_output_dirs {216			let mut dir = path.clone();217			dir.pop();218			create_dir_all(dir)?;219		}220		let mut file = File::create(path)?;221		writeln!(file, "{}", val.manifest(manifest_format)?)?;222	} else {223		let output = val.manifest(manifest_format)?;224		if !output.is_empty() {225			println!("{output}");226		}227	}228229	Ok(())230}
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::{GcOpts, ManifestOpts, MiscOpts, OutputOpts, StdOpts, TlaOpts, 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>,59	/// Print version60	#[clap(long)]61	version: bool,6263	#[clap(flatten)]64	input: InputOpts,65	#[clap(flatten)]66	misc: MiscOpts,67	#[clap(flatten)]68	tla: TlaOpts,69	#[clap(flatten)]70	std: StdOpts,71	#[clap(flatten)]72	gc: GcOpts,7374	#[clap(flatten)]75	trace: TraceOpts,76	#[clap(flatten)]77	manifest: ManifestOpts,78	#[clap(flatten)]79	output: OutputOpts,80	#[clap(flatten)]81	debug: DebugOpts,82}8384fn main() {85	let opts: Opts = Opts::parse();8687	if opts.version {88		print!("{}", Opts::command().render_version());89		std::process::exit(0)90	}9192	if let Some(sub) = opts.sub {93		match sub {94			SubOpts::Generate { shell } => {95				use clap_complete::generate;96				let app = &mut Opts::command();97				let buf = &mut std::io::stdout();98				generate(shell, app, "jrsonnet", buf);99				std::process::exit(0)100			}101		}102	}103104	let success = if let Some(size) = opts.debug.os_stack {105		std::thread::Builder::new()106			.stack_size(size * 1024 * 1024)107			.spawn(|| main_catch(opts))108			.expect("new thread spawned")109			.join()110			.expect("thread finished successfully")111	} else {112		main_catch(opts)113	};114	if !success {115		std::process::exit(1);116	}117}118119#[derive(thiserror::Error, Debug)]120enum Error {121	// Handled differently122	#[error("evaluation error")]123	Evaluation(JrError),124	#[error("io error")]125	Io(#[from] std::io::Error),126	#[error("input is not utf8 encoded")]127	Utf8(#[from] std::str::Utf8Error),128	#[error("missing input argument")]129	MissingInputArgument,130}131impl From<JrError> for Error {132	fn from(e: JrError) -> Self {133		Self::Evaluation(e)134	}135}136impl From<ErrorKind> for Error {137	fn from(e: ErrorKind) -> Self {138		Self::from(JrError::from(e))139	}140}141142fn main_catch(opts: Opts) -> bool {143	let s = State::default();144	let trace = opts.trace.trace_format();145	if let Err(e) = main_real(&s, opts) {146		if let Error::Evaluation(e) = e {147			let mut out = String::new();148			trace.write_trace(&mut out, &e).expect("format error");149			eprintln!("{out}")150		} else {151			eprintln!("{e}");152		}153		return false;154	}155	true156}157158fn main_real(s: &State, opts: Opts) -> Result<(), Error> {159	let _gc_leak_guard = opts.gc.leak_on_exit();160	let _gc_print_stats = opts.gc.stats_printer();161	let _stack_depth_override = opts.misc.stack_size_override();162163	let import_resolver = opts.misc.import_resolver();164	s.set_import_resolver(import_resolver);165166	let std = opts.std.context_initializer(s)?;167	if let Some(std) = std {168		s.set_context_initializer(std);169	}170171	let input = opts.input.input.ok_or(Error::MissingInputArgument)?;172	let val = if opts.input.exec {173		s.evaluate_snippet("<cmdline>".to_owned(), &input as &str)?174	} else if input == "-" {175		let mut input = Vec::new();176		std::io::stdin().read_to_end(&mut input)?;177		let input_str = std::str::from_utf8(&input)?;178		s.evaluate_snippet("<stdin>".to_owned(), input_str)?179	} else {180		s.import(&input)?181	};182183	let tla = opts.tla.tla_opts()?;184	let val = apply_tla(s.clone(), &tla, val)?;185186	let manifest_format = opts.manifest.manifest_format();187	if let Some(multi) = opts.output.multi {188		if opts.output.create_output_dirs {189			let mut dir = multi.clone();190			dir.pop();191			create_dir_all(dir)?;192		}193		let Val::Obj(obj) = val else {194			throw!("value should be object for --multi manifest, got {}", val.value_type())195		};196		for (field, data) in obj.iter(197			#[cfg(feature = "exp-preserve-order")]198			opts.manifest.preserve_order,199		) {200			let data = data.with_description(|| format!("getting field {field} for manifest"))?;201202			let mut path = multi.clone();203			path.push(&field as &str);204			if opts.output.create_output_dirs {205				let mut dir = path.clone();206				dir.pop();207				create_dir_all(dir)?;208			}209			println!("{}", path.to_str().expect("path"));210			let mut file = File::create(path)?;211			write!(212				file,213				"{}",214				data.manifest(&manifest_format)215					.with_description(|| format!("manifesting {field}"))?,216			)?;217			if manifest_format.file_trailing_newline() {218				writeln!(file)?;219			}220			file.flush()?;221		}222	} else if let Some(path) = opts.output.output_file {223		if opts.output.create_output_dirs {224			let mut dir = path.clone();225			dir.pop();226			create_dir_all(dir)?;227		}228		let mut file = File::create(path)?;229		writeln!(file, "{}", val.manifest(manifest_format)?)?;230	} else {231		let output = val.manifest(manifest_format)?;232		if !output.is_empty() {233			println!("{output}");234		}235	}236237	Ok(())238}