1mod spec;
2mod shift;
3mod default;
4mod remove;
5mod modifier;
6mod pointer;
7mod transform;
8mod error;
9pub mod dsl;
10pub mod fuzz;
11
12use serde_json::{Map, Value};
13use serde_json::map::Entry;
14use transform::Transform;
15
16use crate::default::default;
17use crate::remove::remove;
18use crate::modifier::modify;
19use crate::spec::SpecEntry;
20
21pub use spec::TransformSpec;
22use crate::pointer::JsonPointer;
23
24pub use error::{Error, Result};
25
26pub fn transform(input: Value, spec: &TransformSpec) -> Result<Value> {
80 let mut result = input;
81 for entry in spec.entries() {
82 match entry {
83 SpecEntry::Shift(shift) => result = shift.apply(&result)?,
84 SpecEntry::Default(spec) => result = default(result, spec),
85 SpecEntry::Modify(spec) => result = modify(result, spec)?,
86 SpecEntry::Remove(spec) => result = remove(result, spec),
87 }
88 }
89 Ok(result)
90}
91
92pub(crate) fn insert(dest: &mut Value, position: JsonPointer, val: Value) {
93 let elements = position.iter();
94 let folded = elements
95 .skip(1)
96 .try_fold(dest, |target, token| match target {
97 Value::Object(map) => {
98 if let Entry::Vacant(entry) = map.entry(token) {
99 entry.insert(Value::Object(Map::new()));
100 }
101 map.get_mut(token)
102 }
103 _ => None,
104 });
105 if let Some(pointer_mut) = folded {
106 merge(pointer_mut, val);
107 }
108}
109
110fn merge(dest: &mut Value, new_value: Value) {
112 match (dest, new_value) {
113 (Value::Object(dest), Value::Object(new_value)) => {
114 for (key, value) in new_value.into_iter() {
115 dest.insert(key, value);
116 }
117 }
118 (dest, new_value) => *dest = new_value,
119 };
120}
121
122pub(crate) fn delete(dest: &mut Value, position: &JsonPointer) -> Option<()> {
123 if let Some(Value::Object(map)) = dest.pointer_mut(position.parent().join_rfc6901().as_str()) {
124 map.remove(position.leaf_name());
125 }
126 Some(())
127}
128
129#[cfg(test)]
130mod test {
131
132 use serde_json::json;
133 use super::*;
134
135 #[test]
136 fn test_transform() {
137 let spec: TransformSpec = serde_json::from_value(json!(
138 [
139 {
140 "operation": "shift",
141 "spec": {
142 "a": "a_new",
143 "c": "c_new"
144 }
145 }
146 ]
147 ))
148 .expect("parsed spec");
149
150 let source = json!({
151 "a": "b",
152 "c": "d"
153 });
154 let result = transform(source, &spec).unwrap();
155
156 assert_eq!(
157 result,
158 json!({
159 "a_new": "b",
160 "c_new": "d"
161 })
162 );
163 }
164
165 #[test]
166 fn test_insert_object_to_empty() {
167 let mut empty_dest = Value::Object(Map::new());
169 let value = json!({
170 "a": "b",
171 });
172
173 insert(
174 &mut empty_dest,
175 JsonPointer::from_dot_notation("new"),
176 value,
177 );
178
179 assert_eq!(
180 empty_dest,
181 json!({
182 "new": {
183 "a": "b"
184 }
185 })
186 );
187 }
188
189 #[test]
190 fn test_insert_object_to_non_empty() {
191 let mut dest = json!({
193 "b": "bb",
194 "c": "cc",
195 });
196 let value = json!({
197 "a": "b",
198 });
199
200 insert(&mut dest, JsonPointer::from_dot_notation("new"), value);
201
202 assert_eq!(
203 dest,
204 json!({
205 "b": "bb",
206 "c": "cc",
207 "new": {
208 "a": "b"
209 }
210 })
211 );
212 }
213
214 #[test]
215 fn test_insert_object_merged() {
216 let mut dest = json!({
218 "some": {
219 "b": "bb",
220 "c": "cc",
221 }
222 });
223 let value = json!({
224 "a": "b",
225 });
226
227 insert(&mut dest, JsonPointer::from_dot_notation("some"), value);
228
229 assert_eq!(
230 dest,
231 json!({
232 "some": {
233 "a": "b",
234 "b": "bb",
235 "c": "cc",
236 }
237 })
238 );
239 }
240
241 #[test]
242 fn test_insert_object_to_empty_non_root() {
243 let mut empty_dest = Value::Object(Map::new());
245 let value = json!({
246 "a": "b",
247 });
248
249 insert(
250 &mut empty_dest,
251 JsonPointer::from_dot_notation("level1.level2.new"),
252 value,
253 );
254
255 assert_eq!(
256 empty_dest,
257 json!({
258 "level1": {
259 "level2": {
260 "new": {
261 "a": "b"
262 }
263 }
264 }
265 })
266 );
267 }
268
269 #[test]
270 fn test_delete_empty_pointer() {
271 let mut input = json!({
273 "a": "b",
274 });
275
276 let _ = delete(&mut input, &JsonPointer::from_dot_notation(""));
278
279 assert_eq!(
281 input,
282 json!({
283 "a": "b",
284 })
285 );
286 }
287
288 #[test]
289 fn test_delete_not_existing() {
290 let mut input = json!({
292 "a": "b",
293 });
294
295 let _ = delete(&mut input, &JsonPointer::from_dot_notation(".b"));
297
298 assert_eq!(
300 input,
301 json!({
302 "a": "b",
303 })
304 );
305 }
306
307 #[test]
308 fn test_delete() {
309 let mut input1 = json!({
311 "a": "b",
312 });
313 let mut input2 = json!({
314 "a": "b",
315 "b": "c",
316 });
317 let _ = delete(&mut input1, &JsonPointer::from_dot_notation(".a"));
319 let _ = delete(&mut input2, &JsonPointer::from_dot_notation("b"));
320
321 assert_eq!(input1, json!({}));
323 assert_eq!(
324 input2,
325 json!({
326 "a": "b",
327 })
328 );
329 }
330
331 #[test]
332 fn test_invalid_array_index_error_includes_raw_value() {
333 let spec: TransformSpec = serde_json::from_value(json!(
336 [
337 {
338 "operation": "shift",
339 "spec": {
340 "*": "out[&(0,0)]"
341 }
342 }
343 ]
344 ))
345 .expect("parsed spec");
346
347 let source = json!({
348 "not_numeric": 1
349 });
350
351 let result = transform(source, &spec);
352 assert!(result.is_err(), "Expected error when using non-numeric key as array index");
353
354 let err_msg = result.unwrap_err().to_string();
355 assert!(
356 err_msg.contains("not_numeric"),
357 "Error should include the invalid index value, got: {}",
358 err_msg
359 );
360 assert!(
361 err_msg.contains("Invalid index in expression"),
362 "Error should mention 'Invalid index in expression', got: {}",
363 err_msg
364 );
365 }
366}