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};9use jrsonnet_evaluator::{error::LocError, State};1011#[cfg(feature = "mimalloc")]12#[global_allocator]13static GLOBAL: mimallocator::Mimalloc = mimallocator::Mimalloc;1415#[derive(Parser)]16enum SubOpts {17 18 Generate {19 20 shell: Shell,21 },22}2324#[derive(Parser)]25#[clap(next_help_heading = "DEBUG")]26struct DebugOpts {27 28 29 #[clap(long, name = "size")]30 pub os_stack: Option<usize>,31}3233#[derive(Parser)]34#[clap(next_help_heading = "INPUT")]35struct InputOpts {36 37 #[clap(long, short = 'e')]38 pub exec: bool,3940 41 pub input: Option<String>,42}4344#[derive(Parser)]45#[clap(args_conflicts_with_subcommands = true, disable_version_flag = true)]46struct Opts {47 #[clap(subcommand)]48 sub: Option<SubOpts>,4950 #[clap(flatten)]51 input: InputOpts,52 #[clap(flatten)]53 general: GeneralOpts,54 #[clap(flatten)]55 manifest: ManifestOpts,56 #[clap(flatten)]57 output: OutputOpts,58 #[clap(flatten)]59 debug: DebugOpts,60}6162fn main() {63 let opts: Opts = Opts::parse();6465 if let Some(sub) = opts.sub {66 match sub {67 SubOpts::Generate { shell } => {68 use clap_complete::generate;69 let app = &mut Opts::command();70 let buf = &mut std::io::stdout();71 generate(shell, app, "jrsonnet", buf);72 std::process::exit(0)73 }74 }75 }7677 let success = if let Some(size) = opts.debug.os_stack {78 std::thread::Builder::new()79 .stack_size(size * 1024 * 1024)80 .spawn(|| main_catch(opts))81 .expect("new thread spawned")82 .join()83 .expect("thread finished successfully")84 } else {85 main_catch(opts)86 };87 if !success {88 std::process::exit(1);89 }90}9192#[derive(thiserror::Error, Debug)]93enum Error {94 95 #[error("evaluation error")]96 Evaluation(jrsonnet_evaluator::error::LocError),97 #[error("io error")]98 Io(#[from] std::io::Error),99 #[error("input is not utf8 encoded")]100 Utf8(#[from] std::str::Utf8Error),101 #[error("missing input argument")]102 MissingInputArgument,103}104impl From<LocError> for Error {105 fn from(e: LocError) -> Self {106 Self::Evaluation(e)107 }108}109110fn main_catch(opts: Opts) -> bool {111 let s = State::default();112 if let Err(e) = main_real(&s, opts) {113 if let Error::Evaluation(e) = e {114 eprintln!("{}", s.stringify_err(&e));115 } else {116 eprintln!("{}", e);117 }118 return false;119 }120 true121}122123fn main_real(s: &State, opts: Opts) -> Result<(), Error> {124 let _guards = opts.general.configure(s)?;125 opts.manifest.configure(s)?;126127 let input = opts.input.input.ok_or(Error::MissingInputArgument)?;128 let val = if opts.input.exec {129 s.evaluate_snippet("<cmdline>".to_owned(), &input as &str)?130 } else if input == "-" {131 let mut input = Vec::new();132 std::io::stdin().read_to_end(&mut input)?;133 let input_str = std::str::from_utf8(&input)?;134 s.evaluate_snippet("<stdin>".to_owned(), input_str)?135 } else {136 s.import(&input)?137 };138139 let val = s.with_tla(val)?;140141 if let Some(multi) = opts.output.multi {142 if opts.output.create_output_dirs {143 let mut dir = multi.clone();144 dir.pop();145 create_dir_all(dir)?;146 }147 for (file, data) in s.manifest_multi(val)?.iter() {148 let mut path = multi.clone();149 path.push(file as &str);150 if opts.output.create_output_dirs {151 let mut dir = path.clone();152 dir.pop();153 create_dir_all(dir)?;154 }155 println!("{}", path.to_str().expect("path"));156 let mut file = File::create(path)?;157 writeln!(file, "{}", data)?;158 }159 } else if let Some(path) = opts.output.output_file {160 if opts.output.create_output_dirs {161 let mut dir = path.clone();162 dir.pop();163 create_dir_all(dir)?;164 }165 let mut file = File::create(path)?;166 writeln!(file, "{}", s.manifest(val)?)?;167 } else {168 let output = s.manifest(val)?;169 if !output.is_empty() {170 println!("{}", output);171 }172 }173174 Ok(())175}