difftreelog
feat --line_padding flag
in: master
1 file changed
cmds/jrsonnet/src/main.rsdiffbeforeafterboth1pub mod location;23use clap::Clap;4use jsonnet_evaluator::{EvaluationState, LocError, StackTrace, Val};5use location::{offset_to_location, CodeLocation};6use std::env::current_dir;7use std::{path::PathBuf, str::FromStr};89enum Format {10 None,11 Json,12 Yaml,13}1415impl FromStr for Format {16 type Err = &'static str;17 fn from_str(s: &str) -> Result<Self, Self::Err> {18 Ok(match s {19 "none" => Format::None,20 "json" => Format::Json,21 "yaml" => Format::Yaml,22 _ => return Err("no such format"),23 })24 }25}2627#[derive(PartialEq)]28enum TraceFormat {29 CppJsonnet,30 GoJsonnet,31 Custom,32}33impl FromStr for TraceFormat {34 type Err = &'static str;35 fn from_str(s: &str) -> Result<Self, Self::Err> {36 Ok(match s {37 "cpp" => TraceFormat::CppJsonnet,38 "go" => TraceFormat::GoJsonnet,39 "default" => TraceFormat::Custom,40 _ => return Err("no such format"),41 })42 }43}4445#[derive(Clap)]46#[clap(version = "0.1.0", author = "Lach <iam@lach.pw>")]47struct Opts {48 #[clap(long, about = "Disable global std variable")]49 no_stdlib: bool,50 #[clap(long, about = "Add external string")]51 ext_str: Option<Vec<String>>,52 #[clap(long, about = "Add external string from code")]53 ext_code: Option<Vec<String>>,54 #[clap(long, about = "Add TLA")]55 tla_str: Option<Vec<String>>,56 #[clap(long, about = "Add TLA from code")]57 tla_code: Option<Vec<String>>,58 #[clap(long, short = "f", default_value = "json", possible_values = &["none", "json", "yaml"], about = "Output format, wraps resulting value to corresponding std.manifest call")]59 format: Format,60 #[clap(long, default_value = "default", possible_values = &["cpp", "go", "default"], about = "Emulated needed stacktrace display")]61 trace_format: TraceFormat,6263 #[clap(64 long,65 short = "s",66 default_value = "200",67 about = "Number of allowed stack frames"68 )]69 max_stack: usize,70 #[clap(71 long,72 short = "t",73 default_value = "20",74 about = "Max length of stack trace before cropping"75 )]76 max_trace: usize,7778 #[clap(about = "File to compile", index = 1)]79 input: String,80}8182fn main() {83 let opts: Opts = Opts::parse();84 let evaluator = jsonnet_evaluator::EvaluationState::default();85 if !opts.no_stdlib {86 evaluator.add_stdlib();87 }88 let mut input = current_dir().unwrap();89 input.push(opts.input.clone());90 let code_string = String::from_utf8(std::fs::read(opts.input.clone()).unwrap()).unwrap();91 if let Err(e) = evaluator.add_file(input.clone(), code_string.clone()) {92 print_syntax_error(e, &input, &code_string);93 std::process::exit(2);94 }95 let result = evaluator.evaluate_file(&input);96 match result {97 Ok(v) => {98 let v = match opts.format {99 Format::Json => {100 if opts.no_stdlib {101 evaluator.add_stdlib();102 }103 evaluator.add_global("__tmp__to_json__".to_owned(), v);104 let v = evaluator105 .parse_evaluate_raw("std.manifestJsonEx(__tmp__to_json__, \" \")");106 match v {107 Ok(v) => v,108 Err(err) => {109 print_error(&err, evaluator, &opts);110 std::process::exit(2);111 }112 }113 }114 Format::Yaml => {115 if opts.no_stdlib {116 evaluator.add_stdlib();117 }118 evaluator.add_global("__tmp__to_yaml__".to_owned(), v);119 let v = evaluator120 .parse_evaluate_raw("std.manifestYamlDoc(__tmp__to_yaml__, \" \")");121 match v {122 Ok(v) => v,123 Err(err) => {124 print_error(&err, evaluator, &opts);125 std::process::exit(2);126 }127 }128 }129 _ => v,130 };131 match v {132 Val::Str(s) => println!("{}", s),133 Val::Num(n) => println!("{}", n),134 _v => eprintln!(135 "jsonnet output is not a string.\nDid you forgot to set --format, or wrap your data with std.manifestJson?"136 ),137 }138 }139 Err(err) => {140 print_error(&err, evaluator, &opts);141 }142 }143}144145fn print_error(err: &LocError, evaluator: EvaluationState, opts: &Opts) {146 println!("Error: {:?}", err.0);147 print_trace(&(err.1), evaluator, &opts);148}149150fn print_syntax_error(error: jsonnet_parser::ParseError, file: &PathBuf, code: &str) {151 use annotate_snippets::{152 display_list::{DisplayList, FormatOptions},153 snippet::{Annotation, AnnotationType, Slice, Snippet, SourceAnnotation},154 };155 //&("Expected: ".to_owned() + error.expected)156 let origin = file.to_str().unwrap();157 let error_message = format!("Expected: {}", error.expected);158 let snippet = Snippet {159 opt: FormatOptions {160 color: true,161 ..Default::default()162 },163 title: Some(Annotation {164 label: Some(&error_message),165 id: None,166 annotation_type: AnnotationType::Error,167 }),168 footer: vec![],169 slices: vec![Slice {170 source: &code,171 line_start: 1,172 origin: Some(origin),173 fold: false,174 annotations: vec![SourceAnnotation {175 label: "At this position",176 annotation_type: AnnotationType::Error,177 range: (error.location.offset, error.location.offset + 1),178 }],179 }],180 };181182 let dl = DisplayList::from(snippet);183 println!("{}", dl);184}185186fn print_trace(trace: &StackTrace, evaluator: EvaluationState, opts: &Opts) {187 use annotate_snippets::{188 display_list::{DisplayList, FormatOptions},189 snippet::{Annotation, AnnotationType, Slice, Snippet, SourceAnnotation},190 };191 for item in trace.0.iter() {192 let desc = &item.1;193 if (item.0).1.is_none() {194 continue;195 }196 let source = (item.0).1.clone().unwrap();197 let code = evaluator.get_source(&source.0);198 if code.is_none() {199 continue;200 }201 let code = code.unwrap();202 let start_end = offset_to_location(&code, &[source.1, source.2]);203 if opts.trace_format == TraceFormat::Custom {204 let source_fragment: String = code205 .chars()206 .skip(start_end[0].line_start_offset)207 .take(start_end[1].line_end_offset - start_end[0].line_start_offset)208 .collect();209 let snippet = Snippet {210 opt: FormatOptions {211 color: true,212 ..Default::default()213 },214 title: Some(Annotation {215 label: Some(&item.1),216 id: None,217 annotation_type: AnnotationType::Error,218 }),219 footer: vec![],220 slices: vec![Slice {221 source: &source_fragment,222 line_start: start_end[0].line,223 origin: Some(&source.0.to_str().unwrap()),224 fold: false,225 annotations: vec![SourceAnnotation {226 label: desc,227 annotation_type: AnnotationType::Error,228 range: (229 source.1 - start_end[0].line_start_offset,230 source.2 - start_end[0].line_start_offset,231 ),232 }],233 }],234 };235236 let dl = DisplayList::from(snippet);237 println!("{}", dl);238 } else {239 print_jsonnet_pair(240 source.0.to_str().unwrap(),241 &start_end[0],242 &start_end[1],243 opts.trace_format == TraceFormat::GoJsonnet,244 );245 }246 }247}248249fn print_jsonnet_pair(file: &str, start: &CodeLocation, end: &CodeLocation, is_go: bool) {250 if is_go {251 print!(" ");252 } else {253 print!(" ");254 }255 print!("{}:", file);256 if start.line == end.line {257 // IDK why, but this is the behavior original jsonnet cpp impl shows258 if start.column == end.column || !is_go && start.column + 1 == end.column {259 println!("{}:{}", start.line, end.column)260 } else {261 println!("{}:{}-{}", start.line, start.column, end.column);262 }263 } else {264 println!(265 "({}:{})-({}:{})",266 start.line, end.column, start.line, end.column267 );268 }269}1pub mod location;23use clap::Clap;4use jsonnet_evaluator::{EvaluationState, LocError, StackTrace, Val};5use location::{offset_to_location, CodeLocation};6use std::env::current_dir;7use std::{path::PathBuf, str::FromStr};89enum Format {10 None,11 Json,12 Yaml,13}1415impl FromStr for Format {16 type Err = &'static str;17 fn from_str(s: &str) -> Result<Self, Self::Err> {18 Ok(match s {19 "none" => Format::None,20 "json" => Format::Json,21 "yaml" => Format::Yaml,22 _ => return Err("no such format"),23 })24 }25}2627#[derive(PartialEq)]28enum TraceFormat {29 CppJsonnet,30 GoJsonnet,31 Custom,32}33impl FromStr for TraceFormat {34 type Err = &'static str;35 fn from_str(s: &str) -> Result<Self, Self::Err> {36 Ok(match s {37 "cpp" => TraceFormat::CppJsonnet,38 "go" => TraceFormat::GoJsonnet,39 "default" => TraceFormat::Custom,40 _ => return Err("no such format"),41 })42 }43}4445#[derive(Clap)]46#[clap(version = "0.1.0", author = "Lach <iam@lach.pw>")]47struct Opts {48 #[clap(long, about = "Disable global std variable")]49 no_stdlib: bool,50 #[clap(long, about = "Add external string")]51 ext_str: Option<Vec<String>>,52 #[clap(long, about = "Add external string from code")]53 ext_code: Option<Vec<String>>,54 #[clap(long, about = "Add TLA")]55 tla_str: Option<Vec<String>>,56 #[clap(long, about = "Add TLA from code")]57 tla_code: Option<Vec<String>>,58 #[clap(long, short = "f", default_value = "json", possible_values = &["none", "json", "yaml"], about = "Output format, wraps resulting value to corresponding std.manifest call")]59 format: Format,60 #[clap(long, default_value = "default", possible_values = &["cpp", "go", "default"], about = "Emulated needed stacktrace display")]61 trace_format: TraceFormat,6263 #[clap(64 long,65 short = "s",66 default_value = "200",67 about = "Number of allowed stack frames"68 )]69 max_stack: usize,70 #[clap(71 long,72 short = "t",73 default_value = "20",74 about = "Max length of stack trace before cropping"75 )]76 max_trace: usize,7778 #[clap(79 long,80 default_value = "3",81 about = "When using --format, this option specifies string to pad output with"82 )]83 line_padding: usize,8485 #[clap(about = "File to compile", index = 1)]86 input: String,87}8889fn main() {90 let opts: Opts = Opts::parse();91 let evaluator = jsonnet_evaluator::EvaluationState::default();92 if !opts.no_stdlib {93 evaluator.add_stdlib();94 }95 let mut input = current_dir().unwrap();96 input.push(opts.input.clone());97 let code_string = String::from_utf8(std::fs::read(opts.input.clone()).unwrap()).unwrap();98 if let Err(e) = evaluator.add_file(input.clone(), code_string.clone()) {99 print_syntax_error(e, &input, &code_string);100 std::process::exit(1);101 }102 let result = evaluator.evaluate_file(&input);103 match result {104 Ok(v) => {105 let v = match opts.format {106 Format::Json => {107 if opts.no_stdlib {108 evaluator.add_stdlib();109 }110 evaluator.add_global("__tmp__to_json__".to_owned(), v);111 let v = evaluator.parse_evaluate_raw(&format!(112 "std.manifestJsonEx(__tmp__to_json__, \"{}\")",113 " ".repeat(opts.line_padding),114 ));115 match v {116 Ok(v) => v,117 Err(err) => {118 print_error(&err, evaluator, &opts);119 std::process::exit(1);120 }121 }122 }123 Format::Yaml => {124 if opts.no_stdlib {125 evaluator.add_stdlib();126 }127 evaluator.add_global("__tmp__to_yaml__".to_owned(), v);128 let v = evaluator129 .parse_evaluate_raw("std.manifestYamlDoc(__tmp__to_yaml__, \" \")");130 match v {131 Ok(v) => v,132 Err(err) => {133 print_error(&err, evaluator, &opts);134 std::process::exit(1);135 }136 }137 }138 _ => v,139 };140 match v {141 Val::Str(s) => println!("{}", s),142 Val::Num(n) => println!("{}", n),143 _v => eprintln!(144 "jsonnet output is not a string.\nDid you forgot to set --format, or wrap your data with std.manifestJson?"145 ),146 }147 }148 Err(err) => {149 print_error(&err, evaluator, &opts);150 std::process::exit(1);151 }152 }153}154155fn print_error(err: &LocError, evaluator: EvaluationState, opts: &Opts) {156 println!("Error: {:?}", err.0);157 print_trace(&(err.1), evaluator, &opts);158}159160fn print_syntax_error(error: jsonnet_parser::ParseError, file: &PathBuf, code: &str) {161 use annotate_snippets::{162 display_list::{DisplayList, FormatOptions},163 snippet::{Annotation, AnnotationType, Slice, Snippet, SourceAnnotation},164 };165 //&("Expected: ".to_owned() + error.expected)166 let origin = file.to_str().unwrap();167 let error_message = format!("Expected: {}", error.expected);168 let snippet = Snippet {169 opt: FormatOptions {170 color: true,171 ..Default::default()172 },173 title: Some(Annotation {174 label: Some(&error_message),175 id: None,176 annotation_type: AnnotationType::Error,177 }),178 footer: vec![],179 slices: vec![Slice {180 source: &code,181 line_start: 1,182 origin: Some(origin),183 fold: false,184 annotations: vec![SourceAnnotation {185 label: "At this position",186 annotation_type: AnnotationType::Error,187 range: (error.location.offset, error.location.offset + 1),188 }],189 }],190 };191192 let dl = DisplayList::from(snippet);193 println!("{}", dl);194}195196fn print_trace(trace: &StackTrace, evaluator: EvaluationState, opts: &Opts) {197 use annotate_snippets::{198 display_list::{DisplayList, FormatOptions},199 snippet::{Annotation, AnnotationType, Slice, Snippet, SourceAnnotation},200 };201 for item in trace.0.iter() {202 let desc = &item.1;203 if (item.0).1.is_none() {204 continue;205 }206 let source = (item.0).1.clone().unwrap();207 let code = evaluator.get_source(&source.0);208 if code.is_none() {209 continue;210 }211 let code = code.unwrap();212 let start_end = offset_to_location(&code, &[source.1, source.2]);213 if opts.trace_format == TraceFormat::Custom {214 let source_fragment: String = code215 .chars()216 .skip(start_end[0].line_start_offset)217 .take(start_end[1].line_end_offset - start_end[0].line_start_offset)218 .collect();219 let snippet = Snippet {220 opt: FormatOptions {221 color: true,222 ..Default::default()223 },224 title: Some(Annotation {225 label: Some(&item.1),226 id: None,227 annotation_type: AnnotationType::Error,228 }),229 footer: vec![],230 slices: vec![Slice {231 source: &source_fragment,232 line_start: start_end[0].line,233 origin: Some(&source.0.to_str().unwrap()),234 fold: false,235 annotations: vec![SourceAnnotation {236 label: desc,237 annotation_type: AnnotationType::Error,238 range: (239 source.1 - start_end[0].line_start_offset,240 source.2 - start_end[0].line_start_offset,241 ),242 }],243 }],244 };245246 let dl = DisplayList::from(snippet);247 println!("{}", dl);248 } else {249 print_jsonnet_pair(250 source.0.to_str().unwrap(),251 &start_end[0],252 &start_end[1],253 opts.trace_format == TraceFormat::GoJsonnet,254 );255 }256 }257}258259fn print_jsonnet_pair(file: &str, start: &CodeLocation, end: &CodeLocation, is_go: bool) {260 if is_go {261 print!(" ");262 } else {263 print!(" ");264 }265 print!("{}:", file);266 if start.line == end.line {267 // IDK why, but this is the behavior original jsonnet cpp impl shows268 if start.column == end.column || !is_go && start.column + 1 == end.column {269 println!("{}:{}", start.line, end.column)270 } else {271 println!("{}:{}-{}", start.line, start.column, end.column);272 }273 } else {274 println!(275 "({}:{})-({}:{})",276 start.line, end.column, start.line, end.column277 );278 }279}