1use super::rules::{self, Shape};
20use crate::base::{Error, ErrorKind, Result};
21use datavalue::OwnedDataValue;
22use std::fmt;
23use std::str::FromStr;
24use std::sync::Arc;
25
26pub(crate) const MAX_PATH_SEGMENTS: usize = 64;
29
30#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
32#[non_exhaustive]
33pub enum Root {
34 Data,
36 Metadata,
38 TempData,
40}
41
42impl Root {
43 fn row(self) -> usize {
46 match self {
47 Self::Data => 0,
48 Self::Metadata => 1,
49 Self::TempData => 2,
50 }
51 }
52
53 fn from_row(row: usize) -> Option<Self> {
55 match row {
56 0 => Some(Self::Data),
57 1 => Some(Self::Metadata),
58 2 => Some(Self::TempData),
59 _ => None,
60 }
61 }
62
63 pub fn as_str(self) -> &'static str {
66 crate::base::ROOTS[self.row()]
67 }
68}
69
70#[derive(Clone, Debug, PartialEq, Eq)]
76pub(crate) struct Seg {
77 pub(crate) key: Box<str>,
79 pub(crate) index: Option<usize>,
81 pub(crate) hash: u32,
85}
86
87pub(crate) fn key_hash(key: &str) -> u32 {
89 key.bytes().fold(0x811c_9dc5, |h, b| {
90 (h ^ u32::from(b)).wrapping_mul(0x0100_0193)
91 })
92}
93
94impl Seg {
95 pub(crate) fn parse(raw: &str) -> Self {
96 let key = seg_key(raw);
97 Self {
98 key: key.into(),
99 index: seg_index(raw),
100 hash: key_hash(key),
101 }
102 }
103
104 #[cfg(test)]
106 pub(crate) fn key(key: &str) -> Self {
107 Self {
108 key: key.into(),
109 index: None,
110 hash: key_hash(key),
111 }
112 }
113
114 pub(crate) fn index(i: usize) -> Self {
117 debug_assert!(i <= MAX_ARRAY_INDEX, "index {i} is past the cap");
118 let key = i.to_string();
119 Self {
120 hash: key_hash(&key),
121 key: key.into(),
122 index: Some(i),
123 }
124 }
125
126 pub(crate) fn set_index(&mut self, i: usize) {
130 debug_assert!(i <= MAX_ARRAY_INDEX, "index {i} is past the cap");
131 self.index = Some(i);
132 }
133}
134
135fn seg_key(raw: &str) -> &str {
137 raw.strip_prefix('#').unwrap_or(raw)
138}
139
140pub const MAX_ARRAY_INDEX: usize = (1 << 20) - 1;
146
147fn seg_index(raw: &str) -> Option<usize> {
151 raw.parse().ok().filter(|&i| i <= MAX_ARRAY_INDEX)
152}
153
154#[derive(Clone, PartialEq, Eq)]
161pub struct Path {
162 root: Root,
163 segs: Arc<[Seg]>,
167 dotted: Arc<str>,
168}
169
170impl FromStr for Path {
171 type Err = Error;
172
173 fn from_str(dotted: &str) -> Result<Self> {
174 let (first, rest) = match dotted.split_once('.') {
175 Some((first, rest)) => (first, Some(rest)),
176 None => (dotted, None),
177 };
178 let root = match crate::base::ROOTS
179 .iter()
180 .position(|root| *root == first)
181 .and_then(Root::from_row)
182 {
183 Some(root) => root,
184 None => {
185 return Err(Error::new(
186 ErrorKind::Config,
187 format!("path `{dotted}` must start at `data`, `metadata` or `temp_data`"),
188 ));
189 }
190 };
191 let mut segs = vec![Seg::parse(first)];
192 if let Some(rest) = rest {
193 segs.extend(rest.split('.').map(Seg::parse));
194 }
195 if segs.len() > MAX_PATH_SEGMENTS {
196 return Err(Error::new(
197 ErrorKind::Config,
198 format!(
199 "path `{dotted}` has {} segments; a path holds at most {MAX_PATH_SEGMENTS}",
200 segs.len()
201 ),
202 ));
203 }
204 Ok(Self {
205 root,
206 segs: segs.into(),
207 dotted: Arc::from(dotted),
208 })
209 }
210}
211
212impl fmt::Display for Path {
213 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
214 f.write_str(&self.dotted)
215 }
216}
217
218impl fmt::Debug for Path {
219 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
222 f.debug_tuple("Path").field(&&*self.dotted).finish()
223 }
224}
225
226impl Path {
227 pub fn root(&self) -> Root {
229 self.root
230 }
231
232 pub fn is_root(&self) -> bool {
234 self.segs.len() == 1
235 }
236
237 pub(crate) fn segs(&self) -> &[Seg] {
239 &self.segs
240 }
241
242 pub(crate) fn dotted(&self) -> &Arc<str> {
244 &self.dotted
245 }
246
247 #[cfg(test)]
252 pub(crate) fn index_child(&self, index: usize) -> Option<Self> {
253 if self.segs.len() >= MAX_PATH_SEGMENTS {
254 return None;
255 }
256 let segs = self.segs.iter().cloned().chain([Seg::index(index)]);
257 Some(Self {
258 root: self.root,
259 segs: segs.collect(),
260 dotted: Arc::from(format!("{}.{index}", self.dotted)),
261 })
262 }
263
264 pub(crate) fn prefix(&self, len: usize) -> Self {
267 debug_assert!((1..=self.segs.len()).contains(&len), "a prefix of {self}");
268 let end = self
269 .dotted
270 .match_indices('.')
271 .nth(len - 1)
272 .map_or(self.dotted.len(), |(at, _)| at);
273 Self {
274 root: self.root,
275 segs: Arc::from(&self.segs[..len]),
276 dotted: Arc::from(&self.dotted[..end]),
277 }
278 }
279
280 pub fn get<'v>(&self, context: &'v OwnedDataValue) -> Option<&'v OwnedDataValue> {
282 get(context, &self.segs)
283 }
284
285 pub fn set(&self, context: &mut OwnedDataValue, value: OwnedDataValue) -> Result<()> {
288 self.check_set(&value)?;
289 self.assign(context, value);
290 Ok(())
291 }
292
293 pub fn remove(&self, context: &mut OwnedDataValue) -> Result<Option<OwnedDataValue>> {
295 self.check_remove()?;
296 Ok(remove(context, &self.segs))
297 }
298
299 pub(crate) fn check_set(&self, value: &OwnedDataValue) -> Result<()> {
301 if self.is_root() && !matches!(value, OwnedDataValue::Object(_)) {
302 return Err(Error::new(
303 ErrorKind::Config,
304 format!(
305 "`{}` is a root and takes only an object, not {}",
306 self.dotted,
307 kind(value)
308 ),
309 ));
310 }
311 Ok(())
312 }
313
314 pub(crate) fn check_remove(&self) -> Result<()> {
316 if self.is_root() {
317 return Err(Error::new(
318 ErrorKind::Config,
319 format!("`{}` is a root and cannot be removed", self.dotted),
320 ));
321 }
322 Ok(())
323 }
324
325 pub(crate) fn assign(&self, context: &mut OwnedDataValue, value: OwnedDataValue) -> bool {
327 set(context, &self.segs, value)
328 }
329
330 #[cfg(test)]
333 pub(crate) fn append(
334 &self,
335 context: &mut OwnedDataValue,
336 items: Vec<OwnedDataValue>,
337 ) -> std::result::Result<bool, &'static str> {
338 append(context, &self.segs, items)
339 }
340}
341
342fn empty(next: &Seg) -> OwnedDataValue {
345 if next.index.is_some() {
346 OwnedDataValue::Array(Vec::new())
347 } else {
348 OwnedDataValue::Object(Vec::new())
349 }
350}
351
352pub(crate) fn chain(segs: &[Seg], value: OwnedDataValue) -> OwnedDataValue {
356 let Some(first) = segs.first() else {
357 return value;
358 };
359 let mut container = empty(first);
360 set(&mut container, segs, value);
361 container
362}
363
364fn hop<'v>(v: &'v OwnedDataValue, key: &str, index: Option<usize>) -> Option<&'v OwnedDataValue> {
366 match v {
367 OwnedDataValue::Object(pairs) => pairs.iter().find(|(k, _)| k == key).map(|(_, v)| v),
368 OwnedDataValue::Array(items) => items.get(index?),
369 _ => None,
370 }
371}
372
373pub(crate) fn get<'v>(v: &'v OwnedDataValue, segs: &[Seg]) -> Option<&'v OwnedDataValue> {
374 segs.iter()
375 .try_fold(v, |v, seg| hop(v, &seg.key, seg.index))
376}
377
378pub(crate) fn get_dotted<'v>(v: &'v OwnedDataValue, dotted: &str) -> Option<&'v OwnedDataValue> {
381 dotted
382 .split('.')
383 .try_fold(v, |v, raw| hop(v, seg_key(raw), seg_index(raw)))
384}
385
386pub(crate) fn get_mut<'v>(
388 mut v: &'v mut OwnedDataValue,
389 segs: &[Seg],
390) -> Option<&'v mut OwnedDataValue> {
391 for seg in segs {
392 v = match v {
393 OwnedDataValue::Object(pairs) => {
394 &mut pairs.iter_mut().find(|(k, _)| **k == *seg.key)?.1
395 }
396 OwnedDataValue::Array(items) => items.get_mut(seg.index?)?,
397 _ => return None,
398 };
399 }
400 Some(v)
401}
402
403pub(crate) fn set(v: &mut OwnedDataValue, segs: &[Seg], value: OwnedDataValue) -> bool {
405 let Some((last, parents)) = segs.split_last() else {
406 return false;
407 };
408 let mut current = v;
409 for (i, seg) in parents.iter().enumerate() {
410 let next = &segs[i + 1];
411 current = match current {
412 OwnedDataValue::Object(pairs) => {
413 let at = match pairs.iter().position(|(k, _)| **k == *seg.key) {
414 Some(at) => at,
415 None => {
416 pairs.push((String::from(&*seg.key), empty(next)));
419 pairs.len() - 1
420 }
421 };
422 &mut pairs[at].1
423 }
424 OwnedDataValue::Array(items) => {
425 let Some(at) = seg.index else {
426 return false;
427 };
428 if items.len() <= at {
429 items.resize(at + 1, OwnedDataValue::Null);
430 }
431 if items[at].is_null() {
432 items[at] = empty(next);
433 }
434 &mut items[at]
435 }
436 _ => return false,
437 };
438 }
439 match current {
440 OwnedDataValue::Object(pairs) => {
441 match pairs.iter_mut().find(|(k, _)| **k == *last.key) {
442 Some(slot) => slot.1 = value,
443 None => pairs.push((String::from(&*last.key), value)),
444 }
445 true
446 }
447 OwnedDataValue::Array(items) => {
448 let Some(at) = last.index else {
449 return false;
450 };
451 if items.len() <= at {
452 items.resize(at + 1, OwnedDataValue::Null);
453 }
454 items[at] = value;
455 true
456 }
457 _ => false,
458 }
459}
460
461pub(crate) fn remove(v: &mut OwnedDataValue, segs: &[Seg]) -> Option<OwnedDataValue> {
464 let (last, parents) = segs.split_last()?;
465 let parent = get_mut(v, parents)?;
466 match parent {
467 OwnedDataValue::Object(pairs) => {
468 let at = pairs.iter().position(|(k, _)| **k == *last.key)?;
469 Some(pairs.remove(at).1)
470 }
471 OwnedDataValue::Array(items) => {
472 let at = last.index?;
473 (at < items.len()).then(|| items.remove(at))
474 }
475 _ => None,
476 }
477}
478
479pub(crate) fn kind(v: &OwnedDataValue) -> &'static str {
481 Shape::of_owned(v).describe()
482}
483
484pub(crate) fn append(
489 v: &mut OwnedDataValue,
490 segs: &[Seg],
491 items: Vec<OwnedDataValue>,
492) -> std::result::Result<bool, &'static str> {
493 let target = get_mut(v, segs);
494 if rules::append_to(target.as_deref().map(Shape::of_owned))? {
495 if let Some(OwnedDataValue::Array(existing)) = target {
496 existing.extend(items);
497 }
498 return Ok(true);
499 }
500 Ok(set(v, segs, OwnedDataValue::Array(items)))
501}
502
503pub(crate) fn merge_root(context: &mut OwnedDataValue, key: &str, value: OwnedDataValue) {
508 if !matches!(context, OwnedDataValue::Object(_)) {
509 *context = OwnedDataValue::Object(vec![(key.to_string(), value)]);
510 return;
511 }
512 match (rules::owned_child_mut(context, rules::Hop::Key(key)), value) {
513 (Some(slot @ OwnedDataValue::Object(_)), OwnedDataValue::Object(new_pairs)) => {
514 for (k, v) in new_pairs {
515 rules::put(slot, rules::Hop::Key(&k), v);
516 }
517 }
518 (_, value) => {
519 rules::put(context, rules::Hop::Key(key), value);
520 }
521 }
522}
523
524#[cfg(test)]
525mod tests {
526 use super::*;
527
528 fn json(text: &str) -> OwnedDataValue {
529 serde_json::from_str(text).unwrap()
530 }
531
532 fn text(v: &OwnedDataValue) -> String {
533 serde_json::to_string(v).unwrap()
534 }
535
536 fn path(s: &str) -> Path {
537 s.parse().unwrap()
538 }
539
540 #[test]
541 fn a_path_parses_to_its_root_and_displays_back() {
542 let p = path("data.rows.#20.total");
543 assert_eq!(p.root(), Root::Data);
544 assert!(!p.is_root());
545 assert_eq!(p.to_string(), "data.rows.#20.total");
546 assert_eq!(path("temp_data.x").root(), Root::TempData);
547 assert_eq!(path("metadata.x").root().as_str(), "metadata");
548 }
549
550 #[test]
551 fn what_is_not_a_context_root_is_refused() {
552 for bad in ["", "rows.x", "#data.x", "payload", "payload.x", "Data.x"] {
553 let error = bad.parse::<Path>().unwrap_err();
554 assert_eq!(error.kind(), &ErrorKind::Config, "{bad}");
555 }
556 }
557
558 #[test]
559 fn a_bare_root_is_a_root_path() {
560 assert!(path("data").is_root());
561 assert!(path("temp_data").is_root());
562 }
563
564 #[test]
565 fn empty_segments_below_the_root_are_empty_keys() {
566 let mut ctx = json(r#"{"data":{}}"#);
567 path("data..x").set(&mut ctx, json("1")).unwrap();
568 assert_eq!(text(&ctx), r#"{"data":{"":{"x":1}}}"#);
569 }
570
571 #[test]
572 fn set_at_a_root_replaces_with_an_object_and_refuses_anything_else() {
573 let mut ctx = json(r#"{"data":{"a":1},"metadata":{}}"#);
574 path("data").set(&mut ctx, json(r#"{"b":2}"#)).unwrap();
575 assert_eq!(text(&ctx), r#"{"data":{"b":2},"metadata":{}}"#);
576 let error = path("data").set(&mut ctx, json(r#""s""#)).unwrap_err();
577 assert_eq!(error.kind(), &ErrorKind::Config);
578 assert_eq!(text(&ctx), r#"{"data":{"b":2},"metadata":{}}"#);
579 }
580
581 #[test]
582 fn remove_at_a_root_is_refused() {
583 let mut ctx = json(r#"{"data":{}}"#);
584 let error = path("data").remove(&mut ctx).unwrap_err();
585 assert_eq!(error.kind(), &ErrorKind::Config);
586 }
587
588 #[test]
589 fn a_hop_that_cannot_continue_writes_nothing_and_is_ok() {
590 let mut ctx = json(r#"{"data":{"a":5}}"#);
591 path("data.a.b").set(&mut ctx, json("1")).unwrap();
592 assert_eq!(text(&ctx), r#"{"data":{"a":5}}"#);
593 assert!(!path("data.a.b").assign(&mut ctx, json("1")));
594 }
595
596 #[test]
597 fn get_set_remove_work_below_a_root() {
598 let mut ctx = json(r#"{"data":{"rows":[{"t":1}]},"metadata":{}}"#);
599 assert_eq!(text(path("data.rows.0.t").get(&ctx).unwrap()), "1");
600 path("temp_data.x.y").set(&mut ctx, json("2")).unwrap();
601 assert_eq!(
602 text(&ctx),
603 r#"{"data":{"rows":[{"t":1}]},"metadata":{},"temp_data":{"x":{"y":2}}}"#
604 );
605 let gone = path("data.rows.0.t").remove(&mut ctx).unwrap();
606 assert_eq!(gone.map(|v| text(&v)), Some("1".into()));
607 }
608
609 #[test]
610 fn a_dotted_read_follows_the_parsed_segments_rules() {
611 let v = json(r##"{"a":{"20":"key","#b":"x","b":"hash","rows":[{"t":1},{"t":2}]}}"##);
612 for dotted in [
613 "a.20",
614 "a.#20",
615 "a.b",
616 "a.#b",
617 "a.##b",
618 "a.rows.1.t",
619 "a.rows.#1",
620 "a.rows.x",
621 "a",
622 "a.",
623 "",
624 "zz",
625 ] {
626 let segs: Vec<Seg> = dotted.split('.').map(Seg::parse).collect();
627 assert_eq!(
628 get_dotted(&v, dotted).map(text),
629 get(&v, &segs).map(text),
630 "{dotted}"
631 );
632 }
633 assert_eq!(get_dotted(&v, "a.#b").map(text), Some(r#""hash""#.into()));
634 assert_eq!(get_dotted(&v, "a.rows.#1"), None);
635 }
636
637 #[test]
638 fn a_path_holds_at_most_64_segments() {
639 let deep = |n: usize| format!("data{}", ".k".repeat(n - 1));
640 let ok: Path = deep(64).parse().expect("64 segments parse");
641 assert_eq!(ok.segs().len(), 64);
642 let err = deep(65)
643 .parse::<Path>()
644 .expect_err("65 segments are refused");
645 assert_eq!(*err.kind(), ErrorKind::Config);
646 assert!(err.to_string().contains("at most 64"), "{err}");
647 }
648
649 #[test]
650 fn an_index_stops_at_the_cap_and_a_larger_number_is_a_key() {
651 let at_cap = path(&format!("data.list.{MAX_ARRAY_INDEX}"));
652 assert_eq!(at_cap.segs().last().unwrap().index, Some(MAX_ARRAY_INDEX));
653 let past = path(&format!("data.list.{}", MAX_ARRAY_INDEX + 1));
654 assert_eq!(past.segs().last().unwrap().index, None);
655 assert_eq!(&*past.segs().last().unwrap().key, "1048576");
656
657 let mut ctx = json(r#"{"data":{"list":[]}}"#);
660 assert!(!path("data.list.4294967295").assign(&mut ctx, json("7")));
661 assert!(!path("data.list.3000000000").assign(&mut ctx, json("7")));
662 assert_eq!(text(&ctx), r#"{"data":{"list":[]}}"#);
663 assert!(path("data.list.2").assign(&mut ctx, json("7")));
664 assert_eq!(text(&ctx), r#"{"data":{"list":[null,null,7]}}"#);
665 assert!(path("data.obj.4294967295").assign(&mut ctx, json("1")));
667 assert_eq!(
668 get_dotted(&ctx, "data.obj.4294967295").map(text),
669 Some("1".into())
670 );
671 }
672
673 #[test]
674 fn an_index_child_is_its_dotted_spelling_parsed() {
675 for (parent, index) in [("data.rs", 0), ("temp_data.a.#b.2", 17), ("metadata", 3)] {
676 let child = path(parent).index_child(index).expect("room for the index");
677 assert_eq!(child, path(&format!("{parent}.{index}")));
678 assert_eq!(child.segs().last().unwrap().index, Some(index));
679 }
680 let deep = format!("data{}", ".k".repeat(63));
682 assert!(path(&deep).index_child(0).is_none());
683 let roomy = format!("data{}", ".k".repeat(62));
684 assert_eq!(path(&roomy).index_child(0).unwrap().segs().len(), 64);
685 }
686
687 #[test]
688 fn append_extends_or_creates() {
689 let mut ctx = json(r#"{"data":{"a":[1]}}"#);
690 assert_eq!(path("data.a").append(&mut ctx, vec![json("2")]), Ok(true));
691 assert_eq!(path("data.b").append(&mut ctx, vec![json("3")]), Ok(true));
692 assert_eq!(text(&ctx), r#"{"data":{"a":[1,2],"b":[3]}}"#);
693 path("data.s").set(&mut ctx, json(r#""x""#)).unwrap();
694 assert_eq!(path("data.s").append(&mut ctx, vec![]), Err("a string"));
695 }
696
697 #[test]
698 fn merge_root_merges_objects_and_replaces_the_rest() {
699 let mut ctx = json(r#"{"data":{"a":1,"b":2}}"#);
700 merge_root(&mut ctx, "data", json(r#"{"b":3,"c":4}"#));
701 assert_eq!(text(&ctx), r#"{"data":{"a":1,"b":3,"c":4}}"#);
702 merge_root(&mut ctx, "data", json("5"));
703 merge_root(&mut ctx, "metadata", json("{}"));
704 assert_eq!(text(&ctx), r#"{"data":5,"metadata":{}}"#);
705 }
706
707 #[test]
708 fn chain_builds_the_containers_a_write_would_create() {
709 let segs = &path("data.a.0").segs[1..];
710 assert_eq!(text(&chain(segs, json("1"))), r#"{"a":[1]}"#);
711 assert_eq!(text(&chain(&[], json("1"))), "1");
712 }
713}