simple_update_in/path.rs
1//! Path accessors.
2//!
3//! A path is a list of accessors that describe how to walk into a value. Each
4//! step is a string key, a numeric index, or a predicate that selects matching
5//! children.
6
7use crate::value::Value;
8
9/// What a predicate sees as the second argument: a map key or an array index.
10///
11/// A predicate is called with the child value and its location. Maps pass a
12/// string key. Arrays pass a numeric index. The selector owns its key, so a
13/// closure can match it without fighting a borrowed `&&str`.
14#[derive(Clone, Debug, PartialEq, Eq)]
15pub enum Selector {
16 /// A map key.
17 Key(String),
18 /// An array index.
19 Index(usize),
20}
21
22impl Selector {
23 /// The key, if this selector is a map key.
24 #[must_use]
25 pub fn as_key(&self) -> Option<&str> {
26 match self {
27 Selector::Key(k) => Some(k),
28 Selector::Index(_) => None,
29 }
30 }
31
32 /// The index, if this selector is an array index.
33 #[must_use]
34 pub fn as_index(&self) -> Option<usize> {
35 match self {
36 Selector::Index(i) => Some(*i),
37 Selector::Key(_) => None,
38 }
39 }
40}
41
42/// A boxed predicate: `(value, key|index) -> bool`.
43pub type Predicate = Box<dyn Fn(&Value, &Selector) -> bool>;
44
45/// One step in a path.
46pub enum Accessor {
47 /// Address a map key. Drives map coercion.
48 Key(String),
49 /// Address an array index. Drives array coercion.
50 Index(usize),
51 /// Select matching children. Branches the update across every match.
52 Predicate(Predicate),
53}
54
55impl Accessor {
56 /// Build a key accessor from anything string-like.
57 #[must_use]
58 pub fn key(k: impl Into<String>) -> Accessor {
59 Accessor::Key(k.into())
60 }
61
62 /// Build an index accessor.
63 #[must_use]
64 pub fn index(i: usize) -> Accessor {
65 Accessor::Index(i)
66 }
67
68 /// Build a predicate accessor from a closure.
69 #[must_use]
70 pub fn predicate<F>(f: F) -> Accessor
71 where
72 F: Fn(&Value, &Selector) -> bool + 'static,
73 {
74 Accessor::Predicate(Box::new(f))
75 }
76}
77
78impl std::fmt::Debug for Accessor {
79 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
80 match self {
81 Accessor::Key(k) => write!(f, "Key({k:?})"),
82 Accessor::Index(i) => write!(f, "Index({i})"),
83 Accessor::Predicate(_) => write!(f, "Predicate(..)"),
84 }
85 }
86}
87
88/// A resolved step: a key or an index, never a predicate.
89///
90/// Path resolution turns predicates into the concrete keys and indices they
91/// matched. The write phase walks these. This is an internal type. Callers
92/// build [`Accessor`] values and never see a `Step`.
93#[derive(Clone, Debug, PartialEq, Eq)]
94pub(crate) enum Step {
95 /// A concrete map key.
96 Key(String),
97 /// A concrete array index.
98 Index(usize),
99}