mockforge_core/overrides/
patcher.rs1use json_patch::{AddOperation, PatchOperation, RemoveOperation, ReplaceOperation};
7use jsonptr::PointerBuf;
8use serde_json::Value;
9
10use super::models::PatchOp;
11use crate::templating::expand_tokens;
12
13pub fn apply_patch(doc: &mut Value, op: &PatchOp) -> anyhow::Result<()> {
15 let ops = match op {
16 PatchOp::Add { path, value } => vec![PatchOperation::Add(AddOperation {
17 path: path.parse().unwrap_or_else(|_| PointerBuf::new()),
18 value: value.clone(),
19 })],
20 PatchOp::Replace { path, value } => vec![PatchOperation::Replace(ReplaceOperation {
21 path: path.parse().unwrap_or_else(|_| PointerBuf::new()),
22 value: value.clone(),
23 })],
24 PatchOp::Remove { path } => vec![PatchOperation::Remove(RemoveOperation {
25 path: path.parse().unwrap_or_else(|_| PointerBuf::new()),
26 })],
27 };
28
29 json_patch::patch(doc, &ops)?;
31 Ok(())
32}
33
34pub fn apply_merge_patch(doc: &mut Value, op: &PatchOp) -> anyhow::Result<()> {
36 match op {
37 PatchOp::Add { path, value } | PatchOp::Replace { path, value } => {
38 merge_at_path(doc, path, value)?;
40 }
41 PatchOp::Remove { path: _ } => {
42 apply_patch(doc, op)?;
44 }
45 }
46 Ok(())
47}
48
49fn merge_at_path(doc: &mut Value, path: &str, value: &Value) -> anyhow::Result<()> {
51 if path.is_empty() || path == "/" {
52 merge_values(doc, value);
54 return Ok(());
55 }
56
57 let ptr: PointerBuf = path.parse().unwrap_or_else(|_| PointerBuf::new());
58 let mut current = doc;
59
60 let segments = ptr.as_str().trim_start_matches('/').split('/').collect::<Vec<_>>();
62 for (i, segment) in segments.iter().enumerate() {
63 let decoded_segment = segment.replace("~1", "/").replace("~0", "~");
64
65 if i == segments.len() - 1 {
66 match current {
68 Value::Object(map) => {
69 if let Some(existing) = map.get_mut(&decoded_segment) {
70 merge_values(existing, value);
71 } else {
72 map.insert(decoded_segment, value.clone());
73 }
74 }
75 _ => {
76 *current = serde_json::json!({ decoded_segment: value });
78 }
79 }
80 } else {
81 match current {
83 Value::Object(map) => {
84 current =
85 map.entry(decoded_segment).or_insert(Value::Object(serde_json::Map::new()));
86 }
87 _ => {
88 let mut new_obj = serde_json::Map::new();
90 new_obj
91 .insert(decoded_segment.to_string(), Value::Object(serde_json::Map::new()));
92 *current = Value::Object(new_obj);
93 current = &mut current[decoded_segment];
94 }
95 }
96 }
97 }
98
99 Ok(())
100}
101
102fn merge_values(target: &mut Value, source: &Value) {
104 match target {
105 Value::Object(target_map) => {
106 if let Value::Object(source_map) = source {
107 for (key, source_value) in source_map {
108 if let Some(target_value) = target_map.get_mut(key) {
109 merge_values(target_value, source_value);
110 } else {
111 target_map.insert(key.clone(), source_value.clone());
112 }
113 }
114 } else {
115 *target = source.clone();
117 }
118 }
119 Value::Array(target_arr) => {
120 if let Value::Array(source_arr) = source {
121 target_arr.extend(source_arr.clone());
122 } else {
123 *target = source.clone();
125 }
126 }
127 _ => {
128 *target = source.clone();
130 }
131 }
132}
133
134pub fn apply_post_templating(value: &mut Value) {
136 *value = expand_tokens(value);
137}