1use serde_json::{Map, Value};
6use std::collections::HashMap;
7
8#[cfg(feature = "stateful")]
9pub type JsonObject = Map<String, Value>;
10
11#[allow(dead_code)]
15pub(crate) fn replace_str(value: &mut Value, from: &str, to: &str) {
16 let replacer = create_str_replacer(from, to);
17 replace_str_with(value, &replacer);
18}
19
20pub(crate) fn replace_str_in_map(map: &mut Map<String, Value>, from: &str, to: &str) {
24 let replacer = create_str_replacer(from, to);
25 replace_str_in_map_with(map, &replacer);
26}
27
28fn replace_str_with<F>(value: &mut Value, replacer: &F)
29where
30 F: Fn(&str) -> Option<String> + ?Sized,
31{
32 match value {
33 Value::String(s) => {
34 if let Some(r) = replacer(s) {
35 *s = r;
36 }
37 }
38
39 Value::Array(list) => {
40 for item in list.iter_mut() {
41 replace_str_with(item, replacer);
42 }
43 }
44
45 Value::Object(map) => {
46 replace_str_in_map_with(map, replacer);
47 }
48
49 _ => (),
50 };
51}
52
53pub(crate) fn replace_str_in_map_with<F>(map: &mut Map<String, Value>, replacer: &F)
54where
55 F: Fn(&str) -> Option<String> + ?Sized,
56{
57 for v in map.values_mut() {
59 replace_str_with(v, replacer);
60 }
61
62 let mut changes = HashMap::new();
64 for k in map.keys() {
65 if let Some(new) = replacer(k) {
66 changes.insert(k.to_owned(), new);
67 }
68 }
69
70 for (k, new) in changes {
71 let v = map.remove(&k).unwrap();
72 _ = map.insert(new, v);
73 }
74}
75
76fn create_str_replacer<'a>(from: &'a str, to: &'a str) -> impl Fn(&str) -> Option<String> + 'a {
77 move |s: &str| -> Option<String> {
78 if s.contains(from) {
79 Some(s.replace(from, to))
80 } else {
81 None
82 }
83 }
84}
85
86#[cfg(test)]
87mod unit_tests {
88 use super::*;
89 use serde_json::json;
90
91 #[test]
92 fn test_replace_str() {
93 let mut value = json!("{test}");
94 replace_str(&mut value, "{test}", "success");
95 assert_eq!(value, json!("success"));
96
97 let mut value = json!("{test}-postfix");
98 replace_str(&mut value, "{test}", "success");
99 assert_eq!(value, json!("success-postfix"));
100
101 let mut value = json!("prefix-{test}");
102 replace_str(&mut value, "{test}", "success");
103 assert_eq!(value, json!("prefix-success"));
104
105 let mut value = json!("prefix-{test}-postfix");
106 replace_str(&mut value, "{test}", "success");
107 assert_eq!(value, json!("prefix-success-postfix"));
108
109 let mut value = json!("prefix-{test}-multi-{test}-postfix");
110 replace_str(&mut value, "{test}", "success");
111 assert_eq!(value, json!("prefix-success-multi-success-postfix"));
112 }
113
114 #[test]
115 fn test_replace_str_in_array() {
116 let mut value = json!(["alice", "bob", "{placeholder}", "daphne"]);
117 replace_str(&mut value, "{placeholder}", "charlie");
118 assert_eq!(value, json!(["alice", "bob", "charlie", "daphne"]));
119 }
120
121 #[test]
122 fn test_replace_str_in_map() {
123 let mut value = json!({
124 "key": "{test}",
125 "not": true,
126 "or": 2,
127 });
128 replace_str(&mut value, "{test}", "success");
129 assert_eq!(
130 value,
131 json!({
132 "key": "success",
133 "not": true,
134 "or": 2,
135 })
136 );
137 }
138
139 #[test]
140 fn test_replace_str_in_map_keys() {
141 let mut value = json!({
142 "{test}-en-US": "{test}",
143 "not": true,
144 "or": 2,
145 });
146 replace_str(&mut value, "{test}", "success");
147 assert_eq!(
148 value,
149 json!({
150 "success-en-US": "success",
151 "not": true,
152 "or": 2,
153 })
154 );
155 }
156
157 #[test]
158 fn test_replace_str_mixed() {
159 let mut value = json!({
160 "messages": {
161 "{test}-en-US": {
162 "test": "{test}"
163 },
164 "{test}{test}": {
165 "test": "{test}{test}"
166 }
167 }
168 });
169 replace_str(&mut value, "{test}", "success");
170 assert_eq!(
171 value,
172 json!({
173 "messages": {
174 "success-en-US": {
175 "test": "success"
176 },
177 "successsuccess": {
178 "test": "successsuccess"
179 }
180 }
181 })
182 );
183 }
184}