309 lines
8.9 KiB
Rust
309 lines
8.9 KiB
Rust
//! [`JsonValue`] DOM 及其辅助方法。
|
|
//! The [`JsonValue`] DOM and its helper methods.
|
|
//!
|
|
//! 对象使用 `Vec<(String, JsonValue)>` 保留插入顺序,而不是哈希表——
|
|
//! 这样序列化结果是确定性的,也与 provider 输出字段的顺序一致。
|
|
//! Objects preserve insertion order using a `Vec<(String, JsonValue)>` rather
|
|
//! than a hash map, which keeps serialization deterministic and matches the
|
|
//! order in which providers emit fields.
|
|
|
|
use crate::error::{JsonError, JsonResult};
|
|
use std::fmt;
|
|
|
|
/// [`JsonValue::entries`] 使用的私有双臂迭代器组合子。
|
|
/// Private two-arm iterator combinator used by [`JsonValue::entries`].
|
|
enum Either<L, R> {
|
|
Left(L),
|
|
Right(R),
|
|
}
|
|
|
|
impl<L, R, I> Iterator for Either<L, R>
|
|
where
|
|
L: Iterator<Item = I>,
|
|
R: Iterator<Item = I>,
|
|
{
|
|
type Item = I;
|
|
fn next(&mut self) -> Option<I> {
|
|
match self {
|
|
Either::Left(l) => l.next(),
|
|
Either::Right(r) => r.next(),
|
|
}
|
|
}
|
|
}
|
|
|
|
/// 一个已解析的 JSON 值。
|
|
/// A parsed JSON value.
|
|
#[derive(Debug, Clone, PartialEq, Default)]
|
|
pub enum JsonValue {
|
|
#[default]
|
|
Null,
|
|
Bool(bool),
|
|
Num(f64),
|
|
Str(String),
|
|
Arr(Vec<JsonValue>),
|
|
/// 按插入顺序排列的键值对。
|
|
/// Key-value pairs in insertion order.
|
|
Obj(Vec<(String, JsonValue)>),
|
|
}
|
|
|
|
impl JsonValue {
|
|
// ---- 构造函数 ----
|
|
// ---- constructors ----------------------------------------------------
|
|
|
|
/// 创建一个空对象。
|
|
/// Create an empty object.
|
|
pub fn obj() -> Self {
|
|
JsonValue::Obj(Vec::new())
|
|
}
|
|
|
|
/// 创建一个空数组。
|
|
/// Create an empty array.
|
|
pub fn arr() -> Self {
|
|
JsonValue::Arr(Vec::new())
|
|
}
|
|
|
|
// ---- 判定 ----
|
|
// ---- predicates -------------------------------------------------------
|
|
|
|
/// 是否为 null。
|
|
/// Whether this is the null value.
|
|
pub fn is_null(&self) -> bool {
|
|
matches!(self, JsonValue::Null)
|
|
}
|
|
|
|
/// 是否为对象。
|
|
/// Whether this is an object.
|
|
pub fn is_object(&self) -> bool {
|
|
matches!(self, JsonValue::Obj(_))
|
|
}
|
|
|
|
/// 是否为数组。
|
|
/// Whether this is an array.
|
|
pub fn is_array(&self) -> bool {
|
|
matches!(self, JsonValue::Arr(_))
|
|
}
|
|
|
|
// ---- 访问器 ----
|
|
// ---- accessors --------------------------------------------------------
|
|
|
|
/// 按键从对象中获取字段。非对象返回 `None`。
|
|
/// Get a field from an object by key. Returns `None` for non-objects.
|
|
pub fn get(&self, key: &str) -> Option<&JsonValue> {
|
|
match self {
|
|
JsonValue::Obj(entries) => entries.iter().find_map(|(k, v)| (k == key).then_some(v)),
|
|
_ => None,
|
|
}
|
|
}
|
|
|
|
/// 若字段为字符串,则以字符串切片借用。
|
|
/// Borrow a field as a string slice if it is a string.
|
|
pub fn get_str(&self, key: &str) -> Option<&str> {
|
|
match self.get(key)? {
|
|
JsonValue::Str(s) => Some(s.as_str()),
|
|
_ => None,
|
|
}
|
|
}
|
|
|
|
/// 以布尔值借用字段。
|
|
/// Borrow a field as a bool.
|
|
pub fn get_bool(&self, key: &str) -> Option<bool> {
|
|
match self.get(key)? {
|
|
JsonValue::Bool(b) => Some(*b),
|
|
_ => None,
|
|
}
|
|
}
|
|
|
|
/// 以数字借用字段。
|
|
/// Borrow a field as a number.
|
|
pub fn get_num(&self, key: &str) -> Option<f64> {
|
|
match self.get(key)? {
|
|
JsonValue::Num(n) => Some(*n),
|
|
_ => None,
|
|
}
|
|
}
|
|
|
|
/// 以数组借用字段。
|
|
/// Borrow a field as an array.
|
|
pub fn get_arr(&self, key: &str) -> Option<&Vec<JsonValue>> {
|
|
match self.get(key)? {
|
|
JsonValue::Arr(a) => Some(a),
|
|
_ => None,
|
|
}
|
|
}
|
|
|
|
/// 以对象借用字段。
|
|
/// Borrow a field as an object.
|
|
pub fn get_obj(&self, key: &str) -> Option<&Vec<(String, JsonValue)>> {
|
|
match self.get(key)? {
|
|
JsonValue::Obj(o) => Some(o),
|
|
_ => None,
|
|
}
|
|
}
|
|
|
|
/// 迭代对象的键值对;非对象返回空迭代器。
|
|
/// Iterate object entries. Empty for non-objects.
|
|
pub fn entries(&self) -> impl Iterator<Item = (&String, &JsonValue)> {
|
|
match self {
|
|
JsonValue::Obj(entries) => Either::Left(entries.iter().map(|(k, v)| (k, v))),
|
|
_ => Either::Right(std::iter::empty()),
|
|
}
|
|
}
|
|
|
|
// ---- 修改器 ----
|
|
// ---- mutators ---------------------------------------------------------
|
|
|
|
/// 向对象插入键值对;键已存在则替换。`self` 非对象时返回错误。
|
|
/// Insert a key-value pair into an object. Replaces if key exists.
|
|
/// Returns an error if `self` is not an object.
|
|
pub fn insert(&mut self, key: impl Into<String>, value: JsonValue) -> JsonResult<()> {
|
|
match self {
|
|
JsonValue::Obj(entries) => {
|
|
let key = key.into();
|
|
if let Some(slot) = entries.iter_mut().find(|(k, _)| *k == key) {
|
|
slot.1 = value;
|
|
} else {
|
|
entries.push((key, value));
|
|
}
|
|
Ok(())
|
|
}
|
|
_ => Err(JsonError::new("cannot insert into non-object JsonValue")),
|
|
}
|
|
}
|
|
|
|
/// 向数组追加一个值。`self` 非数组时返回错误。
|
|
/// Push a value into an array. Returns an error if `self` is not an array.
|
|
pub fn push(&mut self, value: JsonValue) -> JsonResult<()> {
|
|
match self {
|
|
JsonValue::Arr(items) => {
|
|
items.push(value);
|
|
Ok(())
|
|
}
|
|
_ => Err(JsonError::new("cannot push into non-array JsonValue")),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl fmt::Display for JsonValue {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
f.write_str(&crate::to_string(self))
|
|
}
|
|
}
|
|
|
|
// ---- 从基本类型的转换 ----
|
|
// ---- conversions from primitives ----------------------------------------
|
|
|
|
/// 从布尔值构造 JSON 值。
|
|
/// Construct a JSON value from a bool.
|
|
impl From<bool> for JsonValue {
|
|
fn from(b: bool) -> Self {
|
|
JsonValue::Bool(b)
|
|
}
|
|
}
|
|
|
|
/// 从字符串切片构造 JSON 值。
|
|
/// Construct a JSON value from a string slice.
|
|
impl From<&str> for JsonValue {
|
|
fn from(s: &str) -> Self {
|
|
JsonValue::Str(s.to_string())
|
|
}
|
|
}
|
|
|
|
/// 从字符串构造 JSON 值。
|
|
/// Construct a JSON value from a String.
|
|
impl From<String> for JsonValue {
|
|
fn from(s: String) -> Self {
|
|
JsonValue::Str(s)
|
|
}
|
|
}
|
|
|
|
/// 从 f64 构造 JSON 值。
|
|
/// Construct a JSON value from an f64.
|
|
impl From<f64> for JsonValue {
|
|
fn from(n: f64) -> Self {
|
|
JsonValue::Num(n)
|
|
}
|
|
}
|
|
|
|
/// 从 i64 构造 JSON 值(转为 f64 存储)。
|
|
/// Construct a JSON value from an i64 (stored as f64).
|
|
impl From<i64> for JsonValue {
|
|
fn from(n: i64) -> Self {
|
|
JsonValue::Num(n as f64)
|
|
}
|
|
}
|
|
|
|
/// 从 u64 构造 JSON 值(转为 f64 存储)。
|
|
/// Construct a JSON value from a u64 (stored as f64).
|
|
impl From<u64> for JsonValue {
|
|
fn from(n: u64) -> Self {
|
|
JsonValue::Num(n as f64)
|
|
}
|
|
}
|
|
|
|
/// 从 usize 构造 JSON 值(转为 f64 存储)。
|
|
/// Construct a JSON value from a usize (stored as f64).
|
|
impl From<usize> for JsonValue {
|
|
fn from(n: usize) -> Self {
|
|
JsonValue::Num(n as f64)
|
|
}
|
|
}
|
|
|
|
/// 从向量构造 JSON 数组(逐元素转换)。
|
|
/// Construct a JSON array from a vector (element-wise conversion).
|
|
impl<T: Into<JsonValue>> From<Vec<T>> for JsonValue {
|
|
fn from(v: Vec<T>) -> Self {
|
|
JsonValue::Arr(v.into_iter().map(Into::into).collect())
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn object_preserves_insertion_order() {
|
|
let mut o = JsonValue::obj();
|
|
o.insert("b", JsonValue::Num(2.0)).unwrap();
|
|
o.insert("a", JsonValue::Num(1.0)).unwrap();
|
|
o.insert("c", JsonValue::Num(3.0)).unwrap();
|
|
let keys: Vec<_> = o.entries().map(|(k, _)| k.as_str()).collect();
|
|
assert_eq!(keys, vec!["b", "a", "c"]);
|
|
}
|
|
|
|
#[test]
|
|
fn insert_replaces_existing_key() {
|
|
let mut o = JsonValue::obj();
|
|
o.insert("x", JsonValue::Num(1.0)).unwrap();
|
|
o.insert("x", JsonValue::Num(2.0)).unwrap();
|
|
assert_eq!(o.get_num("x"), Some(2.0));
|
|
assert_eq!(o.entries().count(), 1);
|
|
}
|
|
|
|
#[test]
|
|
fn accessors_return_none_for_wrong_types() {
|
|
let s = JsonValue::Str("hi".into());
|
|
assert_eq!(s.get("x"), None);
|
|
assert_eq!(s.get_str("x"), None);
|
|
|
|
let mut o = JsonValue::obj();
|
|
o.insert("n", JsonValue::Num(1.0)).unwrap();
|
|
assert_eq!(o.get_str("n"), None);
|
|
assert_eq!(o.get_num("n"), Some(1.0));
|
|
assert_eq!(o.get_str("missing"), None);
|
|
}
|
|
|
|
#[test]
|
|
fn insert_into_array_errors() {
|
|
let mut a = JsonValue::arr();
|
|
let res = a.insert("x", JsonValue::Null);
|
|
assert!(res.is_err());
|
|
}
|
|
|
|
#[test]
|
|
fn push_into_object_errors() {
|
|
let mut o = JsonValue::obj();
|
|
let res = o.push(JsonValue::Null);
|
|
assert!(res.is_err());
|
|
}
|
|
}
|