json_eval_rs/jsoneval/
path_utils.rs1use serde_json::Value;
7
8#[inline]
16pub fn normalize_to_json_pointer(path: &str) -> String {
17 if path.is_empty() {
18 return String::new();
19 }
20
21 if path.starts_with("#/") {
23 let stripped = &path[1..];
24 if !stripped.contains("//") {
25 return stripped.to_string();
26 }
27 }
28
29 if path.starts_with('/') && !path.contains("//") {
31 return if path == "/" {
32 String::new()
33 } else {
34 path.to_string()
35 };
36 }
37
38 let mut normalized = if path.starts_with("#/") {
40 path[1..].to_string()
41 } else if !path.starts_with('/') {
42 if path.contains('.') {
43 format!("/{}", path.replace('.', "/"))
44 } else {
45 format!("/{}", path)
46 }
47 } else {
48 path.to_string()
49 };
50
51 while normalized.contains("//") {
53 normalized = normalized.replace("//", "/");
54 }
55
56 if normalized == "/" {
57 String::new()
58 } else {
59 normalized
60 }
61}
62
63#[inline]
72pub fn dot_notation_to_schema_pointer(path: &str) -> String {
73 if path.starts_with('#') || path.starts_with('/') {
75 return path.to_string();
76 }
77
78 let parts: Vec<&str> = path.split('.').collect();
80 if parts.is_empty() {
81 return "#/".to_string();
82 }
83
84 let mut result = String::from("#");
88 for (i, part) in parts.iter().enumerate() {
89 if part.eq(&"properties") {
90 continue;
91 }
92
93 if i > 0 && !path.starts_with('$') {
94 result.push_str("/properties");
95 }
96 result.push_str("/");
97 result.push_str(part);
98 }
99
100 result
101}
102
103#[inline]
112pub fn pointer_to_dot_notation(path: &str) -> String {
113 if path.is_empty() {
114 return String::new();
115 }
116
117 if !path.starts_with('#') && !path.starts_with('/') {
119 return path.to_string();
120 }
121
122 let clean_path = if path.starts_with("#/") {
124 &path[2..]
125 } else if path.starts_with('/') {
126 &path[1..]
127 } else if path.starts_with('#') {
128 &path[1..]
129 } else {
130 path
131 };
132
133 clean_path.replace('/', ".")
135}
136
137#[inline]
141pub fn get_value_by_pointer<'a>(data: &'a Value, pointer: &str) -> Option<&'a Value> {
142 if pointer.is_empty() {
143 Some(data)
144 } else {
145 data.pointer(pointer)
146 }
147}
148
149#[inline]
150pub fn get_value_by_pointer_without_properties<'a>(
151 data: &'a Value,
152 pointer: &str,
153) -> Option<&'a Value> {
154 if pointer.is_empty() {
155 Some(data)
156 } else {
157 data.pointer(&pointer.replace("properties/", ""))
158 }
159}
160
161pub fn get_values_by_pointers<'a>(data: &'a Value, pointers: &[String]) -> Vec<Option<&'a Value>> {
163 pointers
164 .iter()
165 .map(|pointer| get_value_by_pointer(data, pointer))
166 .collect()
167}
168
169#[inline]
173pub fn get_array_element<'a>(data: &'a Value, index: usize) -> Option<&'a Value> {
174 data.as_array()?.get(index)
175}
176
177#[inline]
181pub fn get_array_element_by_pointer<'a>(
182 data: &'a Value,
183 pointer: &str,
184 index: usize,
185) -> Option<&'a Value> {
186 get_value_by_pointer(data, pointer)?.as_array()?.get(index)
187}
188
189#[derive(Debug, Clone)]
191pub struct ArrayMetadata {
192 pub pointer: String,
194 pub length: usize,
196 pub column_names: Vec<String>,
198 pub is_uniform: bool,
200}
201
202impl ArrayMetadata {
203 pub fn build(data: &Value, pointer: &str) -> Option<Self> {
205 let array = get_value_by_pointer(data, pointer)?.as_array()?;
206
207 let length = array.len();
208 if length == 0 {
209 return Some(ArrayMetadata {
210 pointer: pointer.to_string(),
211 length: 0,
212 column_names: Vec::new(),
213 is_uniform: true,
214 });
215 }
216
217 let first_element = &array[0];
219 let column_names = if let Value::Object(obj) = first_element {
220 obj.keys().cloned().collect()
221 } else {
222 Vec::new()
223 };
224
225 let is_uniform = if !column_names.is_empty() {
227 array.iter().all(|elem| {
228 if let Value::Object(obj) = elem {
229 obj.keys().len() == column_names.len()
230 && column_names.iter().all(|col| obj.contains_key(col))
231 } else {
232 false
233 }
234 })
235 } else {
236 let first_type = std::mem::discriminant(first_element);
238 array
239 .iter()
240 .all(|elem| std::mem::discriminant(elem) == first_type)
241 };
242
243 Some(ArrayMetadata {
244 pointer: pointer.to_string(),
245 length,
246 column_names,
247 is_uniform,
248 })
249 }
250
251 #[inline]
253 pub fn get_column_value<'a>(
254 &self,
255 data: &'a Value,
256 row_index: usize,
257 column: &str,
258 ) -> Option<&'a Value> {
259 if !self.is_uniform || row_index >= self.length {
260 return None;
261 }
262
263 get_array_element_by_pointer(data, &self.pointer, row_index)?
264 .as_object()?
265 .get(column)
266 }
267
268 #[inline]
270 pub fn is_valid_index(&self, index: usize) -> bool {
271 index < self.length
272 }
273}