brainwires_proxy/convert/
json_transform.rs1use crate::error::{ProxyError, ProxyResult};
4use bytes::Bytes;
5use serde_json::Value;
6
7#[derive(Debug, Clone)]
9pub enum JsonRule {
10 RenameField { from: String, to: String },
12 RemoveField(String),
14 SetField { path: String, value: Value },
16 WrapIn(String),
18 Unwrap(String),
20}
21
22pub struct JsonTransformer {
24 rules: Vec<JsonRule>,
25}
26
27impl JsonTransformer {
28 pub fn new(rules: Vec<JsonRule>) -> Self {
29 Self { rules }
30 }
31
32 pub fn transform(&self, input: &[u8]) -> ProxyResult<Bytes> {
33 let mut value: Value =
34 serde_json::from_slice(input).map_err(|e| ProxyError::Conversion(e.to_string()))?;
35
36 for rule in &self.rules {
37 value = apply_rule(value, rule)?;
38 }
39
40 let out = serde_json::to_vec(&value).map_err(|e| ProxyError::Conversion(e.to_string()))?;
41 Ok(Bytes::from(out))
42 }
43}
44
45fn apply_rule(mut value: Value, rule: &JsonRule) -> ProxyResult<Value> {
46 match rule {
47 JsonRule::RenameField { from, to } => {
48 if let Value::Object(ref mut map) = value
49 && let Some(v) = map.remove(from)
50 {
51 map.insert(to.clone(), v);
52 }
53 Ok(value)
54 }
55 JsonRule::RemoveField(path) => {
56 remove_at_path(&mut value, path);
57 Ok(value)
58 }
59 JsonRule::SetField { path, value: val } => {
60 set_at_path(&mut value, path, val.clone());
61 Ok(value)
62 }
63 JsonRule::WrapIn(key) => {
64 let mut map = serde_json::Map::new();
65 map.insert(key.clone(), value);
66 Ok(Value::Object(map))
67 }
68 JsonRule::Unwrap(path) => get_at_path(&value, path)
69 .cloned()
70 .ok_or_else(|| ProxyError::Conversion(format!("path not found: {path}"))),
71 }
72}
73
74fn get_at_path<'a>(value: &'a Value, path: &str) -> Option<&'a Value> {
75 let mut current = value;
76 for segment in path.split('.') {
77 current = current.get(segment)?;
78 }
79 Some(current)
80}
81
82fn set_at_path(value: &mut Value, path: &str, new_val: Value) {
83 let segments: Vec<&str> = path.split('.').collect();
84 if segments.is_empty() {
85 return;
86 }
87
88 let mut current = value;
89 for segment in &segments[..segments.len() - 1] {
90 current = match current {
91 Value::Object(map) => map
92 .entry(*segment)
93 .or_insert_with(|| Value::Object(serde_json::Map::new())),
94 _ => return,
95 };
96 }
97
98 if let Value::Object(map) = current {
99 map.insert(segments[segments.len() - 1].to_string(), new_val);
100 }
101}
102
103fn remove_at_path(value: &mut Value, path: &str) {
104 let segments: Vec<&str> = path.split('.').collect();
105 if segments.is_empty() {
106 return;
107 }
108
109 let mut current = value;
110 for segment in &segments[..segments.len() - 1] {
111 current = match current {
112 Value::Object(map) => match map.get_mut(*segment) {
113 Some(v) => v,
114 None => return,
115 },
116 _ => return,
117 };
118 }
119
120 if let Value::Object(map) = current {
121 map.remove(segments[segments.len() - 1]);
122 }
123}
124
125#[cfg(test)]
126mod tests {
127 use super::*;
128
129 #[test]
130 fn test_rename_field() {
131 let input = br#"{"old_name": 42, "keep": true}"#;
132 let t = JsonTransformer::new(vec![JsonRule::RenameField {
133 from: "old_name".into(),
134 to: "new_name".into(),
135 }]);
136 let out = t.transform(input).unwrap();
137 let v: Value = serde_json::from_slice(&out).unwrap();
138 assert_eq!(v["new_name"], 42);
139 assert!(v.get("old_name").is_none());
140 }
141
142 #[test]
143 fn test_wrap_and_unwrap() {
144 let input = br#"{"data": [1,2,3]}"#;
145 let t = JsonTransformer::new(vec![JsonRule::WrapIn("wrapper".into())]);
146 let out = t.transform(input).unwrap();
147 let v: Value = serde_json::from_slice(&out).unwrap();
148 assert!(v["wrapper"]["data"].is_array());
149
150 let t2 = JsonTransformer::new(vec![JsonRule::Unwrap("wrapper".into())]);
151 let out2 = t2.transform(&out).unwrap();
152 let v2: Value = serde_json::from_slice(&out2).unwrap();
153 assert_eq!(v2["data"], serde_json::json!([1, 2, 3]));
154 }
155}