Skip to main content

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}