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