Skip to main content

shard_den_json_extractor/
path.rs

1//! Path parsing and traversal
2
3use serde_json::Value;
4use shard_den_core::Result;
5
6/// A parsed JSON path
7#[derive(Debug, Clone)]
8pub enum JsonPath {
9    Key(String),
10    Index(usize),
11    Wildcard,
12    Recursive(String),
13}
14
15/// Path parser for JSONPath-like syntax
16#[derive(Debug, Default)]
17pub struct PathParser;
18
19impl PathParser {
20    /// Create a new parser
21    pub fn new() -> Self {
22        Self
23    }
24
25    /// Parse a path string into components
26    #[doc(hidden)]
27    #[deprecated(since = "0.3.0", note = "Not yet implemented - use extract() instead")]
28    pub fn parse(&self, path: &str) -> Result<Vec<JsonPath>> {
29        // TODO: Implement path parsing
30        let _ = path;
31        Ok(vec![])
32    }
33
34    /// Traverse a JSON value using the parsed path
35    #[doc(hidden)]
36    #[deprecated(since = "0.3.0", note = "Not yet implemented")]
37    pub fn traverse<'a>(&self, value: &'a Value, path: &[JsonPath]) -> Result<Vec<&'a Value>> {
38        // TODO: Implement traversal
39        let _ = path;
40        Ok(vec![value])
41    }
42
43    /// Auto-detect all possible paths in a JSON value
44    pub fn detect_paths(&self, value: &Value) -> Vec<String> {
45        let mut paths = Vec::new();
46        self.detect_paths_recursive(value, "", &mut paths);
47        paths
48    }
49
50    fn detect_paths_recursive(&self, value: &Value, prefix: &str, paths: &mut Vec<String>) {
51        match value {
52            Value::Object(map) => {
53                for (key, val) in map {
54                    let path = if prefix.is_empty() {
55                        format!("$.{}", key)
56                    } else {
57                        format!("{}.{}", prefix, key)
58                    };
59                    paths.push(path.clone());
60                    self.detect_paths_recursive(val, &path, paths);
61                }
62            }
63            Value::Array(arr) => {
64                if arr.is_empty() {
65                    return;
66                }
67                // For arrays, show only [*] wildcard instead of indices
68                let path = format!("{}[*]", prefix);
69                paths.push(path);
70
71                // Only recurse into the first element to avoid duplicates
72                if let Some(first) = arr.first() {
73                    self.detect_paths_recursive(first, &format!("{}[*]", prefix), paths);
74                }
75            }
76            _ => {}
77        }
78    }
79}
80
81#[cfg(test)]
82mod tests {
83    use super::*;
84    use serde_json::json;
85
86    #[test]
87    fn test_path_parser_new() {
88        let parser = PathParser::new();
89        let _ = parser;
90    }
91
92    #[test]
93    fn test_detect_paths() {
94        let parser = PathParser::new();
95        let value = json!({
96            "name": "test",
97            "data": {
98                "items": [1, 2, 3]
99            }
100        });
101
102        let paths = parser.detect_paths(&value);
103        assert!(paths.contains(&"$.name".to_string()));
104        assert!(paths.contains(&"$.data".to_string()));
105        assert!(paths.contains(&"$.data.items[*]".to_string()));
106    }
107
108    #[test]
109    fn test_detect_paths_empty() {
110        let parser = PathParser::new();
111        let value = json!("simple string");
112        let paths = parser.detect_paths(&value);
113        assert!(paths.is_empty());
114    }
115
116    #[test]
117    fn test_detect_paths_array() {
118        let parser = PathParser::new();
119        let value = json!([1, 2, 3]);
120        let paths = parser.detect_paths(&value);
121        assert!(!paths.is_empty());
122        // For array [1,2,3], prefix starts empty so path is "$[*]" from recursion
123        // Actually it will be just "[*]" since prefix is empty initially
124        // But when recursing into first element, it becomes "$[*]" - wait no
125        // Let's check actual output
126        let has_array_path = paths.iter().any(|p| p.contains("[*]"));
127        assert!(
128            has_array_path,
129            "Expected path containing [*], got: {:?}",
130            paths
131        );
132    }
133
134    #[test]
135    fn test_detect_paths_nested() {
136        let parser = PathParser::new();
137        let value = json!({"a": {"b": {"c": 1}}});
138        let paths = parser.detect_paths(&value);
139        assert!(paths.contains(&"$.a".to_string()));
140        assert!(paths.contains(&"$.a.b".to_string()));
141        assert!(paths.contains(&"$.a.b.c".to_string()));
142    }
143
144    #[test]
145    fn test_detect_paths_number() {
146        let parser = PathParser::new();
147        let value = json!(42);
148        let paths = parser.detect_paths(&value);
149        assert!(paths.is_empty());
150    }
151
152    #[test]
153    fn test_detect_paths_bool() {
154        let parser = PathParser::new();
155        let value = json!(true);
156        let paths = parser.detect_paths(&value);
157        assert!(paths.is_empty());
158    }
159
160    #[test]
161    fn test_detect_paths_null() {
162        let parser = PathParser::new();
163        let value = json!(null);
164        let paths = parser.detect_paths(&value);
165        assert!(paths.is_empty());
166    }
167
168    #[test]
169    #[allow(deprecated)]
170    fn test_parse_deprecated() {
171        let parser = PathParser::new();
172        let result = parser.parse("data.items[0]");
173        // Deprecated method returns empty vec
174        assert!(result.is_ok());
175        assert!(result.unwrap().is_empty());
176    }
177
178    #[test]
179    #[allow(deprecated)]
180    fn test_traverse_deprecated() {
181        let parser = PathParser::new();
182        let value = json!({"key": "value"});
183        let result = parser.traverse(&value, &[]);
184        // Deprecated method returns the value itself
185        assert!(result.is_ok());
186        assert_eq!(result.unwrap().len(), 1);
187    }
188
189    #[test]
190    fn test_detect_paths_empty_array() {
191        let parser = PathParser::new();
192        let value = json!([]);
193        let paths = parser.detect_paths(&value);
194        // Empty array should return empty paths
195        assert!(paths.is_empty());
196    }
197
198    #[test]
199    fn test_detect_paths_complex_nested() {
200        let parser = PathParser::new();
201        let value = json!({
202            "users": [
203                {"name": "Alice", "age": 30},
204                {"name": "Bob", "age": 25}
205            ]
206        });
207        let paths = parser.detect_paths(&value);
208        assert!(paths.contains(&"$.users[*]".to_string()));
209    }
210}