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