1use std::{2 env::current_dir,3 fs::{create_dir_all, File},4 io::{Read, Write},5};67use clap::{AppSettings, IntoApp, Parser};8use clap_complete::Shell;9use jrsonnet_cli::{ConfigureState, GcOpts, GeneralOpts, ManifestOpts, OutputOpts};10use jrsonnet_evaluator::{error::LocError, State};1112#[cfg(feature = "mimalloc")]13#[global_allocator]14static GLOBAL: mimallocator::Mimalloc = mimallocator::Mimalloc;1516#[derive(Parser)]17enum SubOpts {18 19 Generate {20 21 shell: Shell,22 },23}2425#[derive(Parser)]26#[clap(next_help_heading = "DEBUG")]27struct DebugOpts {28 29 30 #[clap(long, name = "size")]31 pub os_stack: Option<usize>,32}3334#[derive(Parser)]35#[clap(next_help_heading = "INPUT")]36struct InputOpts {37 38 #[clap(long, short = 'e')]39 pub exec: bool,4041 42 pub input: Option<String>,43}4445#[derive(Parser)]46#[clap(47 global_setting = AppSettings::DeriveDisplayOrder,48 args_conflicts_with_subcommands = true,49)]50struct Opts {51 #[clap(subcommand)]52 sub: Option<SubOpts>,5354 #[clap(flatten)]55 input: InputOpts,56 #[clap(flatten)]57 general: GeneralOpts,58 #[clap(flatten)]59 manifest: ManifestOpts,60 #[clap(flatten)]61 output: OutputOpts,62 #[clap(flatten)]63 debug: DebugOpts,64 #[clap(flatten)]65 gc: GcOpts,66}6768fn main() {69 let opts: Opts = Opts::parse();7071 if let Some(sub) = opts.sub {72 match sub {73 SubOpts::Generate { shell } => {74 use clap_complete::generate;75 let app = &mut Opts::command();76 let buf = &mut std::io::stdout();77 generate(shell, app, "jrsonnet", buf);78 std::process::exit(0)79 }80 }81 }8283 let success = if let Some(size) = opts.debug.os_stack {84 std::thread::Builder::new()85 .stack_size(size * 1024 * 1024)86 .spawn(|| main_catch(opts))87 .expect("new thread spawned")88 .join()89 .expect("thread finished successfully")90 } else {91 main_catch(opts)92 };93 if !success {94 std::process::exit(1);95 }96}9798#[derive(thiserror::Error, Debug)]99enum Error {100 101 #[error("evaluation error")]102 Evaluation(jrsonnet_evaluator::error::LocError),103 #[error("io error")]104 Io(#[from] std::io::Error),105 #[error("input is not utf8 encoded")]106 Utf8(#[from] std::str::Utf8Error),107 #[error("missing input argument")]108 MissingInputArgument,109}110impl From<LocError> for Error {111 fn from(e: LocError) -> Self {112 Self::Evaluation(e)113 }114}115116fn main_catch(opts: Opts) -> bool {117 let _printer = opts.gc.stats_printer();118 let s = State::default();119 if let Err(e) = main_real(&s, opts) {120 if let Error::Evaluation(e) = e {121 eprintln!("{}", s.stringify_err(&e));122 } else {123 eprintln!("{}", e);124 }125 return false;126 }127 true128}129130fn main_real(s: &State, opts: Opts) -> Result<(), Error> {131 opts.general.configure(s)?;132 opts.manifest.configure(s)?;133134 let input = opts.input.input.ok_or(Error::MissingInputArgument)?;135 let val = if opts.input.exec {136 s.evaluate_snippet("<cmdline>".to_owned(), (&input as &str).into())?137 } else if input == "-" {138 let mut input = Vec::new();139 std::io::stdin().read_to_end(&mut input)?;140 let input_str = std::str::from_utf8(&input)?.into();141 s.evaluate_snippet("<stdin>".to_owned(), input_str)?142 } else {143 s.import(s.resolve_file(¤t_dir().expect("cwd"), &input)?)?144 };145146 let val = s.with_tla(val)?;147148 if let Some(multi) = opts.output.multi {149 if opts.output.create_output_dirs {150 let mut dir = multi.clone();151 dir.pop();152 create_dir_all(dir)?;153 }154 for (file, data) in s.manifest_multi(val)?.iter() {155 let mut path = multi.clone();156 path.push(file as &str);157 if opts.output.create_output_dirs {158 let mut dir = path.clone();159 dir.pop();160 create_dir_all(dir)?;161 }162 println!("{}", path.to_str().expect("path"));163 let mut file = File::create(path)?;164 writeln!(file, "{}", data)?;165 }166 } else if let Some(path) = opts.output.output_file {167 if opts.output.create_output_dirs {168 let mut dir = path.clone();169 dir.pop();170 create_dir_all(dir)?;171 }172 let mut file = File::create(path)?;173 writeln!(file, "{}", s.manifest(val)?)?;174 } else {175 let output = s.manifest(val)?;176 if !output.is_empty() {177 println!("{}", output);178 }179 }180181 Ok(())182}