diff --git a/rsjsonnet-lang/src/program/data.rs b/rsjsonnet-lang/src/program/data.rs index f44f09d..62bc22e 100644 --- a/rsjsonnet-lang/src/program/data.rs +++ b/rsjsonnet-lang/src/program/data.rs @@ -726,6 +726,7 @@ pub(super) enum BuiltInFunc { ManifestJsonEx, ManifestYamlDoc, ManifestYamlStream, + ManifestTomlEx, // Arrays MakeArray, Count, diff --git a/rsjsonnet-lang/src/program/eval/call.rs b/rsjsonnet-lang/src/program/eval/call.rs index 3773748..93f5ebd 100644 --- a/rsjsonnet-lang/src/program/eval/call.rs +++ b/rsjsonnet-lang/src/program/eval/call.rs @@ -593,6 +593,12 @@ impl Evaluator<'_> { self.state_stack.push(State::DoThunk(arg1.view())); self.state_stack.push(State::DoThunk(arg0.view())); } + BuiltInFunc::ManifestTomlEx => { + let [arg0, arg1] = check_num_args(args); + self.state_stack.push(State::StdManifestTomlEx); + self.state_stack.push(State::DoThunk(arg1.view())); + self.state_stack.push(State::DoThunk(arg0.view())); + } BuiltInFunc::MakeArray => { let [arg0, arg1] = check_num_args(args); self.state_stack.push(State::StdMakeArray); diff --git a/rsjsonnet-lang/src/program/eval/manifest.rs b/rsjsonnet-lang/src/program/eval/manifest.rs index d9204ab..80a6076 100644 --- a/rsjsonnet-lang/src/program/eval/manifest.rs +++ b/rsjsonnet-lang/src/program/eval/manifest.rs @@ -1,8 +1,9 @@ use std::fmt::Write as _; use std::rc::Rc; -use super::super::ValueData; +use super::super::{ArrayData, ObjectData, ValueData}; use super::{EvalError, EvalErrorKind, Evaluator, State, TraceItem}; +use crate::gc::GcView; use crate::interner::InternedStr; pub(super) struct ManifestJsonFormat { @@ -500,6 +501,348 @@ impl Evaluator<'_> { Ok(()) } + + pub(super) fn do_manifest_toml_peek_sub_table(&mut self) { + if let ValueData::Array(array) = self.value_stack.pop().unwrap() { + let array = array.view(); + if let Some(item0) = array.first() { + let item0 = item0.view(); + self.state_stack + .push(State::ManifestTomlPeekSubTableArrayItem { array, index: 0 }); + self.state_stack.push(State::DoThunk(item0)); + } + } + } + + pub(super) fn do_manifest_toml_peek_sub_table_array_item( + &mut self, + array: GcView, + index: usize, + ) { + let value = self.value_stack.pop().unwrap(); + if matches!(value, ValueData::Object(_)) { + let next_index = index + 1; + if let Some(item) = array.get(next_index) { + let item = item.view(); + self.state_stack + .push(State::ManifestTomlPeekSubTableArrayItem { + array, + index: next_index, + }); + self.state_stack.push(State::DoThunk(item)); + } + } + } + + pub(super) fn prepare_manifest_toml_table( + &mut self, + object: GcView, + has_header: bool, + path: Rc<[InternedStr]>, + indent: Rc, + ) { + self.state_stack.push(State::ManifestTomlTable { + object: object.clone(), + has_header, + path, + indent, + }); + + let visible_fields: Vec<_> = object + .get_fields_order() + .iter() + .filter_map(|(name, visible)| visible.then_some(name)) + .collect(); + for field_name in visible_fields.iter().rev() { + let field_thunk = self + .program + .find_object_field_thunk(&object, 0, field_name) + .unwrap(); + + self.state_stack.push(State::ManifestTomlPeekSubTable); + self.state_stack.push(State::DoThunk(field_thunk)); + } + self.check_object_asserts(&object); + } + + pub(super) fn do_manifest_toml_table( + &mut self, + object: GcView, + has_header: bool, + path: Rc<[InternedStr]>, + indent: Rc, + ) { + let visible_fields: Vec<_> = object + .get_fields_order() + .iter() + .filter(|&&(_, visible)| visible) + .map(|(name, _)| { + // Check if the field is an object or an array of objects + let (_, field) = object.find_field(0, name).unwrap(); + let field_value = field.thunk.get().unwrap().view().get_value().unwrap(); + let is_sub_table = match field_value { + ValueData::Array(array) => { + let array = array.view(); + !array.is_empty() + && array.iter().all(|item| { + matches!(item.view().get_value().unwrap(), ValueData::Object(_)) + }) + } + ValueData::Object(_) => true, + _ => false, + }; + (name, is_sub_table) + }) + .collect(); + + let mut has_sub_tables = false; + for (i, &(field_name, _)) in visible_fields + .iter() + .rev() + .filter(|&&(_, is_sub_table)| is_sub_table) + .enumerate() + { + if i != 0 { + self.state_stack.push(State::AppendToString('\n'.into())); + } + + has_sub_tables = true; + + let (_, field) = object.find_field(0, field_name).unwrap(); + let field_value = field.thunk.get().unwrap().view().get_value().unwrap(); + + let sub_path: Rc<[_]> = path + .iter() + .chain(std::iter::once(field_name)) + .cloned() + .collect(); + + self.push_trace_item(TraceItem::ManifestObjectField { + name: field_name.clone(), + }); + + match field_value { + ValueData::Array(array) => { + let array = array.view(); + + let mut header = String::new(); + header.push('\n'); + for _ in 0..path.len() { + header.push_str(&indent); + } + header.push_str("[["); + for part in path.iter() { + header.push_str(&escape_key_toml(part.value())); + header.push('.'); + } + header.push_str(&escape_key_toml(field_name.value())); + header.push_str("]]"); + + for (i, item) in array.iter().enumerate().rev() { + if i != array.len() - 1 { + self.state_stack.push(State::AppendToString('\n'.into())); + } + + let ValueData::Object(sub_object) = item.view().get_value().unwrap() else { + unreachable!(); + }; + let sub_object = sub_object.view(); + + self.push_trace_item(TraceItem::ManifestArrayItem { index: i }); + self.prepare_manifest_toml_table( + sub_object, + true, + sub_path.clone(), + indent.clone(), + ); + self.delay_trace_item(); + self.state_stack.push(State::AppendToString(header.clone())); + } + } + ValueData::Object(sub_object) => { + let sub_object = sub_object.view(); + self.prepare_manifest_toml_table(sub_object, true, sub_path, indent.clone()); + + let mut header = String::new(); + header.push('\n'); + for _ in 0..path.len() { + header.push_str(&indent); + } + header.push('['); + for part in path.iter() { + header.push_str(&escape_key_toml(part.value())); + header.push('.'); + } + header.push_str(&escape_key_toml(field_name.value())); + header.push(']'); + self.state_stack.push(State::AppendToString(header)); + } + _ => unreachable!(), + } + + self.delay_trace_item(); + } + + if has_sub_tables { + self.state_stack.push(State::AppendToString('\n'.into())); + } + + for (i, &(field_name, _)) in visible_fields + .iter() + .rev() + .filter(|&&(_, is_sub_table)| !is_sub_table) + .enumerate() + { + if i != 0 { + self.state_stack.push(State::AppendToString('\n'.into())); + } + + let field_thunk = self + .program + .find_object_field_thunk(&object, 0, field_name) + .unwrap(); + + self.push_trace_item(TraceItem::ManifestObjectField { + name: field_name.clone(), + }); + self.state_stack.push(State::ManifestTomlValue { + indent: indent.clone(), + depth: path.len(), + single_line: false, + }); + self.state_stack.push(State::DoThunk(field_thunk)); + self.delay_trace_item(); + + let mut tmp = String::new(); + for _ in 0..path.len() { + tmp.push_str(&indent); + } + tmp.push_str(&escape_key_toml(field_name.value())); + tmp.push_str(" = "); + self.state_stack.push(State::AppendToString(tmp)); + } + + if has_header && !visible_fields.is_empty() { + self.state_stack.push(State::AppendToString('\n'.into())); + } + } + + pub(super) fn do_manifest_toml_value( + &mut self, + indent: Rc, + depth: usize, + single_line: bool, + ) -> Result<(), Box> { + let value = self.value_stack.pop().unwrap(); + let result = self.string_stack.last_mut().unwrap(); + match value { + ValueData::Null => { + return Err(self.report_error(EvalErrorKind::Other { + span: None, + message: "cannot manifest null in TOML".into(), + })); + } + ValueData::Bool(b) => { + result.push_str(if b { "true" } else { "false" }); + } + ValueData::Number(n) => { + write!(result, "{n}").unwrap(); + } + ValueData::String(s) => { + escape_string_toml(&s, result); + } + ValueData::Array(array) => { + let array = array.view(); + if array.is_empty() { + result.push_str("[]"); + } else { + if single_line { + result.push_str("[ "); + self.state_stack.push(State::AppendToString(" ]".into())); + } else { + result.push_str("[\n"); + + let mut tmp = String::new(); + tmp.push('\n'); + for _ in 0..depth { + tmp.push_str(&indent); + } + tmp.push(']'); + self.state_stack.push(State::AppendToString(tmp)); + } + + for (i, item) in array.iter().enumerate().rev() { + if i != array.len() - 1 { + let mut tmp = String::new(); + if single_line { + tmp.push_str(", "); + } else { + tmp.push_str(",\n"); + } + self.state_stack.push(State::AppendToString(tmp)); + } + + self.push_trace_item(TraceItem::ManifestArrayItem { index: i }); + self.state_stack.push(State::ManifestTomlValue { + indent: indent.clone(), + depth: depth + 1, + single_line: true, + }); + self.state_stack.push(State::DoThunk(item.view())); + self.delay_trace_item(); + if !single_line && !indent.is_empty() { + self.state_stack + .push(State::AppendToString(indent.repeat(depth + 1))); + } + } + } + } + ValueData::Object(object) => { + let object = object.view(); + let visible_fields: Vec<_> = object + .get_fields_order() + .iter() + .filter_map(|(name, visible)| visible.then_some(name)) + .collect(); + if visible_fields.is_empty() { + result.push_str("{ }"); + } else { + result.push_str("{ "); + self.state_stack.push(State::AppendToString(" }".into())); + + for (i, &field_name) in visible_fields.iter().rev().enumerate() { + if i != 0 { + self.state_stack.push(State::AppendToString(", ".into())); + } + + self.push_trace_item(TraceItem::ManifestObjectField { + name: field_name.clone(), + }); + self.state_stack.push(State::ManifestTomlValue { + indent: indent.clone(), + depth: depth + 1, + single_line: true, + }); + let field_thunk = self + .program + .find_object_field_thunk(&object, 0, field_name) + .unwrap(); + self.state_stack.push(State::DoThunk(field_thunk)); + self.delay_trace_item(); + self.state_stack.push(State::AppendToString(" = ".into())); + self.state_stack + .push(State::AppendToString(escape_key_toml(field_name.value()))); + } + } + self.check_object_asserts(&object); + } + ValueData::Function(_) => { + return Err(self.report_error(EvalErrorKind::ManifestFunction)); + } + } + + Ok(()) + } } fn is_safe_yaml_plain(s: &str) -> bool { @@ -572,6 +915,22 @@ fn is_safe_yaml_plain(s: &str) -> bool { true } +fn is_safe_toml_plain(s: &str) -> bool { + !s.is_empty() + && s.bytes() + .all(|b| b.is_ascii_alphanumeric() || b == b'_' || b == b'-') +} + +fn escape_key_toml(s: &str) -> String { + if is_safe_toml_plain(s) { + s.into() + } else { + let mut escaped = String::new(); + escape_string_toml(s, &mut escaped); + escaped + } +} + pub(super) fn escape_string_json(s: &str, result: &mut String) { result.push('"'); for chr in s.chars() { @@ -595,3 +954,7 @@ pub(super) fn escape_string_json(s: &str, result: &mut String) { fn escape_string_python(s: &str, result: &mut String) { escape_string_json(s, result); } + +fn escape_string_toml(s: &str, result: &mut String) { + escape_string_json(s, result); +} diff --git a/rsjsonnet-lang/src/program/eval/mod.rs b/rsjsonnet-lang/src/program/eval/mod.rs index 1726915..1e536e1 100644 --- a/rsjsonnet-lang/src/program/eval/mod.rs +++ b/rsjsonnet-lang/src/program/eval/mod.rs @@ -458,6 +458,21 @@ impl<'a> Evaluator<'a> { parent_is_array, parent_is_object, )?, + State::ManifestTomlPeekSubTable => self.do_manifest_toml_peek_sub_table(), + State::ManifestTomlPeekSubTableArrayItem { array, index } => { + self.do_manifest_toml_peek_sub_table_array_item(array, index) + } + State::ManifestTomlTable { + object, + has_header, + path, + indent, + } => self.do_manifest_toml_table(object, has_header, path, indent), + State::ManifestTomlValue { + indent, + depth, + single_line, + } => self.do_manifest_toml_value(indent, depth, single_line)?, State::Expr { expr, env } => self.do_expr(expr, env)?, State::Error { span } => { let msg = self.string_stack.pop().unwrap(); @@ -1404,6 +1419,7 @@ impl<'a> Evaluator<'a> { State::StdManifestJsonEx => self.do_std_manifest_json_ex()?, State::StdManifestYamlDoc => self.do_std_manifest_yaml_doc()?, State::StdManifestYamlStream => self.do_std_manifest_yaml_stream()?, + State::StdManifestTomlEx => self.do_std_manifest_toml_ex()?, State::StdMakeArray => self.do_std_make_array()?, State::StdCount { value } => self.do_std_count(value)?, State::StdCountInner { array } => self.do_std_count_inner(array), diff --git a/rsjsonnet-lang/src/program/eval/state.rs b/rsjsonnet-lang/src/program/eval/state.rs index ba598e4..1c38b4f 100644 --- a/rsjsonnet-lang/src/program/eval/state.rs +++ b/rsjsonnet-lang/src/program/eval/state.rs @@ -52,6 +52,22 @@ pub(super) enum State { parent_is_array: bool, parent_is_object: bool, }, + ManifestTomlPeekSubTable, + ManifestTomlPeekSubTableArrayItem { + array: GcView, + index: usize, + }, + ManifestTomlTable { + object: GcView, + has_header: bool, + path: Rc<[InternedStr]>, + indent: Rc, + }, + ManifestTomlValue { + indent: Rc, + depth: usize, + single_line: bool, + }, Expr { expr: ir::RcExpr, env: GcView, @@ -301,6 +317,7 @@ pub(super) enum State { StdManifestJsonEx, StdManifestYamlDoc, StdManifestYamlStream, + StdManifestTomlEx, StdMakeArray, StdCount { value: GcView, diff --git a/rsjsonnet-lang/src/program/eval/stdlib.rs b/rsjsonnet-lang/src/program/eval/stdlib.rs index 97d58bd..939a84c 100644 --- a/rsjsonnet-lang/src/program/eval/stdlib.rs +++ b/rsjsonnet-lang/src/program/eval/stdlib.rs @@ -1097,6 +1097,21 @@ impl Evaluator<'_> { Ok(()) } + pub(super) fn do_std_manifest_toml_ex(&mut self) -> Result<(), Box> { + let indent = self.value_stack.pop().unwrap(); + let value = self.value_stack.pop().unwrap(); + + let object = self.expect_std_func_arg_object(value, "manifestTomlEx", 0)?; + let indent = self.expect_std_func_arg_string(indent, "manifestTomlEx", 1)?; + + self.string_stack.push(String::new()); + self.state_stack.push(State::StringToValue); + + self.prepare_manifest_toml_table(object, false, Rc::new([]), indent); + + Ok(()) + } + pub(super) fn do_std_make_array(&mut self) -> Result<(), Box> { let func_value = self.value_stack.pop().unwrap(); let sz_value = self.value_stack.pop().unwrap(); diff --git a/rsjsonnet-lang/src/program/std.jsonnet b/rsjsonnet-lang/src/program/std.jsonnet index 18f8446..053e2e0 100644 --- a/rsjsonnet-lang/src/program/std.jsonnet +++ b/rsjsonnet-lang/src/program/std.jsonnet @@ -156,86 +156,6 @@ limitations under the License. manifestToml(value):: std.manifestTomlEx(value, ' '), - manifestTomlEx(value, indent):: - local - escapeStringToml = std.escapeStringJson, - escapeKeyToml(key) = - local bare_allowed = std.set(std.stringChars('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-')); - if std.setUnion(std.set(std.stringChars(key)), bare_allowed) == bare_allowed then key else escapeStringToml(key), - isTableArray(v) = std.isArray(v) && std.length(v) > 0 && std.all(std.map(std.isObject, v)), - isSection(v) = std.isObject(v) || isTableArray(v), - renderValue(v, indexedPath, inline, cindent) = - if v == true then - 'true' - else if v == false then - 'false' - else if v == null then - error 'Tried to manifest "null" at ' + indexedPath - else if std.isNumber(v) then - '' + v - else if std.isString(v) then - escapeStringToml(v) - else if std.isFunction(v) then - error 'Tried to manifest function at ' + indexedPath - else if std.isArray(v) then - if std.length(v) == 0 then - '[]' - else - local range = std.range(0, std.length(v) - 1); - local new_indent = if inline then '' else cindent + indent; - local separator = if inline then ' ' else '\n'; - local lines = ['[' + separator] - + std.join([',' + separator], - [ - [new_indent + renderValue(v[i], indexedPath + [i], true, '')] - for i in range - ]) - + [separator + (if inline then '' else cindent) + ']']; - std.join('', lines) - else if std.isObject(v) then - local lines = ['{ '] - + std.join([', '], - [ - [escapeKeyToml(k) + ' = ' + renderValue(v[k], indexedPath + [k], true, '')] - for k in std.objectFields(v) - ]) - + [' }']; - std.join('', lines), - renderTableInternal(v, path, indexedPath, cindent) = - local kvp = std.flattenArrays([ - [cindent + escapeKeyToml(k) + ' = ' + renderValue(v[k], indexedPath + [k], false, cindent)] - for k in std.objectFields(v) - if !isSection(v[k]) - ]); - local sections = [std.join('\n', kvp)] + [ - ( - if std.isObject(v[k]) then - renderTable(v[k], path + [k], indexedPath + [k], cindent) - else - renderTableArray(v[k], path + [k], indexedPath + [k], cindent) - ) - for k in std.objectFields(v) - if isSection(v[k]) - ]; - std.join('\n\n', sections), - renderTable(v, path, indexedPath, cindent) = - cindent + '[' + std.join('.', std.map(escapeKeyToml, path)) + ']' - + (if v == {} then '' else '\n') - + renderTableInternal(v, path, indexedPath, cindent + indent), - renderTableArray(v, path, indexedPath, cindent) = - local range = std.range(0, std.length(v) - 1); - local sections = [ - (cindent + '[[' + std.join('.', std.map(escapeKeyToml, path)) + ']]' - + (if v[i] == {} then '' else '\n') - + renderTableInternal(v[i], path, indexedPath + [i], cindent + indent)) - for i in range - ]; - std.join('\n\n', sections); - if std.isObject(value) then - renderTableInternal(value, [], [], '') - else - error 'TOML body must be an object. Got ' + std.type(value), - escapeStringPython(str):: std.escapeStringJson(str), diff --git a/rsjsonnet-lang/src/program/stdlib.rs b/rsjsonnet-lang/src/program/stdlib.rs index 23078c9..e846d38 100644 --- a/rsjsonnet-lang/src/program/stdlib.rs +++ b/rsjsonnet-lang/src/program/stdlib.rs @@ -154,6 +154,11 @@ impl Program { add_simple("decodeUTF8", BuiltInFunc::DecodeUtf8, &["arr"]); add_simple("manifestIni", BuiltInFunc::ManifestIni, &["ini"]); add_simple("manifestPython", BuiltInFunc::ManifestPython, &["v"]); + add_simple( + "manifestTomlEx", + BuiltInFunc::ManifestTomlEx, + &["value", "indent"], + ); add_simple( "manifestPythonVars", BuiltInFunc::ManifestPythonVars, diff --git a/ui-tests/fail/object/assert_failed_with_msg_manifest.jsonnet.stderr b/ui-tests/fail/object/assert_failed_with_msg_manifest.jsonnet.stderr index 400915d..455343b 100644 --- a/ui-tests/fail/object/assert_failed_with_msg_manifest.jsonnet.stderr +++ b/ui-tests/fail/object/assert_failed_with_msg_manifest.jsonnet.stderr @@ -4,9 +4,9 @@ error: assertion failed: assert msg 1 | std.manifestJson({ assert false : "assert msg" }) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: while evaluating call to `manifestJsonEx` - --> :242:25 + --> :162:25 | -242 | manifestJson(value):: std.manifestJsonEx(value, ' '), +162 | manifestJson(value):: std.manifestJsonEx(value, ' '), | --------------------------------- note: while evaluating call to function --> assert_failed_with_msg_manifest.jsonnet:1:1 diff --git a/ui-tests/fail/stdlib/escapeStringPython/manifest_function.jsonnet.stderr b/ui-tests/fail/stdlib/escapeStringPython/manifest_function.jsonnet.stderr index 3f3feb4..394d420 100644 --- a/ui-tests/fail/stdlib/escapeStringPython/manifest_function.jsonnet.stderr +++ b/ui-tests/fail/stdlib/escapeStringPython/manifest_function.jsonnet.stderr @@ -1,8 +1,8 @@ error: functions cannot be manifested note: while evaluating call to `escapeStringJson` - --> :240:5 + --> :160:5 | -240 | std.escapeStringJson(str), +160 | std.escapeStringJson(str), | ------------------------- note: while evaluating call to function --> manifest_function.jsonnet:1:1 diff --git a/ui-tests/fail/stdlib/manifestToml/inner_error.jsonnet b/ui-tests/fail/stdlib/manifestToml/inner_error.jsonnet new file mode 100644 index 0000000..786a747 --- /dev/null +++ b/ui-tests/fail/stdlib/manifestToml/inner_error.jsonnet @@ -0,0 +1 @@ +std.manifestToml({ x: { y: [0, [{ a: 0, b: error "err" }]] } }) diff --git a/ui-tests/fail/stdlib/manifestToml/inner_error.jsonnet.stderr b/ui-tests/fail/stdlib/manifestToml/inner_error.jsonnet.stderr new file mode 100644 index 0000000..0edb5ae --- /dev/null +++ b/ui-tests/fail/stdlib/manifestToml/inner_error.jsonnet.stderr @@ -0,0 +1,22 @@ +error: explicit error: err + --> inner_error.jsonnet:1:44 + | +1 | std.manifestToml({ x: { y: [0, [{ a: 0, b: error "err" }]] } }) + | ^^^^^^^^^^^ +note: while manifesting object field "b" +note: while manifesting array item 0 +note: while manifesting array item 1 +note: while manifesting object field "y" +note: while manifesting object field "x" +note: while evaluating call to `manifestTomlEx` + --> :157:25 + | +157 | manifestToml(value):: std.manifestTomlEx(value, ' '), + | ------------------------------- +note: while evaluating call to function + --> inner_error.jsonnet:1:1 + | +1 | std.manifestToml({ x: { y: [0, [{ a: 0, b: error "err" }]] } }) + | --------------------------------------------------------------- +note: during top-level value evaluation + diff --git a/ui-tests/fail/stdlib/manifestToml/invalid_arg.jsonnet.stderr b/ui-tests/fail/stdlib/manifestToml/invalid_arg.jsonnet.stderr index be0a440..44be91d 100644 --- a/ui-tests/fail/stdlib/manifestToml/invalid_arg.jsonnet.stderr +++ b/ui-tests/fail/stdlib/manifestToml/invalid_arg.jsonnet.stderr @@ -1,9 +1,5 @@ -error: explicit error: TOML body must be an object. Got null - --> :237:7 - | -237 | error 'TOML body must be an object. Got ' + std.type(value), - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -note: while evaluating call to function +error: first argument of `std.manifestTomlEx` is expected to be object, got null +note: while evaluating call to `manifestTomlEx` --> :157:25 | 157 | manifestToml(value):: std.manifestTomlEx(value, ' '), diff --git a/ui-tests/fail/stdlib/manifestToml/manifest_function.jsonnet b/ui-tests/fail/stdlib/manifestToml/manifest_function.jsonnet new file mode 100644 index 0000000..ea3f9ea --- /dev/null +++ b/ui-tests/fail/stdlib/manifestToml/manifest_function.jsonnet @@ -0,0 +1 @@ +std.manifestToml({ x: function() null }) diff --git a/ui-tests/fail/stdlib/manifestToml/manifest_function.jsonnet.stderr b/ui-tests/fail/stdlib/manifestToml/manifest_function.jsonnet.stderr new file mode 100644 index 0000000..d8604af --- /dev/null +++ b/ui-tests/fail/stdlib/manifestToml/manifest_function.jsonnet.stderr @@ -0,0 +1,14 @@ +error: functions cannot be manifested +note: while manifesting object field "x" +note: while evaluating call to `manifestTomlEx` + --> :157:25 + | +157 | manifestToml(value):: std.manifestTomlEx(value, ' '), + | ------------------------------- +note: while evaluating call to function + --> manifest_function.jsonnet:1:1 + | +1 | std.manifestToml({ x: function() null }) + | ---------------------------------------- +note: during top-level value evaluation + diff --git a/ui-tests/fail/stdlib/manifestToml/manifest_null.jsonnet.stderr b/ui-tests/fail/stdlib/manifestToml/manifest_null.jsonnet.stderr index 0da41f6..35ab240 100644 --- a/ui-tests/fail/stdlib/manifestToml/manifest_null.jsonnet.stderr +++ b/ui-tests/fail/stdlib/manifestToml/manifest_null.jsonnet.stderr @@ -1,29 +1,6 @@ -error: explicit error: Tried to manifest "null" at ["x"] - --> :173:11 - | -173 | error 'Tried to manifest "null" at ' + indexedPath - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -note: while evaluating call to `renderValue` - --> :206:49 - | -206 | [cindent + escapeKeyToml(k) + ' = ' + renderValue(v[k], indexedPath + [k], false, cindent)] - | ---------------------------------------------------- -note: while evaluating call to `join` - --> :210:27 - | -210 | local sections = [std.join('\n', kvp)] + [ - | ------------------- -note: while evaluating call to `join` - --> :220:9 - | -220 | std.join('\n\n', sections), - | -------------------------- -note: while evaluating call to `renderTableInternal` - --> :235:7 - | -235 | renderTableInternal(value, [], [], '') - | -------------------------------------- -note: while evaluating call to function +error: cannot manifest null in TOML +note: while manifesting object field "x" +note: while evaluating call to `manifestTomlEx` --> :157:25 | 157 | manifestToml(value):: std.manifestTomlEx(value, ' '), diff --git a/ui-tests/fail/stdlib/manifestToml/object_assert_failed_root.jsonnet b/ui-tests/fail/stdlib/manifestToml/object_assert_failed_root.jsonnet new file mode 100644 index 0000000..827540e --- /dev/null +++ b/ui-tests/fail/stdlib/manifestToml/object_assert_failed_root.jsonnet @@ -0,0 +1 @@ +std.manifestToml({ assert false }) diff --git a/ui-tests/fail/stdlib/manifestToml/object_assert_failed_root.jsonnet.stderr b/ui-tests/fail/stdlib/manifestToml/object_assert_failed_root.jsonnet.stderr new file mode 100644 index 0000000..1ec2ca0 --- /dev/null +++ b/ui-tests/fail/stdlib/manifestToml/object_assert_failed_root.jsonnet.stderr @@ -0,0 +1,17 @@ +error: assertion failed + --> object_assert_failed_root.jsonnet:1:20 + | +1 | std.manifestToml({ assert false }) + | ^^^^^^^^^^^^ +note: while evaluating call to `manifestTomlEx` + --> :157:25 + | +157 | manifestToml(value):: std.manifestTomlEx(value, ' '), + | ------------------------------- +note: while evaluating call to function + --> object_assert_failed_root.jsonnet:1:1 + | +1 | std.manifestToml({ assert false }) + | ---------------------------------- +note: during top-level value evaluation + diff --git a/ui-tests/fail/stdlib/manifestToml/object_assert_failed_sub_table.jsonnet b/ui-tests/fail/stdlib/manifestToml/object_assert_failed_sub_table.jsonnet new file mode 100644 index 0000000..f764031 --- /dev/null +++ b/ui-tests/fail/stdlib/manifestToml/object_assert_failed_sub_table.jsonnet @@ -0,0 +1 @@ +std.manifestToml({ x: { assert false } }) diff --git a/ui-tests/fail/stdlib/manifestToml/object_assert_failed_sub_table.jsonnet.stderr b/ui-tests/fail/stdlib/manifestToml/object_assert_failed_sub_table.jsonnet.stderr new file mode 100644 index 0000000..b84aa82 --- /dev/null +++ b/ui-tests/fail/stdlib/manifestToml/object_assert_failed_sub_table.jsonnet.stderr @@ -0,0 +1,18 @@ +error: assertion failed + --> object_assert_failed_sub_table.jsonnet:1:25 + | +1 | std.manifestToml({ x: { assert false } }) + | ^^^^^^^^^^^^ +note: while manifesting object field "x" +note: while evaluating call to `manifestTomlEx` + --> :157:25 + | +157 | manifestToml(value):: std.manifestTomlEx(value, ' '), + | ------------------------------- +note: while evaluating call to function + --> object_assert_failed_sub_table.jsonnet:1:1 + | +1 | std.manifestToml({ x: { assert false } }) + | ----------------------------------------- +note: during top-level value evaluation + diff --git a/ui-tests/fail/stdlib/manifestToml/object_assert_failed_value.jsonnet b/ui-tests/fail/stdlib/manifestToml/object_assert_failed_value.jsonnet new file mode 100644 index 0000000..683b231 --- /dev/null +++ b/ui-tests/fail/stdlib/manifestToml/object_assert_failed_value.jsonnet @@ -0,0 +1 @@ +std.manifestToml({ x: [ { assert false } ] }) diff --git a/ui-tests/fail/stdlib/manifestToml/object_assert_failed_value.jsonnet.stderr b/ui-tests/fail/stdlib/manifestToml/object_assert_failed_value.jsonnet.stderr new file mode 100644 index 0000000..db8599d --- /dev/null +++ b/ui-tests/fail/stdlib/manifestToml/object_assert_failed_value.jsonnet.stderr @@ -0,0 +1,19 @@ +error: assertion failed + --> object_assert_failed_value.jsonnet:1:27 + | +1 | std.manifestToml({ x: [ { assert false } ] }) + | ^^^^^^^^^^^^ +note: while manifesting array item 0 +note: while manifesting object field "x" +note: while evaluating call to `manifestTomlEx` + --> :157:25 + | +157 | manifestToml(value):: std.manifestTomlEx(value, ' '), + | ------------------------------- +note: while evaluating call to function + --> object_assert_failed_value.jsonnet:1:1 + | +1 | std.manifestToml({ x: [ { assert false } ] }) + | --------------------------------------------- +note: during top-level value evaluation + diff --git a/ui-tests/fail/stdlib/manifestTomlEx/invalid_arg.jsonnet b/ui-tests/fail/stdlib/manifestTomlEx/invalid_arg.jsonnet deleted file mode 100644 index cc18647..0000000 --- a/ui-tests/fail/stdlib/manifestTomlEx/invalid_arg.jsonnet +++ /dev/null @@ -1 +0,0 @@ -std.manifestToml(null) diff --git a/ui-tests/fail/stdlib/manifestTomlEx/invalid_arg.jsonnet.stderr b/ui-tests/fail/stdlib/manifestTomlEx/invalid_arg.jsonnet.stderr deleted file mode 100644 index be0a440..0000000 --- a/ui-tests/fail/stdlib/manifestTomlEx/invalid_arg.jsonnet.stderr +++ /dev/null @@ -1,17 +0,0 @@ -error: explicit error: TOML body must be an object. Got null - --> :237:7 - | -237 | error 'TOML body must be an object. Got ' + std.type(value), - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -note: while evaluating call to function - --> :157:25 - | -157 | manifestToml(value):: std.manifestTomlEx(value, ' '), - | ------------------------------- -note: while evaluating call to function - --> invalid_arg.jsonnet:1:1 - | -1 | std.manifestToml(null) - | ---------------------- -note: during top-level value evaluation - diff --git a/ui-tests/fail/stdlib/manifestTomlEx/invalid_arg_1.jsonnet b/ui-tests/fail/stdlib/manifestTomlEx/invalid_arg_1.jsonnet new file mode 100644 index 0000000..1f02612 --- /dev/null +++ b/ui-tests/fail/stdlib/manifestTomlEx/invalid_arg_1.jsonnet @@ -0,0 +1 @@ +std.manifestTomlEx(null, " ") diff --git a/ui-tests/fail/stdlib/manifestTomlEx/invalid_arg_1.jsonnet.stderr b/ui-tests/fail/stdlib/manifestTomlEx/invalid_arg_1.jsonnet.stderr new file mode 100644 index 0000000..90eecbd --- /dev/null +++ b/ui-tests/fail/stdlib/manifestTomlEx/invalid_arg_1.jsonnet.stderr @@ -0,0 +1,8 @@ +error: first argument of `std.manifestTomlEx` is expected to be object, got null +note: while evaluating call to `manifestTomlEx` + --> invalid_arg_1.jsonnet:1:1 + | +1 | std.manifestTomlEx(null, " ") + | ------------------------------ +note: during top-level value evaluation + diff --git a/ui-tests/fail/stdlib/manifestTomlEx/invalid_arg_2.jsonnet b/ui-tests/fail/stdlib/manifestTomlEx/invalid_arg_2.jsonnet new file mode 100644 index 0000000..99d0b31 --- /dev/null +++ b/ui-tests/fail/stdlib/manifestTomlEx/invalid_arg_2.jsonnet @@ -0,0 +1 @@ +std.manifestTomlEx({}, null) diff --git a/ui-tests/fail/stdlib/manifestTomlEx/invalid_arg_2.jsonnet.stderr b/ui-tests/fail/stdlib/manifestTomlEx/invalid_arg_2.jsonnet.stderr new file mode 100644 index 0000000..2d25a81 --- /dev/null +++ b/ui-tests/fail/stdlib/manifestTomlEx/invalid_arg_2.jsonnet.stderr @@ -0,0 +1,8 @@ +error: second argument of `std.manifestTomlEx` is expected to be string, got null +note: while evaluating call to `manifestTomlEx` + --> invalid_arg_2.jsonnet:1:1 + | +1 | std.manifestTomlEx({}, null) + | ---------------------------- +note: during top-level value evaluation + diff --git a/ui-tests/pass/stdlib/manifestToml.jsonnet b/ui-tests/pass/stdlib/manifestToml.jsonnet index f657b3e..8a0deeb 100644 --- a/ui-tests/pass/stdlib/manifestToml.jsonnet +++ b/ui-tests/pass/stdlib/manifestToml.jsonnet @@ -41,7 +41,7 @@ std.assertEqual( std.assertEqual( std.manifestToml({ - array: [1, 2, 3, { a: 4, b: 5, c: [6, 7] }], + array: [1, 2, 3, [4, 5], { a: 6, b: 7, c: [8, 9], d: {} }], object1: { field1: "a", field2: "b", @@ -50,7 +50,7 @@ std.assertEqual( value: 1, array: [ { a: 1, b: 2 }, - { a: 3, b: 4 }, + { object3: { a: 3, b: 4 } }, ] } }) + "\n", @@ -59,7 +59,8 @@ std.assertEqual( 1, 2, 3, - { a = 4, b = 5, c = [ 6, 7 ] } + [ 4, 5 ], + { a = 6, b = 7, c = [ 8, 9 ], d = { } } ] [object1] @@ -74,14 +75,17 @@ std.assertEqual( b = 2 [[object2.array]] - a = 3 - b = 4 + + + [object2.array.object3] + a = 3 + b = 4 |||, ) && std.assertEqual( std.manifestTomlEx({ - array: [1, 2, 3, { a: 4, b: 5, c: [6, 7] }], + array: [1, 2, 3, [4, 5], { a: 6, b: 7, c: [8, 9], d: {} }], object1: { field1: "a", field2: "b", @@ -90,7 +94,7 @@ std.assertEqual( value: 1, array: [ { a: 1, b: 2 }, - { a: 3, b: 4 }, + { object3: { a: 3, b: 4 } }, ] } }, " ") + "\n", @@ -99,7 +103,8 @@ std.assertEqual( 1, 2, 3, - { a = 4, b = 5, c = [ 6, 7 ] } + [ 4, 5 ], + { a = 6, b = 7, c = [ 8, 9 ], d = { } } ] [object1] @@ -114,13 +119,17 @@ std.assertEqual( b = 2 [[object2.array]] - a = 3 - b = 4 + + + [object2.array.object3] + a = 3 + b = 4 |||, ) && std.assertEqual( std.manifestToml({ + "": "empty key", "some\"key": "some\"string", "some\"object": { "some\"array": [ @@ -130,6 +139,7 @@ std.assertEqual( } }) + "\n", ||| + "" = "empty key" "some\"key" = "some\"string" ["some\"object"]