git.delta.rocks / jrsonnet / refs/commits / 7a7e4e4e737d

difftreelog

source

cmds/jrsonnet/src/main.rs4.4 KiBsourcehistory
1use clap::{AppSettings, Clap, IntoApp};2use jrsonnet_cli::{ConfigureState, GeneralOpts, InputOpts, ManifestOpts, OutputOpts};3use jrsonnet_evaluator::{error::LocError, EvaluationState, ManifestFormat};4use std::{5	fs::{create_dir_all, File},6	io::Read,7	io::Write,8	path::PathBuf,9	rc::Rc,10	str::FromStr,11};1213#[cfg(feature = "mimalloc")]14#[global_allocator]15static GLOBAL: mimallocator::Mimalloc = mimallocator::Mimalloc;1617#[derive(Clap)]18#[clap(help_heading = "DEBUG")]19struct DebugOpts {20	/// Required OS stack size.21	/// This shouldn't be changed unless jrsonnet is failing with stack overflow error.22	#[clap(long, name = "size")]23	pub os_stack: Option<usize>,24	/// Generate completions script25	#[clap(long)]26	generate: Option<GenerateTarget>,27}2829enum GenerateTarget {30	Bash,31	Zsh,32	Fish,33	PowerShell,34}35impl FromStr for GenerateTarget {36	type Err = &'static str;3738	fn from_str(s: &str) -> Result<Self, Self::Err> {39		match s {40			"bash" => Ok(Self::Bash),41			"zsh" => Ok(Self::Zsh),42			"fish" => Ok(Self::Fish),43			"powershell" => Ok(Self::PowerShell),44			_ => Err("unknown target"),45		}46	}47}4849#[derive(Clap)]50#[clap(51	global_setting = AppSettings::ColoredHelp,52	global_setting = AppSettings::DeriveDisplayOrder,53)]54struct Opts {55	#[clap(flatten)]56	input: InputOpts,57	#[clap(flatten)]58	general: GeneralOpts,59	#[clap(flatten)]60	manifest: ManifestOpts,61	#[clap(flatten)]62	output: OutputOpts,63	#[clap(flatten)]64	debug: DebugOpts,65}6667fn main() {68	let opts: Opts = Opts::parse();6970	if let Some(target) = opts.debug.generate {71		use clap_generate::{generate, generators};72		use GenerateTarget::*;73		let app = &mut Opts::into_app();74		let buf = &mut std::io::stdout();75		let bin = "jrsonnet";76		match target {77			Bash => generate::<generators::Bash, _>(app, bin, buf),78			Zsh => generate::<generators::Zsh, _>(app, bin, buf),79			Fish => generate::<generators::Fish, _>(app, bin, buf),80			PowerShell => generate::<generators::PowerShell, _>(app, bin, buf),81		}82		std::process::exit(0);83	};8485	let success;86	if let Some(size) = opts.debug.os_stack {87		success = std::thread::Builder::new()88			.stack_size(size * 1024 * 1024)89			.spawn(|| main_catch(opts))90			.expect("new thread spawned")91			.join()92			.expect("thread finished successfully");93	} else {94		success = main_catch(opts)95	}96	if !success {97		std::process::exit(1);98	}99}100101#[derive(thiserror::Error, Debug)]102enum Error {103	// Handled differently104	#[error("evaluation error")]105	Evaluation(jrsonnet_evaluator::error::LocError),106	#[error("io error")]107	Io(#[from] std::io::Error),108	#[error("input is not utf8 encoded")]109	Utf8(#[from] std::str::Utf8Error),110}111impl From<LocError> for Error {112	fn from(e: LocError) -> Self {113		Self::Evaluation(e)114	}115}116117fn main_catch(opts: Opts) -> bool {118	let state = EvaluationState::default();119	if let Err(e) = main_real(&state, opts) {120		if let Error::Evaluation(e) = e {121			eprintln!("{}", state.stringify_err(&e));122		} else {123			eprintln!("{}", e);124		}125		return false;126	}127	true128}129130fn main_real(state: &EvaluationState, opts: Opts) -> Result<(), Error> {131	opts.general.configure(&state)?;132	opts.manifest.configure(&state)?;133134	let val = if opts.input.exec {135		state.set_manifest_format(ManifestFormat::ToString);136		state.evaluate_snippet_raw(137			Rc::new(PathBuf::from("args")),138			(&opts.input.input as &str).into(),139		)?140	} else if opts.input.input == "-" {141		let mut input = Vec::new();142		std::io::stdin().read_to_end(&mut input)?;143		let input_str = std::str::from_utf8(&input)?.into();144		state.evaluate_snippet_raw(Rc::new(PathBuf::from("<stdin>")), input_str)?145	} else {146		state.evaluate_file_raw(&PathBuf::from(opts.input.input))?147	};148149	let val = state.with_tla(val)?;150151	if let Some(multi) = opts.output.multi {152		if opts.output.create_output_dirs {153			let mut dir = multi.clone();154			dir.pop();155			create_dir_all(dir)?;156		}157		for (file, data) in state.manifest_multi(val)?.iter() {158			let mut path = multi.clone();159			path.push(&file as &str);160			if opts.output.create_output_dirs {161				let mut dir = path.clone();162				dir.pop();163				create_dir_all(dir)?;164			}165			println!("{}", path.to_str().expect("path"));166			let mut file = File::create(path)?;167			writeln!(file, "{}", data)?;168		}169	} else if let Some(path) = opts.output.output_file {170		if opts.output.create_output_dirs {171			let mut dir = path.clone();172			dir.pop();173			create_dir_all(dir)?;174		}175		let mut file = File::create(path)?;176		writeln!(file, "{}", state.manifest(val)?)?;177	} else {178		let output = state.manifest(val)?;179		if !output.is_empty() {180			println!("{}", output);181		}182	}183184	Ok(())185}