1use serde_json::Value;
2
3enum IndexingState {
4 LookingForLeftParenthesis,
5 Accumulating(usize),
6 Starting,
7}
8
9pub fn update_nested_value<V>(target: &mut Value, path: &[&str], value: V)
11where
12 V: Into<String>,
13{
14 let map = match target {
15 Value::Object(map) => map,
16 _ => return,
17 };
18
19 let (key, rest) = match path.split_first() {
20 Some(tuple) => tuple,
21 None => return,
22 };
23
24 let entry = map.entry(key.to_owned());
25
26 if rest.is_empty() {
27 entry.or_insert_with(|| Value::String(value.into()));
28 } else {
29 let sub_object = entry.or_insert_with(|| Value::Object(Default::default()));
30 update_nested_value(sub_object, rest, value);
31 }
32}
33
34pub fn merge_values(a: &mut Value, b: Value) {
39 match (a, b) {
40 (a @ &mut Value::Object(_), Value::Object(b)) => {
42 let a = a.as_object_mut().unwrap();
43 for (k, v) in b {
44 merge_values(a.entry(k).or_insert(Value::Null), v);
45 }
46 }
47 (a @ &mut Value::Null, b) => *a = b,
49 (_a, _b) => {}
51 }
52}
53
54fn get_indexes(full_string: &str) -> Result<Vec<&str>, ()> {
56 let mut ret_vals = vec![];
57 let mut state = IndexingState::Starting;
58 for (idx, by) in full_string.bytes().enumerate() {
60 match state {
61 IndexingState::Starting => {
62 if by == b'[' {
63 state = IndexingState::Accumulating(idx + 1)
64 }
65 }
66 IndexingState::LookingForLeftParenthesis => {
67 if by == b'[' {
68 state = IndexingState::Accumulating(idx + 1);
69 } else if by == b'=' {
70 return Ok(ret_vals);
71 } else {
72 return Err(());
73 }
74 }
75 IndexingState::Accumulating(start_idx) => {
76 if by == b']' {
77 let slice = &full_string[start_idx..idx];
78 ret_vals.push(slice);
79 state = IndexingState::LookingForLeftParenthesis;
80 }
81 }
82 }
83 }
84 Ok(ret_vals)
85}
86
87pub fn get_sentry_entry_indexes(param_name: &str) -> Option<Vec<&str>> {
89 if param_name.starts_with("sentry[") {
90 get_indexes(param_name).ok()
91 } else {
92 None
93 }
94}
95
96pub fn get_sentry_chunk_index(key: &str, prefix: &str) -> Option<usize> {
101 key.strip_prefix(prefix).and_then(|rest| rest.parse().ok())
102}
103
104#[derive(Clone, Debug, Default)]
106pub struct ChunkedFormDataAggregator<'a> {
107 parts: Vec<&'a str>,
108}
109
110impl<'a> ChunkedFormDataAggregator<'a> {
111 pub fn new() -> Self {
113 Self::default()
114 }
115
116 pub fn insert(&mut self, index: usize, value: &'a str) {
121 if index >= self.parts.len() {
122 self.parts.resize(index + 1, "");
123 }
124
125 self.parts[index] = value;
126 }
127
128 pub fn is_empty(&self) -> bool {
130 self.parts.is_empty()
131 }
132
133 pub fn join(&self) -> String {
135 self.parts.join("")
136 }
137}
138
139#[cfg(test)]
140mod tests {
141 use super::*;
142
143 #[test]
144 fn test_index_parser() {
145 let examples: &[(&str, Option<&[&str]>)] = &[
146 ("fafdasd[a][b][33]", Some(&["a", "b", "33"])),
147 ("fafdasd[a]b[33]", None),
148 ("fafdasd[a][b33]xx", None),
149 ("[23a][234][abc123]", Some(&["23a", "234", "abc123"])),
150 ("sentry[abc][123][]=SomeVal", Some(&["abc", "123", ""])),
151 ("sentry[Grüße][Jürgen][❤]", Some(&["Grüße", "Jürgen", "❤"])),
152 (
153 "[农22历][新年][b新年c]",
154 Some(&["农22历", "新年", "b新年c"]),
155 ),
156 ("[ὈΔΥΣΣΕΎΣ][abc]", Some(&["ὈΔΥΣΣΕΎΣ", "abc"])),
157 ];
158
159 for &(example, expected_result) in examples {
160 let indexes = get_indexes(example).ok();
161 assert_eq!(indexes, expected_result.map(|vec| vec.into()));
162 }
163 }
164
165 #[test]
166 fn test_update_value() {
167 let mut val = Value::Object(serde_json::Map::new());
168
169 update_nested_value(&mut val, &["x", "y", "z"], "xx");
170
171 insta::assert_json_snapshot!(val, @r###"
172 {
173 "x": {
174 "y": {
175 "z": "xx"
176 }
177 }
178 }
179 "###);
180
181 update_nested_value(&mut val, &["x", "y", "k"], "kk");
182 update_nested_value(&mut val, &["w", ""], "w");
183 update_nested_value(&mut val, &["z1"], "val1");
184 insta::assert_json_snapshot!(val, @r###"
185 {
186 "w": {
187 "": "w"
188 },
189 "x": {
190 "y": {
191 "k": "kk",
192 "z": "xx"
193 }
194 },
195 "z1": "val1"
196 }
197 "###);
198 }
199
200 #[test]
201 fn test_merge_vals() {
202 let mut original = serde_json::json!({
203 "k1": "v1",
204 "k2": {
205 "k3": "v3",
206 "k4": "v4"
207 },
208 "k5": [ 1,2,3]
209 });
210
211 let modified = serde_json::json!({
212 "k1": "v1bis",
213 "k2": {
214 "k4": "v4bis",
215 "k4-1": "v4-1"
216 },
217 "k6": "v6"
218 });
219
220 merge_values(&mut original, modified);
221 insta::assert_json_snapshot!(original, @r###"
222 {
223 "k1": "v1",
224 "k2": {
225 "k3": "v3",
226 "k4": "v4",
227 "k4-1": "v4-1"
228 },
229 "k5": [
230 1,
231 2,
232 3
233 ],
234 "k6": "v6"
235 }
236 "###);
237 }
238
239 #[test]
240 fn test_chunk_index() {
241 assert_eq!(get_sentry_chunk_index("sentry__0", "sentry__"), Some(0));
242 assert_eq!(get_sentry_chunk_index("sentry__1", "sentry__"), Some(1));
243
244 assert_eq!(get_sentry_chunk_index("foo__0", "sentry__"), None);
245 assert_eq!(get_sentry_chunk_index("sentry__", "sentry__"), None);
246 assert_eq!(get_sentry_chunk_index("sentry__-1", "sentry__"), None);
247 assert_eq!(get_sentry_chunk_index("sentry__xx", "sentry__"), None);
248 }
249
250 #[test]
251 fn test_aggregator_empty() {
252 let aggregator = ChunkedFormDataAggregator::new();
253 assert!(aggregator.is_empty());
254 assert_eq!(aggregator.join(), "");
255 }
256
257 #[test]
258 fn test_aggregator_base_0() {
259 let mut aggregator = ChunkedFormDataAggregator::new();
260 aggregator.insert(0, "hello,");
261 aggregator.insert(1, " world");
262
263 assert!(!aggregator.is_empty());
264 assert_eq!(aggregator.join(), "hello, world");
265 }
266
267 #[test]
268 fn test_aggregator_base_1() {
269 let mut aggregator = ChunkedFormDataAggregator::new();
270 aggregator.insert(1, "hello,");
271 aggregator.insert(2, " world");
272
273 assert!(!aggregator.is_empty());
274 assert_eq!(aggregator.join(), "hello, world");
275 }
276
277 #[test]
278 fn test_aggregator_holes() {
279 let mut aggregator = ChunkedFormDataAggregator::new();
280 aggregator.insert(0, "hello,");
281 aggregator.insert(3, " world");
282
283 assert!(!aggregator.is_empty());
284 assert_eq!(aggregator.join(), "hello, world");
285 }
286
287 #[test]
288 fn test_aggregator_reversed() {
289 let mut aggregator = ChunkedFormDataAggregator::new();
290 aggregator.insert(1, " world");
291 aggregator.insert(0, "hello,");
292
293 assert!(!aggregator.is_empty());
294 assert_eq!(aggregator.join(), "hello, world");
295 }
296
297 #[test]
298 fn test_aggregator_override() {
299 let mut aggregator = ChunkedFormDataAggregator::new();
300 aggregator.insert(0, "hello,");
301 aggregator.insert(0, "bye");
302
303 assert!(!aggregator.is_empty());
304 assert_eq!(aggregator.join(), "bye");
305 }
306}