From c886578b6b488e11410296a09bfb2acbbd339f4d Mon Sep 17 00:00:00 2001 From: VikingMew Date: Fri, 4 Sep 2026 14:07:02 +0000 Subject: [PATCH] fix: match json.dumps default separators --- src/builtins/json.rs | 76 +++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 72 insertions(+), 4 deletions(-) diff --git a/src/builtins/json.rs b/src/builtins/json.rs index 22dfb4b..ce50cd3 100644 --- a/src/builtins/json.rs +++ b/src/builtins/json.rs @@ -38,14 +38,51 @@ fn json_dumps(args: Vec) -> Result { } let json_value = value_to_json(&args[0])?; - let json_str = serde_json::to_string(&json_value).map_err(|e| { + let mut json_str = String::new(); + write_json_default(&json_value, &mut json_str)?; + + Ok(Value::String(json_str)) +} + +fn write_json_default(json: &serde_json::Value, output: &mut String) -> Result<(), Value> { + match json { + serde_json::Value::Array(items) => { + output.push('['); + for (index, item) in items.iter().enumerate() { + if index > 0 { + output.push_str(", "); + } + write_json_default(item, output)?; + } + output.push(']'); + } + serde_json::Value::Object(object) => { + output.push('{'); + for (index, (key, value)) in object.iter().enumerate() { + if index > 0 { + output.push_str(", "); + } + write_json_scalar(&serde_json::Value::String(key.clone()), output)?; + output.push_str(": "); + write_json_default(value, output)?; + } + output.push('}'); + } + scalar => write_json_scalar(scalar, output)?, + } + + Ok(()) +} + +fn write_json_scalar(json: &serde_json::Value, output: &mut String) -> Result<(), Value> { + let encoded = serde_json::to_string(json).map_err(|error| { Value::error( ExceptionType::RuntimeError, - format!("Failed to serialize: {}", e), + format!("Failed to serialize: {}", error), ) })?; - - Ok(Value::String(json_str)) + output.push_str(&encoded); + Ok(()) } fn json_to_value(json: &serde_json::Value) -> Result { @@ -118,3 +155,34 @@ fn value_to_json(value: &Value) -> Result { )), } } + +#[cfg(test)] +mod tests { + use super::*; + + fn dumps_json(source: &str) -> String { + let json: serde_json::Value = serde_json::from_str(source).unwrap(); + let value = json_to_value(&json).unwrap(); + json_dumps(vec![value]) + .unwrap() + .as_string() + .unwrap() + .to_owned() + } + + #[test] + fn dumps_uses_python_default_separators() { + assert_eq!(dumps_json(r#"{"a":1}"#), r#"{"a": 1}"#); + assert_eq!(dumps_json("[1,2,3]"), "[1, 2, 3]"); + assert_eq!( + dumps_json(r#"{"a":[1,2,{"b":3}]}"#), + r#"{"a": [1, 2, {"b": 3}]}"# + ); + } + + #[test] + fn medium_response_dump_has_cpython_length() { + let source = include_str!("../../benchmarks/talk_comparison/fixtures/medium_response.json"); + assert_eq!(dumps_json(source).len(), 1815); + } +}