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 21 22 #[clap(long, name = "size")]23 pub os_stack: Option<usize>,24 25 #[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 104 #[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}