wallfacer_core/property/
jsonpath.rs1use serde_json::Value;
8use thiserror::Error;
9
10#[derive(Debug, Error)]
12pub enum JsonPathError {
13 #[error("invalid JSONPath `{path}`: {source}")]
15 Parse {
16 path: String,
17 #[source]
18 source: serde_json_path::ParseError,
19 },
20 #[error("path `{0}` did not resolve to any value")]
22 Missing(String),
23}
24
25pub type Result<T> = std::result::Result<T, JsonPathError>;
26
27pub fn resolve(root: &Value, path: &str) -> Result<Vec<Value>> {
30 let parsed = serde_json_path::JsonPath::parse(path).map_err(|source| JsonPathError::Parse {
31 path: path.to_string(),
32 source,
33 })?;
34 Ok(parsed
35 .query(root)
36 .all()
37 .into_iter()
38 .cloned()
39 .collect::<Vec<_>>())
40}
41
42pub fn resolve_one(root: &Value, path: &str) -> Result<Value> {
45 let nodes = resolve(root, path)?;
46 nodes
47 .into_iter()
48 .next()
49 .ok_or_else(|| JsonPathError::Missing(path.to_string()))
50}
51
52#[cfg(test)]
53#[allow(clippy::expect_used, clippy::unwrap_used, clippy::panic)]
54mod tests {
55 use super::*;
56 use serde_json::json;
57
58 #[test]
59 fn resolves_nested_field() {
60 let root = json!({"a": {"b": 42}});
61 assert_eq!(resolve_one(&root, "$.a.b").unwrap(), json!(42));
62 }
63
64 #[test]
65 fn resolves_array_index() {
66 let root = json!({"items": [10, 20, 30]});
67 assert_eq!(resolve_one(&root, "$.items[1]").unwrap(), json!(20));
68 }
69
70 #[test]
71 fn resolves_wildcard_in_middle() {
72 let root = json!({"items": [{"v": 1}, {"v": 2}, {"v": 3}]});
76 let values = resolve(&root, "$.items[*].v").unwrap();
77 assert_eq!(values, vec![json!(1), json!(2), json!(3)]);
78 }
79
80 #[test]
81 fn missing_returns_missing_error() {
82 let root = json!({"a": 1});
83 let err = resolve_one(&root, "$.nope").unwrap_err();
84 assert!(matches!(err, JsonPathError::Missing(_)));
85 }
86
87 #[test]
88 fn invalid_syntax_returns_parse_error() {
89 let root = json!({});
90 let err = resolve(&root, "not-a-path").unwrap_err();
91 assert!(matches!(err, JsonPathError::Parse { .. }));
92 }
93
94 #[test]
95 fn filter_expression_works() {
96 let root = json!({"items": [{"v": 1}, {"v": 2}, {"v": 3}]});
97 let values = resolve(&root, "$.items[?@.v > 1].v").unwrap();
98 assert_eq!(values, vec![json!(2), json!(3)]);
99 }
100}