json_eval_rs/
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 "".to_string();
19 }
20
21 let mut normalized = path.to_string();
22
23 if normalized.starts_with("#/") {
25 normalized = normalized[1..].to_string(); } else if !normalized.starts_with('/') {
27 if normalized.contains('.') {
29 normalized = format!("/{}", normalized.replace('.', "/"));
30 } else {
31 normalized = format!("/{}", normalized);
33 }
34 }
35
36 while normalized.contains("//") {
38 normalized = normalized.replace("//", "/");
39 }
40
41 if normalized == "/" {
43 "".to_string() } else {
45 normalized
46 }
47}
48
49#[inline]
58pub fn dot_notation_to_schema_pointer(path: &str) -> String {
59 if path.starts_with('#') || path.starts_with('/') {
61 return path.to_string();
62 }
63
64 let parts: Vec<&str> = path.split('.').collect();
66 if parts.is_empty() {
67 return "#/".to_string();
68 }
69
70 let mut result = String::from("#/");
73 for (i, part) in parts.iter().enumerate() {
74 if part.eq(&"properties") {
75 continue;
76 }
77
78 if i > 0 {
79 result.push_str("/properties/");
80 }
81 result.push_str(part);
82 }
83
84 result
85}
86
87#[inline]
96pub fn pointer_to_dot_notation(path: &str) -> String {
97 if path.is_empty() {
98 return String::new();
99 }
100
101 if !path.starts_with('#') && !path.starts_with('/') {
103 return path.to_string();
104 }
105
106 let clean_path = if path.starts_with("#/") {
108 &path[2..]
109 } else if path.starts_with('/') {
110 &path[1..]
111 } else if path.starts_with('#') {
112 &path[1..]
113 } else {
114 path
115 };
116
117 clean_path.replace('/', ".")
119}
120
121#[inline]
125pub fn get_value_by_pointer<'a>(data: &'a Value, pointer: &str) -> Option<&'a Value> {
126 if pointer.is_empty() {
127 Some(data)
128 } else {
129 data.pointer(pointer)
130 }
131}
132
133#[inline]
134pub fn get_value_by_pointer_without_properties<'a>(data: &'a Value, pointer: &str) -> Option<&'a Value> {
135 if pointer.is_empty() {
136 Some(data)
137 } else {
138 data.pointer(&pointer.replace("properties/", ""))
139 }
140}
141
142pub fn get_values_by_pointers<'a>(data: &'a Value, pointers: &[String]) -> Vec<Option<&'a Value>> {
144 pointers.iter()
145 .map(|pointer| get_value_by_pointer(data, pointer))
146 .collect()
147}
148
149#[inline]
153pub fn get_array_element<'a>(data: &'a Value, index: usize) -> Option<&'a Value> {
154 data.as_array()?.get(index)
155}
156
157#[inline]
161pub fn get_array_element_by_pointer<'a>(data: &'a Value, pointer: &str, index: usize) -> Option<&'a Value> {
162 get_value_by_pointer(data, pointer)?
163 .as_array()?
164 .get(index)
165}
166
167#[derive(Debug, Clone)]
169pub struct ArrayMetadata {
170 pub pointer: String,
172 pub length: usize,
174 pub column_names: Vec<String>,
176 pub is_uniform: bool,
178}
179
180impl ArrayMetadata {
181 pub fn build(data: &Value, pointer: &str) -> Option<Self> {
183 let array = get_value_by_pointer(data, pointer)?.as_array()?;
184
185 let length = array.len();
186 if length == 0 {
187 return Some(ArrayMetadata {
188 pointer: pointer.to_string(),
189 length: 0,
190 column_names: Vec::new(),
191 is_uniform: true,
192 });
193 }
194
195 let first_element = &array[0];
197 let column_names = if let Value::Object(obj) = first_element {
198 obj.keys().cloned().collect()
199 } else {
200 Vec::new()
201 };
202
203 let is_uniform = if !column_names.is_empty() {
205 array.iter().all(|elem| {
206 if let Value::Object(obj) = elem {
207 obj.keys().len() == column_names.len() &&
208 column_names.iter().all(|col| obj.contains_key(col))
209 } else {
210 false
211 }
212 })
213 } else {
214 let first_type = std::mem::discriminant(first_element);
216 array.iter().all(|elem| std::mem::discriminant(elem) == first_type)
217 };
218
219 Some(ArrayMetadata {
220 pointer: pointer.to_string(),
221 length,
222 column_names,
223 is_uniform,
224 })
225 }
226
227 #[inline]
229 pub fn get_column_value<'a>(&self, data: &'a Value, row_index: usize, column: &str) -> Option<&'a Value> {
230 if !self.is_uniform || row_index >= self.length {
231 return None;
232 }
233
234 get_array_element_by_pointer(data, &self.pointer, row_index)?
235 .as_object()?
236 .get(column)
237 }
238
239 #[inline]
241 pub fn is_valid_index(&self, index: usize) -> bool {
242 index < self.length
243 }
244}
245
246