1#![cfg(test)]23use miette::{4 Diagnostic, GraphicalReportHandler, GraphicalTheme, LabeledSpan, ThemeCharacters, ThemeStyles,5};6use thiserror::Error;78use crate::{parse, AstNode};910#[derive(Debug, Error)]11#[error("syntax error")]12struct MyDiagnostic {13 code: String,14 spans: Vec<LabeledSpan>,15}16impl Diagnostic for MyDiagnostic {17 fn code<'a>(&'a self) -> Option<Box<dyn std::fmt::Display + 'a>> {18 None19 }2021 fn severity(&self) -> Option<miette::Severity> {22 None23 }2425 fn help<'a>(&'a self) -> Option<Box<dyn std::fmt::Display + 'a>> {26 None27 }2829 fn url<'a>(&'a self) -> Option<Box<dyn std::fmt::Display + 'a>> {30 None31 }3233 fn source_code(&self) -> Option<&dyn miette::SourceCode> {34 Some(&self.code)35 }3637 fn labels(&self) -> Option<Box<dyn Iterator<Item = miette::LabeledSpan> + '_>> {38 Some(Box::new(self.spans.clone().into_iter()))39 }4041 fn related<'a>(&'a self) -> Option<Box<dyn Iterator<Item = &'a dyn Diagnostic> + 'a>> {42 None43 }44}4546fn process(text: &str) -> String {47 use std::fmt::Write;48 let mut out = String::new();49 let (node, errors) = parse(text);50 write!(out, "{:#?}", node.syntax()).unwrap();51 if !errors.is_empty() && !text.is_empty() {52 writeln!(out, "===").unwrap();53 for err in &errors {54 writeln!(out, "{:?}", err).unwrap();55 }56 let diag = MyDiagnostic {57 code: text.to_string(),58 spans: errors.into_iter().map(|e| e.into()).collect(),59 };6061 let handler = GraphicalReportHandler::new_themed(GraphicalTheme {62 characters: ThemeCharacters::ascii(),63 styles: ThemeStyles::none(),64 });6566 write!(out, "===").unwrap();67 handler.render_report(&mut out, &diag).unwrap();68 }69 out70}71macro_rules! mk_test {72 ($($name:ident => $test:expr)+) => {$(73 #[test]74 fn $name() {75 let src = indoc::indoc!($test);76 let result = process(&src);77 insta::assert_snapshot!(stringify!($name), result, src);7879 }80 )+};81 }82mk_test!(83 empty => r#" "#84 function => r#"85 function(a, b = 1) a + b86 "#87 function_error_no_value => r#"88 function(a, b = ) a + b89 "#90 function_error_rparen => r#"91 function(a, b92 "#93 function_error_body => r#"94 function(a, b)95 "#96 local_novalue => r#"97 local a =98 "#99 local_no_value_recovery => r#"100 local a =101 local b = 3;102 1103 "#104105 array_comp => r#"106 [a for a in [1, 2, 3]]107 "#108 array_comp_incompatible_with_multiple_elems => r#"109 [a for a in [1, 2, 3], b]110 "#111112 no_rhs => r#"113 a +114 "#115 no_lhs => r#"116 + 2117 "#118 no_operator => "119 2 2120 "121122 named_before_positional => "123 a(1, 2, b=4, 3, 5, k = 12, 6)124 "125126 wrong_field_end => "127 {128 a: 1;129 b: 2;130 }131 "132133134 plain_call => "135 std.substr(a, 0, std.length(b)) == b136 "137138 destruct => "139 local [a, b, c] = arr;140 local [a, ...] = arr_rest;141 local [..., a] = rest_arr;142 local [...] = rest_in_arr;143 local [a, ...n] = arr_rest_n;144 local [...n, a] = rest_arr_n;145 local [...n] = rest_in_arr_n;146147 local {a, b, c} = obj;148 local {a, b, c, ...} = obj_rest;149 local {a, b, c, ...n} = obj_rest_n;150151 null152 "153154 str_block_missing_indent => "155 |||156 "157 str_block_missing_termination => "158 |||159 hello160 "161 str_block_missing_newline => "162 |||hello163 "164 str_block_missing_indent_text => "165 |||166 hello167 "168);169170#[test]171fn stdlib() {172 let src = jrsonnet_stdlib::STDLIB_STR;173 let result = process(src);174 insta::assert_snapshot!("stdlib", result, src);175}