1use serde_json::Value;
2
3use crate::types::{new_key_path, ProcessHandling, TypedValue, ValueType};
4
5fn typed_value_from_json(val: &Value) -> Option<(TypedValue, ValueType)> {
6 match val {
7 Value::Bool(b) => Some((TypedValue::Bool(*b), ValueType::Bool)),
8 Value::String(s) => Some((TypedValue::String(s.clone()), ValueType::String)),
9 Value::Number(n) => {
10 if let Some(i) = n.as_i64() {
11 if n.is_i64() {
13 return Some((TypedValue::Integer(i), ValueType::Integer));
14 }
15 }
16 n.as_f64()
17 .map(|f| (TypedValue::Number(f), ValueType::Number))
18 }
19 Value::Null => Some((TypedValue::Null, ValueType::Null)),
20 _ => None, }
22}
23
24fn typed_value_to_json(typed: TypedValue) -> Value {
25 match typed {
26 TypedValue::Bool(b) => Value::Bool(b),
27 TypedValue::Integer(i) => Value::Number(i.into()),
28 TypedValue::Number(f) => Value::Number(
29 serde_json::Number::from_f64(f).unwrap_or_else(|| 0i64.into()),
30 ),
31 TypedValue::String(s) => Value::String(s),
32 TypedValue::Null => Value::Null,
33 }
34}
35
36type Processor<'a> = &'a mut dyn FnMut(
37 TypedValue,
38 ValueType,
39 &str,
40) -> Result<(TypedValue, ValueType, ProcessHandling), Box<dyn std::error::Error>>;
41
42fn traverse(
43 val: &mut Value,
44 key_path: &str,
45 processor: Processor<'_>,
46 filter_keys: &[String],
47) -> Result<bool, Box<dyn std::error::Error>> {
48 if !filter_keys.is_empty()
55 && filter_keys.iter().any(|k| k.as_str() == key_path)
56 && matches!(val, Value::Object(_) | Value::Array(_))
57 {
58 let serialized = format!("__subnode__\n{}", serde_json::to_string(val)?);
59 match processor(TypedValue::String(serialized), ValueType::String, key_path) {
60 Ok((new_typed, _, ProcessHandling::Process)) => {
61 *val = typed_value_to_json(new_typed);
62 return Ok(true);
63 }
64 Ok((_, _, ProcessHandling::Cancel)) => return Ok(false),
65 Ok((_, _, ProcessHandling::Skip)) => {
66 }
68 Err(e) => {
69 eprintln!("error processing key '{}': {}", key_path, e);
70 return Ok(false);
71 }
72 }
73 }
74
75 match val {
76 Value::Object(map) => {
77 let keys: Vec<String> = map.keys().cloned().collect();
79 for key in &keys {
80 let new_path = new_key_path(key_path, key);
81 let child = map.get_mut(key).unwrap();
82 if !traverse(child, &new_path, processor, filter_keys)? {
83 return Ok(false);
84 }
85 }
86 }
87 Value::Array(arr) => {
88 for item in arr.iter_mut() {
89 if !traverse(item, key_path, processor, filter_keys)? {
90 return Ok(false);
91 }
92 }
93 }
94 scalar => {
95 if !filter_keys.is_empty()
96 && !filter_keys.iter().any(|k| k.as_str() == key_path)
97 {
98 return Ok(true);
99 }
100 if let Some((typed, vt)) = typed_value_from_json(scalar) {
101 match processor(typed, vt, key_path) {
102 Ok((new_typed, _, ProcessHandling::Process)) => {
103 let mut restored = false;
107 if let TypedValue::String(ref s) = new_typed {
108 if let Some(inner) = s.strip_prefix("__subnode__\n") {
109 if let Ok(parsed) = serde_json::from_str::<Value>(inner) {
110 if matches!(parsed, Value::Object(_) | Value::Array(_)) {
111 *scalar = parsed;
112 restored = true;
113 }
114 }
115 }
116 }
117 if !restored {
118 *scalar = typed_value_to_json(new_typed);
119 }
120 }
121 Ok((_, _, ProcessHandling::Cancel)) => return Ok(false),
122 Ok((_, _, ProcessHandling::Skip)) => {}
123 Err(e) => {
124 eprintln!("error processing key '{}': {}", key_path, e);
125 return Ok(false);
126 }
127 }
128 }
129 }
130 }
131 Ok(true)
132}
133
134pub fn process_in_memory(
137 input: &str,
138 filter_keys: &[String],
139 processor: Processor<'_>,
140) -> Result<Value, Box<dyn std::error::Error>> {
141 let content = std::fs::read_to_string(input)
142 .map_err(|e| format!("error reading '{}': {}", input, e))?;
143 let mut root: Value = serde_json::from_str(&content)
144 .map_err(|e| format!("error parsing JSON from '{}': {}", input, e))?;
145 if !traverse(&mut root, "", processor, filter_keys)? {
146 return Err("processing canceled".into());
147 }
148 Ok(root)
149}
150
151pub fn process_file(
155 input: &str,
156 output: &str,
157 filter_keys: &[String],
158 processor: Processor<'_>,
159) -> Result<(), Box<dyn std::error::Error>> {
160 let content = std::fs::read_to_string(input)
161 .map_err(|e| format!("error reading '{}': {}", input, e))?;
162 let mut root: Value = serde_json::from_str(&content)
163 .map_err(|e| format!("error parsing JSON from '{}': {}", input, e))?;
164
165 if !traverse(&mut root, "", processor, filter_keys)? {
166 return Err("processing canceled".into());
167 }
168
169 let output_str = serde_json::to_string_pretty(&root)?;
170 if output == "stdout" {
171 println!("{}", output_str);
172 } else {
173 std::fs::write(output, &output_str)
174 .map_err(|e| format!("error writing '{}': {}", output, e))?;
175 }
176 Ok(())
177}
178
179pub fn get_encrypted_keys(input: &str) -> Result<Vec<String>, Box<dyn std::error::Error>> {
181 let content = std::fs::read_to_string(input)?;
182 let root: Value = serde_json::from_str(&content)?;
183 let mut encrypted = Vec::new();
184 collect_encrypted(&root, "", &mut encrypted);
185 Ok(encrypted)
186}
187
188fn collect_encrypted(val: &Value, key_path: &str, out: &mut Vec<String>) {
189 match val {
190 Value::Object(map) => {
191 for (key, child) in map.iter() {
192 collect_encrypted(child, &new_key_path(key_path, key), out);
193 }
194 }
195 Value::Array(arr) => {
196 for item in arr.iter() {
197 collect_encrypted(item, key_path, out);
198 }
199 }
200 Value::String(s) if s.contains("$ANSIBLE_VAULT;") => {
201 out.push(key_path.to_string());
202 }
203 _ => {}
204 }
205}
206
207pub fn collect_paths_at_level(input: &str, level: u32) -> Result<Vec<String>, Box<dyn std::error::Error>> {
210 let content = std::fs::read_to_string(input)
211 .map_err(|e| format!("error reading '{}': {}", input, e))?;
212 let root: Value = serde_json::from_str(&content)
213 .map_err(|e| format!("error parsing JSON from '{}': {}", input, e))?;
214 let mut paths = Vec::new();
215 collect_at_level(&root, "", level, &mut paths);
216 Ok(paths)
217}
218
219fn collect_at_level(val: &Value, key_path: &str, remaining: u32, out: &mut Vec<String>) {
220 if remaining == 0 {
221 out.push(key_path.to_string());
222 return;
223 }
224 if let Value::Object(map) = val {
225 for (key, child) in map.iter() {
226 collect_at_level(child, &new_key_path(key_path, key), remaining - 1, out);
227 }
228 }
229 }