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