difftreelog
feat add --version argumnet
in: master
1 file changed
cmds/jrsonnet/src/main.rsdiffbeforeafterboth1use 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}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}