ricecoder_external_lsp/mapping/
json_path.rs1use crate::error::{ExternalLspError, Result};
10use serde_json::Value;
11use std::str::FromStr;
12
13#[derive(Debug, Clone, PartialEq, Eq)]
15enum PathSegment {
16 Root,
18 Field(String),
20 Index(usize),
22 Wildcard,
24}
25
26#[derive(Debug, Clone)]
28pub struct JsonPathParser {
29 segments: Vec<PathSegment>,
30}
31
32impl JsonPathParser {
33 pub fn parse(expression: &str) -> Result<Self> {
41 let segments = Self::parse_expression(expression)?;
42 Ok(Self { segments })
43 }
44
45 fn parse_expression(expr: &str) -> Result<Vec<PathSegment>> {
47 let expr = expr.trim();
48
49 if !expr.starts_with('$') {
50 return Err(ExternalLspError::JsonPathError(
51 "JSON path must start with $".to_string(),
52 ));
53 }
54
55 let mut segments = vec![PathSegment::Root];
56 let mut remaining = &expr[1..];
57
58 while !remaining.is_empty() {
59 if remaining.starts_with('.') {
60 remaining = &remaining[1..];
62
63 let end = remaining
65 .find(['.', '['])
66 .unwrap_or(remaining.len());
67
68 if end == 0 {
69 return Err(ExternalLspError::JsonPathError(
70 "Empty field name in JSON path".to_string(),
71 ));
72 }
73
74 let field = remaining[..end].to_string();
75 segments.push(PathSegment::Field(field));
76 remaining = &remaining[end..];
77 } else if remaining.starts_with('[') {
78 let end = remaining.find(']').ok_or_else(|| {
80 ExternalLspError::JsonPathError("Unclosed bracket in JSON path".to_string())
81 })?;
82
83 let index_str = &remaining[1..end];
84
85 if index_str == "*" {
86 segments.push(PathSegment::Wildcard);
87 } else {
88 let index: usize = index_str.parse().map_err(|_| {
89 ExternalLspError::JsonPathError(format!(
90 "Invalid array index: {}",
91 index_str
92 ))
93 })?;
94 segments.push(PathSegment::Index(index));
95 }
96
97 remaining = &remaining[end + 1..];
98 } else {
99 return Err(ExternalLspError::JsonPathError(format!(
100 "Unexpected character in JSON path: {}",
101 remaining.chars().next().unwrap_or('?')
102 )));
103 }
104 }
105
106 Ok(segments)
107 }
108
109 pub fn extract(&self, value: &Value) -> Result<Vec<Value>> {
113 Self::extract_recursive(value, &self.segments, 0)
114 }
115
116 fn extract_recursive(value: &Value, segments: &[PathSegment], index: usize) -> Result<Vec<Value>> {
118 if index >= segments.len() {
119 return Ok(vec![value.clone()]);
120 }
121
122 match &segments[index] {
123 PathSegment::Root => {
124 Self::extract_recursive(value, segments, index + 1)
126 }
127 PathSegment::Field(field) => {
128 let next_value = value
129 .get(field)
130 .ok_or_else(|| {
131 ExternalLspError::JsonPathError(format!(
132 "Field '{}' not found in JSON object",
133 field
134 ))
135 })?;
136
137 Self::extract_recursive(next_value, segments, index + 1)
138 }
139 PathSegment::Index(idx) => {
140 let array = value.as_array().ok_or_else(|| {
141 ExternalLspError::JsonPathError(format!(
142 "Expected array at index access, got {}",
143 Self::value_type_name(value)
144 ))
145 })?;
146
147 let next_value = array.get(*idx).ok_or_else(|| {
148 ExternalLspError::JsonPathError(format!(
149 "Array index {} out of bounds (length: {})",
150 idx,
151 array.len()
152 ))
153 })?;
154
155 Self::extract_recursive(next_value, segments, index + 1)
156 }
157 PathSegment::Wildcard => {
158 let array = value.as_array().ok_or_else(|| {
159 ExternalLspError::JsonPathError(format!(
160 "Expected array for wildcard, got {}",
161 Self::value_type_name(value)
162 ))
163 })?;
164
165 let mut results = Vec::new();
166 for item in array {
167 let mut item_results = Self::extract_recursive(item, segments, index + 1)?;
168 results.append(&mut item_results);
169 }
170
171 Ok(results)
172 }
173 }
174 }
175
176 pub fn extract_single(&self, value: &Value) -> Result<Value> {
178 let results = self.extract(value)?;
179
180 if results.is_empty() {
181 return Err(ExternalLspError::JsonPathError(
182 "JSON path returned no results".to_string(),
183 ));
184 }
185
186 if results.len() > 1 {
187 return Err(ExternalLspError::JsonPathError(
188 "JSON path returned multiple results, expected single value".to_string(),
189 ));
190 }
191
192 Ok(results[0].clone())
193 }
194
195 fn value_type_name(value: &Value) -> &'static str {
197 match value {
198 Value::Null => "null",
199 Value::Bool(_) => "boolean",
200 Value::Number(_) => "number",
201 Value::String(_) => "string",
202 Value::Array(_) => "array",
203 Value::Object(_) => "object",
204 }
205 }
206}
207
208impl FromStr for JsonPathParser {
209 type Err = ExternalLspError;
210
211 fn from_str(s: &str) -> Result<Self> {
212 Self::parse(s)
213 }
214}
215
216#[cfg(test)]
217mod tests {
218 use super::*;
219 use serde_json::json;
220
221 #[test]
222 fn test_parse_simple_field() {
223 let parser = JsonPathParser::parse("$.result").unwrap();
224 let json = json!({"result": "value"});
225 let results = parser.extract(&json).unwrap();
226 assert_eq!(results.len(), 1);
227 assert_eq!(results[0], json!("value"));
228 }
229
230 #[test]
231 fn test_parse_nested_field() {
232 let parser = JsonPathParser::parse("$.result.items").unwrap();
233 let json = json!({"result": {"items": [1, 2, 3]}});
234 let results = parser.extract(&json).unwrap();
235 assert_eq!(results.len(), 1);
236 assert_eq!(results[0], json!([1, 2, 3]));
237 }
238
239 #[test]
240 fn test_parse_array_index() {
241 let parser = JsonPathParser::parse("$.items[0]").unwrap();
242 let json = json!({"items": ["a", "b", "c"]});
243 let results = parser.extract(&json).unwrap();
244 assert_eq!(results.len(), 1);
245 assert_eq!(results[0], json!("a"));
246 }
247
248 #[test]
249 fn test_parse_wildcard() {
250 let parser = JsonPathParser::parse("$.items[*].label").unwrap();
251 let json = json!({
252 "items": [
253 {"label": "first"},
254 {"label": "second"},
255 {"label": "third"}
256 ]
257 });
258 let results = parser.extract(&json).unwrap();
259 assert_eq!(results.len(), 3);
260 assert_eq!(results[0], json!("first"));
261 assert_eq!(results[1], json!("second"));
262 assert_eq!(results[2], json!("third"));
263 }
264
265 #[test]
266 fn test_parse_missing_field() {
267 let parser = JsonPathParser::parse("$.missing").unwrap();
268 let json = json!({"result": "value"});
269 let result = parser.extract(&json);
270 assert!(result.is_err());
271 }
272
273 #[test]
274 fn test_parse_invalid_expression() {
275 let result = JsonPathParser::parse("invalid");
276 assert!(result.is_err());
277 }
278
279 #[test]
280 fn test_parse_unclosed_bracket() {
281 let result = JsonPathParser::parse("$.items[0");
282 assert!(result.is_err());
283 }
284
285 #[test]
286 fn test_extract_single_success() {
287 let parser = JsonPathParser::parse("$.result").unwrap();
288 let json = json!({"result": "value"});
289 let result = parser.extract_single(&json).unwrap();
290 assert_eq!(result, json!("value"));
291 }
292
293 #[test]
294 fn test_extract_single_multiple_results() {
295 let parser = JsonPathParser::parse("$.items[*]").unwrap();
296 let json = json!({"items": [1, 2, 3]});
297 let result = parser.extract_single(&json);
298 assert!(result.is_err());
299 }
300}