focus/crates/focus-json/src/writer.rs

93 lines
3.3 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//! JSON 序列化器——把 [`JsonValue`] 渲染回字符串。
//! JSON serializer — renders a [`JsonValue`] back to a String.
use crate::value::JsonValue;
use std::fmt::Write;
/// 将 [`JsonValue`] 序列化为紧凑的 JSON 字符串。
/// Serialize a [`JsonValue`] to a compact JSON string.
pub fn to_string(value: &JsonValue) -> String {
let mut out = String::new();
write_value(value, &mut out);
out
}
/// 把一个值写入 `out`(递归处理数组与对象)。
/// Write a value into `out` (recursing into arrays and objects).
fn write_value(value: &JsonValue, out: &mut String) {
match value {
JsonValue::Null => out.push_str("null"),
JsonValue::Bool(true) => out.push_str("true"),
JsonValue::Bool(false) => out.push_str("false"),
JsonValue::Num(n) => write_number(*n, out),
JsonValue::Str(s) => write_string(s, out),
JsonValue::Arr(items) => {
out.push('[');
for (i, item) in items.iter().enumerate() {
if i > 0 {
out.push(',');
}
write_value(item, out);
}
out.push(']');
}
JsonValue::Obj(entries) => {
out.push('{');
for (i, (key, val)) in entries.iter().enumerate() {
if i > 0 {
out.push(',');
}
write_string(key, out);
out.push(':');
write_value(val, out);
}
out.push('}');
}
}
}
/// 以 JSON 兼容形式渲染数字。整数打印时不带小数部分,符合常见预期
/// (例如 `3` 而非 `3.0`)。
/// Render a number in a JSON-compatible form. Integers print without a
/// fractional part to match common expectations (e.g. `3` not `3.0`).
fn write_number(n: f64, out: &mut String) {
if n.is_finite() {
// 整数走 i64 分支;浮点数走最短表示分支。
// Integers take the i64 branch; fractional numbers take the shortest-repr branch.
if n.fract() == 0.0 && n.abs() < 1e16 {
let _ = write!(out, "{}", n as i64);
} else {
// 通过 format! 得到 Ryu 风格的最短表示:可往返且无需引入依赖。
// Use Ryu-style shortest representation via format!: this gives
// round-trippable floats without pulling in a dependency.
let _ = write!(out, "{}", n);
}
} else {
// JSON 无法表示无穷或 NaN输出 null 以保持有效。
// JSON has no representation for infinity or NaN. Emit null to stay valid.
out.push_str("null");
}
}
/// 将字符串转义并加上引号后写入 `out`。
/// Escape and quote a string into `out`.
fn write_string(s: &str, out: &mut String) {
out.push('"');
for c in s.chars() {
match c {
'"' => out.push_str("\\\""),
'\\' => out.push_str("\\\\"),
'\n' => out.push_str("\\n"),
'\r' => out.push_str("\\r"),
'\t' => out.push_str("\\t"),
'\u{0008}' => out.push_str("\\b"),
'\u{000C}' => out.push_str("\\f"),
c if (c as u32) < 0x20 => {
let _ = write!(out, "\\u{:04x}", c as u32);
}
c => out.push(c),
}
}
out.push('"');
}