1use crate::path::{BorrowedSegment, ValuePath};
4use crate::value::Kind;
5use crate::value::kind::Collection;
6
7impl Kind {
8 #[allow(clippy::needless_pass_by_value)] pub fn insert<'a>(&mut self, path: impl ValuePath<'a>, kind: Self) {
12 self.insert_recursive(path.segment_iter(), kind.upgrade_undefined());
13 }
14
15 #[allow(clippy::needless_pass_by_value)] pub fn set_at_path<'a>(&mut self, path: impl ValuePath<'a>, kind: Self) {
20 self.insert_recursive(path.segment_iter(), kind);
21 }
22
23 #[allow(clippy::too_many_lines)]
29 #[allow(clippy::needless_pass_by_value)] pub fn insert_recursive<'a, 'b>(
31 &'a mut self,
32 mut iter: impl Iterator<Item = BorrowedSegment<'b>> + Clone,
33 kind: Self,
34 ) {
35 if kind.is_never() {
36 return;
39 }
40
41 if let Some(segment) = iter.next() {
42 match segment {
43 BorrowedSegment::Field(field) => {
44 *self = Self::object(self.object.clone().unwrap_or_else(Collection::empty));
46
47 let collection = self.object.as_mut().expect("object was just inserted");
48 let unknown_kind = collection.unknown_kind();
49
50 collection
51 .known_mut()
52 .entry(field.into_owned().into())
53 .or_insert(unknown_kind)
54 .insert_recursive(iter, kind);
55 }
56 BorrowedSegment::Index(mut index) => {
57 *self = Self::array(self.array.clone().unwrap_or_else(Collection::empty));
59 let collection = self.array.as_mut().expect("array was just inserted");
60
61 if index < 0 {
62 let largest_known_index = collection.largest_known_index();
63 let len_required = -index as usize;
65
66 let unknown_kind = collection.unknown_kind();
67 if unknown_kind.contains_any_defined() {
68 let min_length = collection.min_length();
70
71 if len_required > min_length {
72 let max_shifts = len_required - min_length;
77
78 let zero_shifts = collection.clone();
82 for shift_count in 1..=max_shifts {
83 let mut shifted_collection = zero_shifts.clone();
84 shifted_collection.known_mut().clear();
86
87 for i in 1..shift_count {
89 shifted_collection
90 .known_mut()
91 .insert(i.into(), Self::null());
92 }
93
94 for (i, i_kind) in zero_shifts.known() {
96 shifted_collection
97 .known_mut()
98 .insert(*i + shift_count, i_kind.clone());
99 }
100
101 collection.merge(shifted_collection, false);
103 }
104 }
105
106 let min_index = (min_length as isize + index).max(0) as usize;
108
109 debug_assert!(min_index == 0 || min_length >= len_required);
111
112 for i in 0..len_required {
115 collection
116 .known_mut()
117 .entry(i.into())
118 .or_insert_with(|| unknown_kind.clone())
119 .remove_undefined();
121 }
122 for (i, i_kind) in collection.known_mut() {
123 if i.to_usize() >= min_index {
125 let mut kind_with_insertion = i_kind.clone();
126 let remaining_path_segments = iter.clone().collect::<Vec<_>>();
127 kind_with_insertion
128 .insert(&remaining_path_segments, kind.clone());
129 *i_kind = i_kind.union(kind_with_insertion);
130 }
131 }
132
133 let mut unknown_kind_with_insertion = unknown_kind.clone();
134 let remaining_path_segments = iter.clone().collect::<Vec<_>>();
135 unknown_kind_with_insertion.insert(&remaining_path_segments, kind);
136 let mut new_unknown_kind = unknown_kind;
137 new_unknown_kind.merge_keep(unknown_kind_with_insertion, false);
138 collection.set_unknown(new_unknown_kind);
139
140 return;
141 }
142 debug_assert!(
143 collection.unknown_kind().is_undefined(),
144 "all cases with an unknown have been handled"
145 );
146
147 let exact_array_len =
149 largest_known_index.map_or(0, |max_index| max_index + 1);
150
151 if len_required > exact_array_len {
152 for i in exact_array_len..len_required {
154 collection.known_mut().insert(i.into(), Self::null());
156 }
157 }
158 index += (len_required as isize).max(exact_array_len as isize);
159 }
160
161 debug_assert!(index >= 0, "all negative cases have been handled");
162 let index = index as usize;
163
164 let index_exists = collection.known().contains_key(&index.into());
165 if !index_exists {
166 let hole_type = collection.unknown_kind().without_undefined().or_null();
169
170 for i in 0..index {
171 collection
172 .known_mut()
173 .entry(i.into())
174 .or_insert_with(|| hole_type.clone());
175 }
176 }
177
178 let unknown_kind = collection.unknown_kind();
179 collection
180 .known_mut()
181 .entry(index.into())
182 .or_insert(unknown_kind)
183 .insert_recursive(iter, kind);
184 }
185 BorrowedSegment::Invalid => { }
186 }
187 } else {
188 *self = kind;
189 }
190 }
191}
192
193#[cfg(test)]
194mod tests {
195 use std::collections::BTreeMap;
196
197 use crate::owned_value_path;
198 use crate::path::{OwnedValuePath, parse_value_path};
199 use crate::value::kind::Collection;
200
201 use super::*;
202
203 #[test]
204 #[allow(clippy::too_many_lines)]
205 fn test_insert() {
206 struct TestCase {
207 this: Kind,
208 path: OwnedValuePath,
209 kind: Kind,
210 expected: Kind,
211 }
212
213 for (
214 title,
215 TestCase {
216 mut this,
217 path,
218 kind,
219 expected,
220 },
221 ) in [
222 (
223 "root insert",
224 TestCase {
225 this: Kind::bytes(),
226 path: owned_value_path!(),
227 kind: Kind::integer(),
228 expected: Kind::integer(),
229 },
230 ),
231 (
232 "root insert object",
233 TestCase {
234 this: Kind::bytes(),
235 path: owned_value_path!(),
236 kind: Kind::object(BTreeMap::from([("a".into(), Kind::integer())])),
237 expected: Kind::object(BTreeMap::from([("a".into(), Kind::integer())])),
238 },
239 ),
240 (
241 "empty object insert field",
242 TestCase {
243 this: Kind::object(Collection::empty()),
244 path: owned_value_path!("a"),
245 kind: Kind::integer(),
246 expected: Kind::object(BTreeMap::from([("a".into(), Kind::integer())])),
247 },
248 ),
249 (
250 "non-empty object insert field",
251 TestCase {
252 this: Kind::object(BTreeMap::from([("b".into(), Kind::bytes())])),
253 path: owned_value_path!("a"),
254 kind: Kind::integer(),
255 expected: Kind::object(BTreeMap::from([
256 ("a".into(), Kind::integer()),
257 ("b".into(), Kind::bytes()),
258 ])),
259 },
260 ),
261 (
262 "object overwrite field",
263 TestCase {
264 this: Kind::object(BTreeMap::from([("a".into(), Kind::bytes())])),
265 path: owned_value_path!("a"),
266 kind: Kind::integer(),
267 expected: Kind::object(BTreeMap::from([("a".into(), Kind::integer())])),
268 },
269 ),
270 (
271 "set array index on empty array",
272 TestCase {
273 this: Kind::array(Collection::empty()),
274 path: owned_value_path!(0),
275 kind: Kind::integer(),
276 expected: Kind::array(BTreeMap::from([(0.into(), Kind::integer())])),
277 },
278 ),
279 (
280 "set array index past the end without unknown",
281 TestCase {
282 this: Kind::array(Collection::empty()),
283 path: owned_value_path!(1),
284 kind: Kind::integer(),
285 expected: Kind::array(BTreeMap::from([
286 (0.into(), Kind::null()),
287 (1.into(), Kind::integer()),
288 ])),
289 },
290 ),
291 (
292 "set array index past the end with unknown",
293 TestCase {
294 this: Kind::array(Collection::empty().with_unknown(Kind::integer())),
295 path: owned_value_path!(1),
296 kind: Kind::bytes(),
297 expected: Kind::array(
298 Collection::from(BTreeMap::from([
299 (0.into(), Kind::integer().or_null()),
300 (1.into(), Kind::bytes()),
301 ]))
302 .with_unknown(Kind::integer()),
303 ),
304 },
305 ),
306 (
307 "set array index past the end with unknown, nested",
308 TestCase {
309 this: Kind::array(Collection::empty().with_unknown(Kind::integer())),
310 path: owned_value_path!(1, "foo"),
311 kind: Kind::bytes(),
312 expected: Kind::array(
313 Collection::from(BTreeMap::from([
314 (0.into(), Kind::integer().or_null()),
315 (
316 1.into(),
317 Kind::object(BTreeMap::from([("foo".into(), Kind::bytes())])),
318 ),
319 ]))
320 .with_unknown(Kind::integer()),
321 ),
322 },
323 ),
324 (
325 "set array index past the end with null unknown",
326 TestCase {
327 this: Kind::array(Collection::empty().with_unknown(Kind::null())),
328 path: owned_value_path!(1),
329 kind: Kind::integer(),
330 expected: Kind::array(
331 Collection::from(BTreeMap::from([
332 (0.into(), Kind::null()),
333 (1.into(), Kind::integer()),
334 ]))
335 .with_unknown(Kind::null()),
336 ),
337 },
338 ),
339 (
340 "set field on non-object",
341 TestCase {
342 this: Kind::integer(),
343 path: owned_value_path!("a"),
344 kind: Kind::integer(),
345 expected: Kind::object(BTreeMap::from([("a".into(), Kind::integer())])),
346 },
347 ),
348 (
349 "set array index on non-array",
350 TestCase {
351 this: Kind::integer(),
352 path: owned_value_path!(0),
353 kind: Kind::integer(),
354 expected: Kind::array(BTreeMap::from([(0.into(), Kind::integer())])),
355 },
356 ),
357 (
358 "set negative array index (no unknown)",
359 TestCase {
360 this: Kind::array(BTreeMap::from([
361 (0.into(), Kind::integer()),
362 (1.into(), Kind::integer()),
363 ])),
364 path: owned_value_path!(-1),
365 kind: Kind::bytes(),
366 expected: Kind::array(BTreeMap::from([
367 (0.into(), Kind::integer()),
368 (1.into(), Kind::bytes()),
369 ])),
370 },
371 ),
372 (
373 "set negative array index past the end (no unknown)",
374 TestCase {
375 this: Kind::array(BTreeMap::from([(0.into(), Kind::integer())])),
376 path: owned_value_path!(-2),
377 kind: Kind::bytes(),
378 expected: Kind::array(BTreeMap::from([
379 (0.into(), Kind::bytes()),
380 (1.into(), Kind::null()),
381 ])),
382 },
383 ),
384 (
385 "set negative array index size 1 unknown array",
386 TestCase {
387 this: Kind::array(
388 Collection::from(BTreeMap::from([(0.into(), Kind::integer())]))
389 .with_unknown(Kind::integer()),
390 ),
391 path: owned_value_path!(-1),
392 kind: Kind::bytes(),
393 expected: Kind::array(
394 Collection::from(BTreeMap::from([(0.into(), Kind::bytes().or_integer())]))
395 .with_unknown(Kind::integer().or_bytes().or_undefined()),
396 ),
397 },
398 ),
399 (
400 "set negative array index empty unknown array",
401 TestCase {
402 this: Kind::array(Collection::empty().with_unknown(Kind::integer())),
403 path: owned_value_path!(-1),
404 kind: Kind::bytes(),
405 expected: Kind::array(
406 Collection::from(BTreeMap::from([
407 (0.into(), Kind::bytes().or_integer()),
409 ]))
410 .with_unknown(Kind::integer().or_bytes().or_undefined()),
411 ),
412 },
413 ),
414 (
415 "set negative array index empty unknown array (2)",
416 TestCase {
417 this: Kind::array(Collection::empty().with_unknown(Kind::integer())),
418 path: owned_value_path!(-2),
419 kind: Kind::bytes(),
420 expected: Kind::array(
421 Collection::from(BTreeMap::from([
422 (0.into(), Kind::integer().or_bytes()),
423 (1.into(), Kind::integer().or_bytes().or_null()),
427 ]))
428 .with_unknown(Kind::integer().or_bytes().or_undefined()),
429 ),
430 },
431 ),
432 (
433 "set negative array index unknown array",
434 TestCase {
435 this: Kind::array(
436 Collection::from(BTreeMap::from([
437 (0.into(), Kind::integer()),
439 (1.into(), Kind::float()),
440 ]))
441 .with_unknown(Kind::integer()),
442 ),
443 path: owned_value_path!(-3),
444 kind: Kind::bytes(),
445 expected: Kind::array(
446 Collection::from(BTreeMap::from([
447 (0.into(), Kind::integer().or_bytes()),
449 (1.into(), Kind::float().or_bytes().or_integer()),
452 (2.into(), Kind::float().or_bytes().or_integer()),
453 ]))
454 .with_unknown(Kind::integer().or_bytes().or_undefined()),
455 ),
456 },
457 ),
458 (
459 "set negative array index unknown array no holes",
460 TestCase {
461 this: Kind::array(
462 Collection::from(BTreeMap::from([
463 (0.into(), Kind::float()),
464 (1.into(), Kind::float()),
465 (2.into(), Kind::float()),
466 ]))
467 .with_unknown(Kind::integer()),
468 ),
469 path: owned_value_path!(-3),
470 kind: Kind::bytes(),
471 expected: Kind::array(
472 Collection::from(BTreeMap::from([
473 (0.into(), Kind::float().or_bytes()),
474 (1.into(), Kind::float().or_bytes()),
475 (2.into(), Kind::float().or_bytes()),
476 ]))
477 .with_unknown(Kind::integer().or_bytes().or_undefined()),
478 ),
479 },
480 ),
481 (
482 "set negative array index on non-array",
483 TestCase {
484 this: Kind::integer(),
485 path: owned_value_path!(-3),
486 kind: Kind::bytes(),
487 expected: Kind::array(Collection::from(BTreeMap::from([
488 (0.into(), Kind::bytes()),
489 (1.into(), Kind::null()),
490 (2.into(), Kind::null()),
491 ]))),
492 },
493 ),
494 (
495 "set nested negative array index on unknown array",
496 TestCase {
497 this: Kind::array(Collection::empty().with_unknown(Kind::integer())),
498 path: owned_value_path!(-3, "foo"),
499 kind: Kind::bytes(),
500 expected: Kind::array(
501 Collection::from(BTreeMap::from([
502 (
503 0.into(),
504 Kind::integer()
505 .or_object(BTreeMap::from([("foo".into(), Kind::bytes())])),
506 ),
507 (
508 1.into(),
509 Kind::integer()
510 .or_null()
511 .or_object(BTreeMap::from([("foo".into(), Kind::bytes())])),
512 ),
513 (
514 2.into(),
515 Kind::integer()
516 .or_null()
517 .or_object(BTreeMap::from([("foo".into(), Kind::bytes())])),
518 ),
519 ]))
520 .with_unknown(
521 Kind::integer()
522 .or_undefined()
523 .or_object(BTreeMap::from([("foo".into(), Kind::bytes())])),
524 ),
525 ),
526 },
527 ),
528 (
529 "set nested negative array index on unknown array (no holes)",
530 TestCase {
531 this: Kind::array(
532 Collection::from(BTreeMap::from([(0.into(), Kind::integer())]))
533 .with_unknown(Kind::integer()),
534 ),
535 path: owned_value_path!(-1, "foo"),
536 kind: Kind::bytes(),
537 expected: Kind::array(
538 Collection::from(BTreeMap::from([(
539 0.into(),
540 Kind::integer()
541 .or_object(BTreeMap::from([("foo".into(), Kind::bytes())])),
542 )]))
543 .with_unknown(
544 Kind::integer()
545 .or_undefined()
546 .or_object(BTreeMap::from([("foo".into(), Kind::bytes())])),
547 ),
548 ),
549 },
550 ),
551 (
552 "insert into never",
553 TestCase {
554 this: Kind::never(),
555 path: parse_value_path(".").unwrap(),
556 kind: Kind::bytes(),
557 expected: Kind::bytes(),
558 },
559 ),
560 (
561 "insert never",
562 TestCase {
563 this: Kind::object(Collection::empty()),
564 path: parse_value_path(".x").unwrap(),
565 kind: Kind::never(),
566 expected: Kind::object(Collection::empty()),
567 },
568 ),
569 (
570 "insert undefined",
571 TestCase {
572 this: Kind::object(Collection::empty()),
573 path: parse_value_path(".x").unwrap(),
574 kind: Kind::undefined(),
575 expected: Kind::object(BTreeMap::from([("x".into(), Kind::null())])),
576 },
577 ),
578 (
579 "array insert into any",
580 TestCase {
581 this: Kind::any(),
582 path: owned_value_path!(2),
583 kind: Kind::bytes(),
584 expected: Kind::array(
585 Collection::from(BTreeMap::from([
586 (0.into(), Kind::any().without_undefined()),
587 (1.into(), Kind::any().without_undefined()),
588 (2.into(), Kind::bytes()),
589 ]))
590 .with_unknown(Kind::any()),
591 ),
592 },
593 ),
594 (
595 "object insert into any",
596 TestCase {
597 this: Kind::any(),
598 path: owned_value_path!("b"),
599 kind: Kind::bytes(),
600 expected: Kind::object(
601 Collection::from(BTreeMap::from([("b".into(), Kind::bytes())]))
602 .with_unknown(Kind::any()),
603 ),
604 },
605 ),
606 (
607 "nested object/array insert into any",
608 TestCase {
609 this: Kind::any(),
610 path: owned_value_path!("x", 2),
611 kind: Kind::bytes(),
612 expected: Kind::object(
613 Collection::from(BTreeMap::from([(
614 "x".into(),
615 Kind::array(
616 Collection::from(BTreeMap::from([
617 (0.into(), Kind::any().without_undefined()),
618 (1.into(), Kind::any().without_undefined()),
619 (2.into(), Kind::bytes()),
620 ]))
621 .with_unknown(Kind::any()),
622 ),
623 )]))
624 .with_unknown(Kind::any()),
625 ),
626 },
627 ),
628 (
629 "nested array/array insert into any",
630 TestCase {
631 this: Kind::any(),
632 path: owned_value_path!(0, 0),
633 kind: Kind::bytes(),
634 expected: Kind::array(
635 Collection::from(BTreeMap::from([(
636 0.into(),
637 Kind::array(
638 Collection::from(BTreeMap::from([(0.into(), Kind::bytes())]))
639 .with_unknown(Kind::any()),
640 ),
641 )]))
642 .with_unknown(Kind::any()),
643 ),
644 },
645 ),
646 ] {
647 this.insert(&path, kind);
648 assert_eq!(this, expected, "{title}");
649 }
650 }
651}