Skip to content

Commit ac6fc23

Browse files
authored
fix: json array literal stream output (#2032)
Signed-off-by: Peefy <xpf6677@163.com>
1 parent ecae1e4 commit ac6fc23

File tree

15 files changed

+186
-76
lines changed

15 files changed

+186
-76
lines changed

3rdparty/serde_yaml/tests/test_value.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ fn test_tagged() {
142142
Variant(usize),
143143
}
144144

145-
let value = serde_yaml::to_value(&Enum::Variant(0)).unwrap();
145+
let value = serde_yaml::to_value(Enum::Variant(0)).unwrap();
146146

147147
let deserialized: serde_yaml::Value = serde_yaml::from_value(value.clone()).unwrap();
148148
assert_eq!(value, deserialized);

crates/cmd/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ pub fn app() -> Command {
4444
.about("run")
4545
.arg(arg!([input] ... "Specify the input files to run").num_args(0..))
4646
.arg(arg!(output: -o --output <output> "Specify the YAML output file path"))
47+
.arg(arg!(format: -f --format <format> "Specify the output format (yaml or json)"))
4748
.arg(arg!(setting: -Y --setting <setting> ... "Specify the input setting file").num_args(1..))
4849
.arg(arg!(verbose: -v --verbose "Print test information verbosely").action(ArgAction::Count))
4950
.arg(arg!(disable_none: -n --disable_none "Disable dumping None values"))

crates/cmd/src/run.rs

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ pub fn run_command<W: Write>(matches: &ArgMatches, writer: &mut W) -> Result<()>
1515
// Config settings building
1616
let settings = must_build_settings(matches);
1717
let output = settings.output();
18+
let format_opt = matches.get_one::<String>("format").map(|s| s.as_str());
1819
let sess = Arc::new(ParseSession::default());
1920
match exec_program(sess.clone(), &settings.try_into()?) {
2021
Ok(result) => {
@@ -29,12 +30,23 @@ pub fn run_command<W: Write>(matches: &ArgMatches, writer: &mut W) -> Result<()>
2930
}
3031
sess.0.emit_stashed_diagnostics_and_abort()?;
3132
}
32-
if !result.yaml_result.is_empty() {
33+
// Select output based on format option
34+
let output_str = match format_opt {
35+
Some("json") => &result.json_result,
36+
Some("yaml") | None => &result.yaml_result,
37+
Some(f) => {
38+
return Err(anyhow::anyhow!(
39+
"Invalid format '{}', expected 'yaml' or 'json'",
40+
f
41+
));
42+
}
43+
};
44+
if !output_str.is_empty() {
3345
match output {
34-
Some(o) => std::fs::write(o, result.yaml_result)?,
46+
Some(o) => std::fs::write(o, output_str)?,
3547
// [`println!`] is not a good way to output content to stdout,
3648
// using [`writeln`] can be better to redirect the output.
37-
None => writeln!(writer, "{}", result.yaml_result)?,
49+
None => writeln!(writer, "{}", output_str)?,
3850
}
3951
}
4052
}

crates/evaluator/src/lib.rs

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -228,14 +228,25 @@ impl<'ctx> Evaluator<'ctx> {
228228

229229
pub fn plan_value(&self, value: &ValueRef) -> (String, String) {
230230
let mut ctx = self.runtime_ctx.borrow_mut();
231-
let value = match ctx.buffer.custom_manifests_output.clone() {
232-
Some(output) => ValueRef::from_yaml_stream(&mut ctx, &output).unwrap(),
233-
None => value.clone(),
234-
};
235-
let (json_string, yaml_string) = value.plan(&ctx);
236-
ctx.json_result = json_string.clone();
237-
ctx.yaml_result = yaml_string.clone();
238-
(json_string, yaml_string)
231+
// If custom_manifests_output is set (e.g., from yaml_stream), use it directly for YAML
232+
match ctx.buffer.custom_manifests_output.take() {
233+
Some(output) => {
234+
// Use the pre-formatted YAML stream directly
235+
let yaml_string = output.clone();
236+
// For JSON, parse the YAML stream and format as JSON stream
237+
let value = ValueRef::from_yaml_stream(&mut ctx, &yaml_string).unwrap();
238+
let (json_string, _) = value.plan(&ctx);
239+
ctx.json_result = json_string.clone();
240+
ctx.yaml_result = yaml_string.clone();
241+
(json_string, yaml_string)
242+
}
243+
None => {
244+
let (json_string, yaml_string) = value.plan(&ctx);
245+
ctx.json_result = json_string.clone();
246+
ctx.yaml_result = yaml_string.clone();
247+
(json_string, yaml_string)
248+
}
249+
}
239250
}
240251
}
241252

crates/evaluator/src/snapshots/kcl_evaluator__tests__expr_stmt_4.snap

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@
22
source: crates/evaluator/src/tests.rs
33
expression: "format! (\"{}\", evaluator.run().unwrap().1)"
44
---
5-
1
6-
---
7-
2
8-
---
9-
3
5+
- 1
6+
- 2
7+
- 3

crates/runner/src/tests.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -308,10 +308,10 @@ fn exec_with_result_at(path: &str) {
308308
#[cfg(target_os = "windows")]
309309
let newline = "\r\n";
310310

311-
let expected = std::fs::read_to_string(output_file)
312-
.unwrap()
311+
let expected_str = std::fs::read_to_string(output_file).unwrap();
312+
let expected = expected_str
313313
.strip_suffix(newline)
314-
.unwrap()
314+
.unwrap_or(&expected_str)
315315
.to_string();
316316

317317
#[cfg(target_os = "windows")]

crates/runtime/src/manifests/tests.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,28 +4,28 @@ use crate::*;
44
fn test_kcl_manifests_yaml_stream() {
55
let cases = [
66
(
7-
"a: 1\n",
7+
"a: 1",
88
ValueRef::list(Some(&[&ValueRef::dict(Some(&[("a", &ValueRef::int(1))]))])),
99
YamlEncodeOptions::default(),
1010
),
1111
(
12-
"a: 1\nb: 2\n",
12+
"a: 1\nb: 2",
1313
ValueRef::list(Some(&[&ValueRef::dict(Some(&[
1414
("a", &ValueRef::int(1)),
1515
("b", &ValueRef::int(2)),
1616
]))])),
1717
YamlEncodeOptions::default(),
1818
),
1919
(
20-
"a:\n- 1\n- 2\n- 3\nb: s\n",
20+
"a:\n- 1\n- 2\n- 3\nb: s",
2121
ValueRef::list(Some(&[&ValueRef::dict(Some(&[
2222
("a", &ValueRef::list_int(&[1, 2, 3])),
2323
("b", &ValueRef::str("s")),
2424
]))])),
2525
YamlEncodeOptions::default(),
2626
),
2727
(
28-
"a: 1\n",
28+
"a: 1",
2929
ValueRef::list(Some(&[&ValueRef::dict(Some(&[
3030
("a", &ValueRef::int(1)),
3131
("_b", &ValueRef::none()),
@@ -36,15 +36,15 @@ fn test_kcl_manifests_yaml_stream() {
3636
},
3737
),
3838
(
39-
"a: 1\nb: null\n",
39+
"a: 1\nb: null",
4040
ValueRef::list(Some(&[&ValueRef::dict(Some(&[
4141
("a", &ValueRef::int(1)),
4242
("b", &ValueRef::none()),
4343
]))])),
4444
YamlEncodeOptions::default(),
4545
),
4646
(
47-
"a: 1\n",
47+
"a: 1",
4848
ValueRef::list(Some(&[&ValueRef::dict(Some(&[
4949
("a", &ValueRef::int(1)),
5050
("_b", &ValueRef::int(2)),

crates/runtime/src/manifests/yaml.rs

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,10 @@ pub(crate) fn encode_yaml_stream_to_manifests(
1313
.as_list_ref()
1414
.values
1515
.iter()
16-
.map(|v| v.to_yaml_string_with_options(&opts))
16+
.map(|v| {
17+
let s = v.to_yaml_string_with_options(&opts);
18+
s.strip_suffix('\n').unwrap_or(&s).to_string()
19+
})
1720
.collect::<Vec<String>>()
1821
.join(&format!("\n{}\n", opts.sep)),
1922
);
@@ -32,28 +35,28 @@ mod test_manifests_yaml {
3235
fn test_encode_yaml_stream_to_manifests() {
3336
let cases = [
3437
(
35-
"a: 1\n",
38+
"a: 1",
3639
ValueRef::list(Some(&[&ValueRef::dict(Some(&[("a", &ValueRef::int(1))]))])),
3740
YamlEncodeOptions::default(),
3841
),
3942
(
40-
"a: 1\nb: 2\n",
43+
"a: 1\nb: 2",
4144
ValueRef::list(Some(&[&ValueRef::dict(Some(&[
4245
("a", &ValueRef::int(1)),
4346
("b", &ValueRef::int(2)),
4447
]))])),
4548
YamlEncodeOptions::default(),
4649
),
4750
(
48-
"a:\n- 1\n- 2\n- 3\nb: s\n",
51+
"a:\n- 1\n- 2\n- 3\nb: s",
4952
ValueRef::list(Some(&[&ValueRef::dict(Some(&[
5053
("a", &ValueRef::list_int(&[1, 2, 3])),
5154
("b", &ValueRef::str("s")),
5255
]))])),
5356
YamlEncodeOptions::default(),
5457
),
5558
(
56-
"a: 1\n",
59+
"a: 1",
5760
ValueRef::list(Some(&[&ValueRef::dict(Some(&[
5861
("a", &ValueRef::int(1)),
5962
("_b", &ValueRef::none()),
@@ -64,15 +67,15 @@ mod test_manifests_yaml {
6467
},
6568
),
6669
(
67-
"a: 1\nb: null\n",
70+
"a: 1\nb: null",
6871
ValueRef::list(Some(&[&ValueRef::dict(Some(&[
6972
("a", &ValueRef::int(1)),
7073
("b", &ValueRef::none()),
7174
]))])),
7275
YamlEncodeOptions::default(),
7376
),
7477
(
75-
"a: 1\n",
78+
"a: 1",
7679
ValueRef::list(Some(&[&ValueRef::dict(Some(&[
7780
("a", &ValueRef::int(1)),
7881
("_b", &ValueRef::int(2)),

crates/runtime/src/value/api.rs

Lines changed: 30 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -444,14 +444,21 @@ pub unsafe extern "C-unwind" fn kcl_value_plan_to_json(
444444
) -> *mut kcl_value_ref_t {
445445
let p = unsafe { ptr_as_ref(p) };
446446
let ctx: &mut Context = unsafe { mut_ptr_as_ref(ctx) };
447-
let value = match ctx.buffer.custom_manifests_output.clone() {
448-
Some(output) => ValueRef::from_yaml_stream(ctx, &output).unwrap(),
449-
None => p.clone(),
450-
};
451-
let (json_string, yaml_string) = value.plan(ctx);
452-
ctx.json_result = json_string.clone();
453-
ctx.yaml_result = yaml_string.clone();
454-
new_mut_ptr(ctx, ValueRef::str(&json_string))
447+
// If custom_manifests_output is set (e.g., from yaml_stream), use it directly for YAML
448+
// For JSON, parse the YAML stream and format as JSON stream
449+
if let Some(output) = ctx.buffer.custom_manifests_output.take() {
450+
ctx.yaml_result = output.clone();
451+
let yaml_result = ctx.yaml_result.clone();
452+
let value = ValueRef::from_yaml_stream(ctx, &yaml_result).unwrap();
453+
let (json_string, _) = value.plan(ctx);
454+
ctx.json_result = json_string.clone();
455+
new_mut_ptr(ctx, ValueRef::str(&ctx.json_result))
456+
} else {
457+
let (json_string, yaml_string) = p.plan(ctx);
458+
ctx.json_result = json_string.clone();
459+
ctx.yaml_result = yaml_string.clone();
460+
new_mut_ptr(ctx, ValueRef::str(&ctx.json_result))
461+
}
455462
}
456463

457464
#[unsafe(no_mangle)]
@@ -461,14 +468,21 @@ pub unsafe extern "C-unwind" fn kcl_value_plan_to_yaml(
461468
) -> *mut kcl_value_ref_t {
462469
let p = unsafe { ptr_as_ref(p) };
463470
let ctx = unsafe { mut_ptr_as_ref(ctx) };
464-
let value = match ctx.buffer.custom_manifests_output.clone() {
465-
Some(output) => ValueRef::from_yaml_stream(ctx, &output).unwrap(),
466-
None => p.clone(),
467-
};
468-
let (json_string, yaml_string) = value.plan(ctx);
469-
ctx.json_result = json_string.clone();
470-
ctx.yaml_result = yaml_string.clone();
471-
new_mut_ptr(ctx, ValueRef::str(&yaml_string))
471+
// If custom_manifests_output is set (e.g., from yaml_stream), use it directly
472+
if let Some(output) = ctx.buffer.custom_manifests_output.take() {
473+
ctx.yaml_result = output.clone();
474+
// For JSON, we still need to parse and format the YAML stream
475+
let yaml_str = ctx.yaml_result.clone();
476+
let value = ValueRef::from_yaml_stream(ctx, &yaml_str).unwrap();
477+
let (json_string, _) = value.plan(ctx);
478+
ctx.json_result = json_string;
479+
new_mut_ptr(ctx, ValueRef::str(&ctx.yaml_result))
480+
} else {
481+
let (json_string, yaml_string) = p.plan(ctx);
482+
ctx.json_result = json_string.clone();
483+
ctx.yaml_result = yaml_string.clone();
484+
new_mut_ptr(ctx, ValueRef::str(&yaml_string))
485+
}
472486
}
473487

474488
#[unsafe(no_mangle)]

0 commit comments

Comments
 (0)