Skip to main content

aster_a2ui/
validation.rs

1//! A2UI 验证工具
2//!
3//! 提供 JSON Pointer 路径解析和数据模型验证功能
4
5use serde_json::Value;
6
7/// JSON Pointer 路径解析错误
8#[derive(Debug, Clone, PartialEq)]
9pub enum JsonPointerError {
10    /// 路径格式无效
11    InvalidFormat(String),
12    /// 路径不存在
13    PathNotFound(String),
14    /// 数组索引无效
15    InvalidArrayIndex(String),
16}
17
18impl std::fmt::Display for JsonPointerError {
19    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
20        match self {
21            Self::InvalidFormat(msg) => write!(f, "无效的 JSON Pointer 格式: {}", msg),
22            Self::PathNotFound(path) => write!(f, "路径不存在: {}", path),
23            Self::InvalidArrayIndex(idx) => write!(f, "无效的数组索引: {}", idx),
24        }
25    }
26}
27
28impl std::error::Error for JsonPointerError {}
29
30/// 解析 JSON Pointer 路径并获取值
31///
32/// 支持绝对路径(以 `/` 开头)和相对路径
33pub fn resolve_pointer<'a>(data: &'a Value, pointer: &str) -> Result<&'a Value, JsonPointerError> {
34    if pointer.is_empty() || pointer == "/" {
35        return Ok(data);
36    }
37
38    // 确保路径以 / 开头
39    let normalized = if pointer.starts_with('/') {
40        pointer.to_string()
41    } else {
42        format!("/{}", pointer)
43    };
44
45    data.pointer(&normalized)
46        .ok_or_else(|| JsonPointerError::PathNotFound(pointer.to_string()))
47}
48
49/// 解析 JSON Pointer 路径并获取可变引用
50pub fn resolve_pointer_mut<'a>(
51    data: &'a mut Value,
52    pointer: &str,
53) -> Result<&'a mut Value, JsonPointerError> {
54    if pointer.is_empty() || pointer == "/" {
55        return Ok(data);
56    }
57
58    let normalized = if pointer.starts_with('/') {
59        pointer.to_string()
60    } else {
61        format!("/{}", pointer)
62    };
63
64    data.pointer_mut(&normalized)
65        .ok_or_else(|| JsonPointerError::PathNotFound(pointer.to_string()))
66}
67
68/// 在指定路径设置值,自动创建中间路径
69pub fn set_at_pointer(
70    data: &mut Value,
71    pointer: &str,
72    value: Value,
73) -> Result<(), JsonPointerError> {
74    if pointer.is_empty() || pointer == "/" {
75        *data = value;
76        return Ok(());
77    }
78
79    let normalized = if let Some(stripped) = pointer.strip_prefix('/') {
80        stripped
81    } else {
82        pointer
83    };
84
85    let parts: Vec<&str> = normalized.split('/').collect();
86    let mut current = data;
87
88    for (i, part) in parts.iter().enumerate() {
89        let is_last = i == parts.len() - 1;
90
91        if is_last {
92            // 最后一个部分,设置值
93            if let Ok(idx) = part.parse::<usize>() {
94                if let Value::Array(arr) = current {
95                    while arr.len() <= idx {
96                        arr.push(Value::Null);
97                    }
98                    arr[idx] = value;
99                    return Ok(());
100                }
101            }
102            if let Value::Object(obj) = current {
103                obj.insert(part.to_string(), value);
104                return Ok(());
105            }
106            return Err(JsonPointerError::InvalidFormat(
107                "父节点不是对象或数组".to_string(),
108            ));
109        }
110
111        // 中间部分,导航或创建
112        if let Ok(idx) = part.parse::<usize>() {
113            if let Value::Array(arr) = current {
114                while arr.len() <= idx {
115                    arr.push(Value::Null);
116                }
117                if arr[idx].is_null() {
118                    // 检查下一个部分是否是数字
119                    let next_is_array = parts
120                        .get(i + 1)
121                        .map(|p| p.parse::<usize>().is_ok())
122                        .unwrap_or(false);
123                    arr[idx] = if next_is_array {
124                        Value::Array(vec![])
125                    } else {
126                        Value::Object(serde_json::Map::new())
127                    };
128                }
129                current = &mut arr[idx];
130                continue;
131            }
132        }
133
134        if let Value::Object(obj) = current {
135            if !obj.contains_key(*part) {
136                let next_is_array = parts
137                    .get(i + 1)
138                    .map(|p| p.parse::<usize>().is_ok())
139                    .unwrap_or(false);
140                obj.insert(
141                    part.to_string(),
142                    if next_is_array {
143                        Value::Array(vec![])
144                    } else {
145                        Value::Object(serde_json::Map::new())
146                    },
147                );
148            }
149            current = obj.get_mut(*part).unwrap();
150            continue;
151        }
152
153        return Err(JsonPointerError::InvalidFormat(format!(
154            "无法在路径 {} 处导航",
155            part
156        )));
157    }
158
159    Ok(())
160}
161
162#[cfg(test)]
163mod tests {
164    use super::*;
165    use serde_json::json;
166
167    #[test]
168    fn test_resolve_pointer() {
169        let data = json!({
170            "user": {
171                "name": "张三",
172                "age": 25
173            },
174            "items": ["a", "b", "c"]
175        });
176
177        assert_eq!(resolve_pointer(&data, "/user/name").unwrap(), "张三");
178        assert_eq!(resolve_pointer(&data, "/user/age").unwrap(), 25);
179        assert_eq!(resolve_pointer(&data, "/items/0").unwrap(), "a");
180        assert!(resolve_pointer(&data, "/nonexistent").is_err());
181    }
182
183    #[test]
184    fn test_set_at_pointer() {
185        let mut data = json!({});
186
187        set_at_pointer(&mut data, "/user/name", json!("李四")).unwrap();
188        assert_eq!(data["user"]["name"], "李四");
189
190        set_at_pointer(&mut data, "/items/0", json!("first")).unwrap();
191        assert_eq!(data["items"][0], "first");
192    }
193}