Skip to main content

amethystate_core/
path.rs

1use std::borrow::Cow;
2use std::cmp::Ordering;
3use std::fmt::{self, Write as _};
4use std::hash::{Hash, Hasher};
5use std::sync::{Arc, OnceLock};
6
7/// A level's name as a path holds it.
8///
9/// Re-exported because it is part of what [`StorePath::push_shared`] takes
10/// and what a document engine hands a scan: a caller naming that type should
11/// not have to agree with this crate about a dependency to do it. Inline up to
12/// 23 bytes, which is most level names, so holding one allocates nothing.
13pub use smallvec::SmallVec;
14pub use smol_str::SmolStr;
15
16/// What divides one level from the next in a stored key.
17///
18/// Public because a backend that queries by comparison has to reason about it:
19/// the upper bound of a subtree's range is this character's successor, and
20/// spelling it out here is what makes every backend compute the same bound.
21pub const SEPARATOR: char = '.';
22
23pub(crate) const ESCAPE: char = '\\';
24
25/// Where a value lives in the store, as the levels it is under.
26///
27/// A path is built from segments and only from segments, so a name is a name:
28/// putting `"dark.mode"` in one addresses a single value with a dot in it, not
29/// two levels.
30///
31/// Two forms answer the two kinds of engine, and a path holds whichever one it
32/// arrived in. A flat engine keeps a key whole and addresses it by the joined
33/// spelling; a document engine keeps a level at a time and addresses it by the
34/// levels. The other form is built on the first reader that wants it and kept,
35/// so a path costs one of the two, and a scan that never asks for the other
36/// never pays for it.
37///
38/// Which is not a detail: both directions are taken per key of every scan.
39/// Splitting a key allocates for the list of levels; spelling levels out walks
40/// them for a separator and an escape and builds a string. Whichever a caller
41/// does not read is the whole of that cost.
42#[derive(Clone)]
43pub struct StorePath {
44    held: Held,
45}
46
47#[derive(Clone)]
48enum Held {
49    /// Written out where the code is compiled, so both forms are ready.
50    Written {
51        levels: &'static [&'static str],
52        joined: &'static str,
53    },
54
55    /// Built from its levels, which is how a document engine reads one back.
56    ///
57    /// A level comes back borrowed. The spelling is not there until something
58    /// asks - equality, order and hashing all answer from it, and so does any
59    /// flat engine - and then it is kept, and every copy of the path made
60    /// before or after shares it.
61    ///
62    /// Shared rather than held inline because a `StorePath` written into a
63    /// `const` is borrowed for the whole program, and nothing borrowed that way
64    /// may carry a cell. Behind the `Arc` the cell is reached through a pointer,
65    /// which is what the macro's declared paths need it to be.
66    Levels(Arc<Levels>),
67
68    /// Read as one key, which is how a flat engine reads one back.
69    ///
70    /// The levels are not there until something walks them, and then they are
71    /// walked out of the string and carried away by nobody. A level comes back
72    /// as a `Cow` for that reason: one holding an escaped separator is not a
73    /// run of the joined form and has to be assembled, and one without is
74    /// borrowed from it.
75    Joined { joined: SmolStr },
76
77    /// The first `levels` of another path's, sharing the list they came from.
78    ///
79    /// What [`StorePath::parent`] is for. A path's ancestors are its own levels
80    /// with the end taken off, so copying the list to shorten it is copying a
81    /// thing to keep most of it - and the shortening is asked for in loops, one
82    /// ancestor after another, where every copy is thrown away after a
83    /// comparison. Here the list is not copied at all: the count says where to
84    /// stop reading it.
85    ///
86    /// The spelling is the parent's, cut at the separator that ends the last
87    /// level kept - a run of a string the `Arc` already owns, so there is
88    /// nothing to store and no cell to carry.
89    Prefix { of: Arc<Levels>, levels: usize },
90}
91
92/// The levels of a path, and the spelling once somebody has asked for it.
93///
94/// Both halves are [`SmolStr`], which holds a string of 23 bytes or fewer
95/// inline and allocates nothing for it. That is most level names, and a good
96/// share of whole paths; anything longer goes to the heap behind a count, which
97/// is what it would have cost anyway. The level list is copied whole every time
98/// a path grows one - `push`, `join`, `parent` and `strip_prefix` all go through
99/// [`StorePath::own_levels`] - so the copies are where this is felt.
100struct Levels {
101    names: Box<[SmolStr]>,
102    joined: OnceLock<SmolStr>,
103}
104
105/// Where the first `levels` of `joined` end: the byte the separator after them
106/// sits at, or the whole length when there is no separator left to reach.
107///
108/// The escape is honoured, so a name holding one does not end a level early.
109fn end_of_level(joined: &str, levels: usize) -> usize {
110    if levels == 0 {
111        return 0;
112    }
113
114    let mut seen = 0;
115    let mut escaped = false;
116
117    for (at, ch) in joined.char_indices() {
118        match ch {
119            _ if escaped => escaped = false,
120            ESCAPE => escaped = true,
121            SEPARATOR => {
122                seen += 1;
123                if seen == levels {
124                    return at;
125                }
126            }
127            _ => {}
128        }
129    }
130
131    joined.len()
132}
133
134/// How many levels a validated joined key holds, without building any of them.
135fn count_levels(joined: &str) -> usize {
136    if joined.is_empty() {
137        return 0;
138    }
139
140    let mut levels = 1;
141    let mut escaped = false;
142    for ch in joined.chars() {
143        match ch {
144            _ if escaped => escaped = false,
145            ESCAPE => escaped = true,
146            SEPARATOR => levels += 1,
147            _ => {}
148        }
149    }
150    levels
151}
152
153/// One level of a validated joined key, borrowed unless it was escaped.
154fn level_at(joined: &str, index: usize) -> Option<Cow<'_, str>> {
155    let mut level = 0;
156    let mut start = 0;
157    let mut escaped = false;
158
159    for (at, ch) in joined.char_indices() {
160        match ch {
161            _ if escaped => escaped = false,
162            ESCAPE => escaped = true,
163            SEPARATOR => {
164                if level == index {
165                    return Some(unescape(&joined[start..at]));
166                }
167                level += 1;
168                start = at + ch.len_utf8();
169            }
170            _ => {}
171        }
172    }
173
174    (level == index && !joined.is_empty()).then(|| unescape(&joined[start..]))
175}
176
177impl StorePath {
178    /// The path that is under nothing.
179    pub const fn root() -> Self {
180        Self {
181            held: Held::Written {
182                levels: &[],
183                joined: "",
184            },
185        }
186    }
187
188    /// A path whose levels are known when the code is compiled.
189    ///
190    /// Both forms are handed over ready, so this allocates nothing: no level may
191    /// be empty, and `joined` must be exactly the joined spelling a path keeps
192    /// for `segments`, escapes and all.
193    ///
194    /// Both are checked here rather than trusted. The check is a `const fn`, so
195    /// a path written into a `const` - `StateScope::PATH`, which is where these
196    /// come from - fails to compile when the halves disagree, whether a macro or
197    /// a hand-written impl wrote them. A const panic carries no formatting, so
198    /// it names the invariant and not the level; the `#[amethystate(prefix =
199    /// ...)]` macro checks the same two things first and points at the
200    /// attribute.
201    ///
202    /// # Panics
203    ///
204    /// If any level is empty, or if `joined` is not the joined form of
205    /// `segments`. At compile time when the call is in a const context, which is
206    /// the only place it is meant to be.
207    pub const fn from_static(segments: &'static [&'static str], joined: &'static str) -> Self {
208        check_static(segments, joined);
209
210        Self {
211            held: Held::Written {
212                levels: segments,
213                joined,
214            },
215        }
216    }
217
218    /// One level named `name`, whatever `name` contains - the empty name
219    /// included.
220    ///
221    /// A level's name is the caller's to choose and every document format this
222    /// library writes lets a member be named with nothing, so a name that is
223    /// empty is a name. It is the joined spelling that cannot carry one alone:
224    /// `""` reads back as the root, so the one-level path `[""]` has no
225    /// spelling of its own and a flat text document cannot hold it.
226    pub fn segment(name: impl AsRef<str>) -> Self {
227        Self::from_checked(vec![SmolStr::new(name.as_ref())])
228    }
229
230    /// The whole of this path as one level, its spelling being the name.
231    ///
232    /// What a store with no levels is addressed by: `ui.theme` becomes one
233    /// level called `ui.theme`, and a level of the original holding a `.` keeps
234    /// the escape that tells it from a separator, so the two spell differently
235    /// here as well.
236    ///
237    /// # Panics
238    ///
239    /// If this is the root, which spells nothing and so names no level.
240    #[track_caller]
241    pub fn as_one_level(&self) -> Self {
242        Self::segment(PathRef::from(self).as_str())
243    }
244
245    /// A path out of the levels it is under, outermost first.
246    ///
247    /// No levels at all is the root, and here that is a statement rather than
248    /// an accident: an `as_root` struct's path is written out as no levels.
249    /// A list that came from data and turned out empty is somebody's filter
250    /// rather than somebody's decision, and goes through
251    /// [`StorePath::try_from_segments`], which refuses it.
252    /// [`StorePath::try_from_segments`] refuses it, because a list that came
253    /// from data and turned out empty is not something anyone said.
254    #[track_caller]
255    pub fn from_segments<I, S>(segments: I) -> Self
256    where
257        I: IntoIterator<Item = S>,
258        S: AsRef<str>,
259    {
260        match Self::try_from_segments(segments) {
261            Ok(path) => path,
262            Err(StorePathError::EmptyPath) => Self::root(),
263            Err(other) => panic!("{other}"),
264        }
265    }
266
267    /// [`StorePath::from_segments`] for levels that can turn out to be empty.
268    ///
269    /// Which is where the two differ: a written-out empty list means the root,
270    /// and a computed one that filtered down to nothing means nobody decided.
271    /// The second is refused with [`StorePathError::EmptyPath`], because it
272    /// would name the whole store, and a write through it would replace
273    /// everything.
274    pub fn try_from_segments<I, S>(segments: I) -> Result<Self, StorePathError>
275    where
276        I: IntoIterator<Item = S>,
277        S: AsRef<str>,
278    {
279        let segments = segments.into_iter();
280        let mut collected: Vec<SmolStr> = Vec::with_capacity(segments.size_hint().0);
281
282        for segment in segments {
283            collected.push(SmolStr::new(segment.as_ref()));
284        }
285
286        if collected.is_empty() {
287            return Err(StorePathError::EmptyPath);
288        }
289
290        Ok(Self::from_checked(collected))
291    }
292
293    fn from_checked(segments: Vec<SmolStr>) -> Self {
294        Self {
295            held: Held::Levels(Arc::new(Levels {
296                names: segments.into_boxed_slice(),
297                joined: OnceLock::new(),
298            })),
299        }
300    }
301
302    /// This path with one more level under it, whatever `name` contains.
303    pub fn push(&self, name: impl AsRef<str>) -> Self {
304        self.push_shared(SmolStr::new(name.as_ref()))
305    }
306
307    /// [`StorePath::push`] taking a name the caller already holds shared.
308    ///
309    /// What a document engine has: a level's name is stored as one of these,
310    /// and a scan puts a path together out of names it is holding anyway. The
311    /// other form copies the name into a fresh one per key.
312    pub fn push_shared(&self, name: SmolStr) -> Self {
313        let mut segments = self.own_levels();
314        segments.push(name);
315        Self::from_checked(segments)
316    }
317
318    /// This path with `other`'s levels under it.
319    pub fn join(&self, other: &StorePath) -> Self {
320        let mut segments = self.own_levels();
321        segments.extend(other.own_levels());
322        Self::from_checked(segments)
323    }
324
325    /// The levels, outermost first.
326    ///
327    /// Borrowed where the level is a run of the joined form, which is every
328    /// level whose name carries no separator to escape.
329    pub fn segments(&self) -> impl ExactSizeIterator<Item = Level<'_>> + '_ {
330        (0..self.len()).map(|i| self.segment_at(i).expect("counted"))
331    }
332
333    /// One level, or `None` past the end.
334    pub fn segment_at(&self, index: usize) -> Option<Level<'_>> {
335        match &self.held {
336            Held::Written { levels, .. } => levels.get(index).copied().map(Level::named),
337            Held::Levels(held) => held.names.get(index).map(|level| Level::named(level)),
338            Held::Joined { joined } => level_at(joined, index).map(Level::from_cow),
339            Held::Prefix { of, levels } => of
340                .names
341                .get(index)
342                .filter(|_| index < *levels)
343                .map(|level| Level::named(level)),
344        }
345    }
346
347    /// The whole path as one string, with the separator escaped inside names.
348    ///
349    /// This is how a document's plane names a key, where the file has to hold
350    /// something a person can read. A path that arrived as one pays nothing to
351    /// read it back; a path built from levels is spelled here, once, and holds
352    /// on to the spelling.
353    ///
354    /// Private on purpose. A path is its levels, and everything outside this
355    /// module addresses one by them: the plane reaches the spelling through
356    /// [`PathRef`], which is the type that says a joined key is meant, and
357    /// anything that only wants to *show* a path uses [`Display`]. Handing the
358    /// spelling out is how `&str` got back into signatures that had a path to
359    /// hand.
360    fn as_str(&self) -> &str {
361        match &self.held {
362            Held::Written { joined, .. } => joined,
363            Held::Levels(held) => held.joined.get_or_init(|| join(&held.names)),
364            Held::Joined { joined } => joined,
365
366            // A run of the spelling the list it came from already has, so
367            // asking for one spells the *parent* once and every ancestor of it
368            // for nothing after that. Finding where to cut is a walk of the
369            // string rather than a lookup, which is the price of not storing a
370            // second spelling per ancestor.
371            Held::Prefix { of, levels } => {
372                let whole = of.joined.get_or_init(|| join(&of.names));
373                &whole[..end_of_level(whole, *levels)]
374            }
375        }
376    }
377
378    /// The levels as their own list, for the callers that build a new path out
379    /// of them.
380    fn own_levels(&self) -> Vec<SmolStr> {
381        match &self.held {
382            Held::Written { levels, .. } => levels
383                .iter()
384                .map(|level| SmolStr::new_static(level))
385                .collect(),
386            Held::Levels(held) => held.names.to_vec(),
387            Held::Joined { joined } => (0..count_levels(joined))
388                .map(|at| SmolStr::new(&*level_at(joined, at).expect("counted")))
389                .collect(),
390            Held::Prefix { of, levels } => of.names[..*levels].to_vec(),
391        }
392    }
393
394    /// Whether this path names the whole store rather than anything in it.
395    ///
396    /// Answered per state rather than through [`len`](Self::len), because
397    /// counting the levels of a joined key walks the whole of it - and that is
398    /// the state a flat engine's keys arrive in, asked once per key of every
399    /// scan through `level_under`.
400    pub fn is_root(&self) -> bool {
401        match &self.held {
402            Held::Joined { joined } => joined.is_empty(),
403            Held::Written { levels, .. } => levels.is_empty(),
404            Held::Levels(held) => held.names.is_empty(),
405            Held::Prefix { levels, .. } => *levels == 0,
406        }
407    }
408
409    /// How many levels the path has.
410    ///
411    /// There is deliberately no `is_empty` beside it. A path of no levels is
412    /// the root - the one that names the whole store - and that is a thing
413    /// rather than the absence of one, so [`is_root`](Self::is_root) is what
414    /// asks. `is_empty` would be a second name for the same question, and the
415    /// worse of the two.
416    #[allow(clippy::len_without_is_empty)]
417    pub fn len(&self) -> usize {
418        match &self.held {
419            Held::Written { levels, .. } => levels.len(),
420            Held::Levels(held) => held.names.len(),
421            Held::Joined { joined } => count_levels(joined),
422            Held::Prefix { levels, .. } => *levels,
423        }
424    }
425
426    /// Whether every level of `prefix` starts this path.
427    ///
428    /// Compared level by level, so `ui` does not start `uix.width` - which
429    /// comparing the joined strings would say it does.
430    pub fn starts_with(&self, prefix: &StorePath) -> bool {
431        prefix.len() <= self.len() && prefix.segments().zip(self.segments()).all(|(a, b)| a == b)
432    }
433
434    /// The levels below `prefix`, or `None` when `prefix` does not start this
435    /// path.
436    pub fn strip_prefix(&self, prefix: &StorePath) -> Option<StorePath> {
437        self.starts_with(prefix)
438            .then(|| StorePath::from_checked(self.own_levels()[prefix.len()..].to_vec()))
439    }
440
441    /// Whether this subtree and `other`'s hold any key in common.
442    ///
443    /// Two subtrees are nested or apart and never half over each other, so this
444    /// is two containment tests and equality needs no arm of its own:
445    /// [`starts_with`](Self::starts_with) admits the prefix itself.
446    pub fn overlaps(&self, other: &StorePath) -> bool {
447        self.starts_with(other) || other.starts_with(self)
448    }
449
450    /// The level list to hang a prefix off, made once where there is not one
451    /// already.
452    fn shared_levels(&self) -> Arc<Levels> {
453        match &self.held {
454            Held::Levels(held) => held.clone(),
455            Held::Prefix { of, .. } => of.clone(),
456            _ => Arc::new(Levels {
457                names: self.own_levels().into_boxed_slice(),
458                joined: OnceLock::new(),
459            }),
460        }
461    }
462
463    /// The path one level up, or `None` at the root.
464    ///
465    /// Shares the level list rather than copying it short: walking up is asked
466    /// for one ancestor after another, and each answer used to be the whole
467    /// list copied to drop the end of it.
468    pub fn parent(&self) -> Option<StorePath> {
469        let levels = self.len().checked_sub(1)?;
470
471        Some(StorePath {
472            held: Held::Prefix {
473                of: self.shared_levels(),
474                levels,
475            },
476        })
477    }
478
479    /// This path and every path it sits under, nearest first, the root last.
480    ///
481    /// The set a place that holds this one can be at: a path is held at itself
482    /// or at one of its ancestors, and nowhere else. Walking it costs one level
483    /// list, made once and shared by every step - where asking for a parent and
484    /// then its parent used to copy the list per step, and throw each copy away
485    /// after a comparison.
486    pub fn upwards(&self) -> impl Iterator<Item = StorePath> + '_ {
487        let of = self.shared_levels();
488
489        (0..=self.len()).rev().map(move |levels| StorePath {
490            held: Held::Prefix {
491                of: of.clone(),
492                levels,
493            },
494        })
495    }
496
497    /// The last level, or `None` at the root.
498    pub fn name(&self) -> Option<Level<'_>> {
499        self.segment_at(self.len().checked_sub(1)?)
500    }
501
502    /// Where this path sits under `prefix`, and the level it sits at.
503    ///
504    /// The method [`level_under`] would be if it had nothing to check. That
505    /// one takes a key an engine read off a disk and can refuse it; a path has
506    /// already been through that, so this only reads.
507    ///
508    /// Which is what lets the rest of the library ask the question without
509    /// holding a key: an entry one level down and one two levels down are
510    /// different answers, and telling them apart is a map's whole business.
511    pub fn level_under(&self, prefix: &StorePath) -> Under<'_> {
512        level_below(self.as_str(), prefix.as_str(), prefix.is_root())
513    }
514
515    /// The level that follows `prefix`, read straight off the joined form.
516    ///
517    /// The same answer as `starts_with(prefix)` followed by
518    /// `segment_at(prefix.len())`, reached without splitting either path into
519    /// levels. That matters where it is used: a scan hands back a path per
520    /// stored key, and the caller wants one name from each, so splitting every
521    /// key into a level per allocation is work thrown away as soon as it is
522    /// done.
523    ///
524    /// Borrowed unless the name carries an escaped separator, which is the
525    /// only case where the level is not a run of the joined string.
526    pub fn name_under(&self, prefix: &StorePath) -> Option<Level<'_>> {
527        match level_below(self.as_str(), prefix.as_str(), prefix.is_root()) {
528            Under::Entry(name) | Under::Deeper(name) => Some(name),
529            Under::Prefix | Under::Outside => None,
530        }
531    }
532
533    /// The name `key` is stored under, below this path: the *last* level of
534    /// what remains, where [`StorePath::name_under`] is the first.
535    ///
536    /// Taking a prefix off does not touch the end, so the last level of what
537    /// remains is the last level of `key` - there is no remainder to build, and
538    /// the name is borrowed out of `key` rather than copied out of it.
539    pub fn entry_name<'k>(&self, key: &'k StorePath) -> Option<Level<'k>> {
540        if !key.starts_with(self) || key.len() == self.len() {
541            return None;
542        }
543
544        key.name()
545    }
546
547    /// Reads a joined spelling back into the path it spells.
548    ///
549    /// Only for data already on disk: a path in code is built from its
550    /// segments, so nothing else needs to parse one. Fallible because a key
551    /// this library did not write can hold a level with no name, and such a
552    /// path is not one.
553    pub fn parse_joined(joined: &str) -> Result<Self, StorePathError> {
554        validate_joined(joined)?;
555
556        Ok(Self {
557            held: Held::Joined {
558                joined: SmolStr::new(joined),
559            },
560        })
561    }
562}
563
564/// A path that borrows its joined form instead of owning one.
565///
566/// What an engine has in hand when it reads a key out of its own page: the
567/// bytes are there, in the joined spelling a path keeps, and
568/// building a `StorePath` per key would allocate once for every entry on the
569/// hot path of loading a map.
570///
571/// It carries the same checked-ness as an owned path and can be had no other
572/// way: either [`PathRef::parse`] walked the key, or it borrows a path that
573/// was walked already. So a caller holding one needs no opinion about how a
574/// path is spelled, which is the whole point - the alternative is passing
575/// `&str` and hoping every reader remembers what it is.
576#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
577pub struct PathRef<'a> {
578    joined: &'a str,
579}
580
581impl<'a> PathRef<'a> {
582    /// Reads a key an engine holds, refusing one this library did not write.
583    ///
584    /// The same walk [`StorePath::parse_joined`] makes, without the
585    /// allocation that follows it.
586    pub fn parse(joined: &'a str) -> Result<Self, StorePathError> {
587        validate_joined(joined)?;
588
589        Ok(Self { joined })
590    }
591
592    /// The whole path as one string, which is what it was built from.
593    pub fn as_str(&self) -> &'a str {
594        self.joined
595    }
596
597    pub fn is_root(&self) -> bool {
598        self.joined.is_empty()
599    }
600
601    /// Where this sits under `prefix`, and the level it sits at. See
602    /// [`StorePath::level_under`], which answers the same question of an owned
603    /// path.
604    pub fn level_under(&self, prefix: &StorePath) -> Under<'a> {
605        level_below(self.joined, prefix.as_str(), prefix.is_root())
606    }
607
608    /// The level directly under `prefix`, or `None` when `prefix` does not
609    /// hold this path. See [`StorePath::name_under`].
610    pub fn name_under(&self, prefix: &StorePath) -> Option<Level<'a>> {
611        match self.level_under(prefix) {
612            Under::Entry(name) | Under::Deeper(name) => Some(name),
613            Under::Prefix | Under::Outside => None,
614        }
615    }
616
617    /// An owned copy, for a caller that has to keep it past the borrow.
618    ///
619    /// This is where the allocation the type exists to avoid is finally paid,
620    /// so it is paid by whoever actually keeps the path.
621    pub fn to_path(&self) -> StorePath {
622        StorePath::parse_joined(self.joined).expect("a borrowed path was checked when it was made")
623    }
624}
625
626/// Borrowing a path costs nothing and checks nothing: it has been through the
627/// check already.
628impl<'a> From<&'a StorePath> for PathRef<'a> {
629    fn from(path: &'a StorePath) -> Self {
630        Self {
631            joined: path.as_str(),
632        }
633    }
634}
635
636impl fmt::Debug for PathRef<'_> {
637    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
638        write!(f, "PathRef({:?})", self.joined)
639    }
640}
641
642impl fmt::Display for PathRef<'_> {
643    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
644        f.write_str(self.joined)
645    }
646}
647
648impl PartialEq<StorePath> for PathRef<'_> {
649    fn eq(&self, other: &StorePath) -> bool {
650        self.joined == other.as_str()
651    }
652}
653
654/// One level of a path: a name, as the store means a name.
655///
656/// Not a run of a joined key. A name holding a separator is still one name -
657/// `dark.mode` is a level called `dark.mode`, not two - and the joined form
658/// spells it with the separator doubled. Which is the whole reason this is a
659/// type: the two are the same characters often enough that a `&str` between them
660/// says nothing, and wrong often enough that it matters.
661///
662/// Borrowed where the level is a run of the joined form, which is every level
663/// whose name carries nothing to escape, and held where it is not. A held name
664/// is a [`SmolStr`], the same way the level list of a path holds one, so a name
665/// of 23 bytes or fewer costs no allocation to keep.
666#[derive(Clone)]
667pub struct Level<'a>(Name<'a>);
668
669#[derive(Clone)]
670enum Name<'a> {
671    Borrowed(&'a str),
672    Held(SmolStr),
673}
674
675impl<'a> Level<'a> {
676    /// A name a caller wrote or a document holds, taken as the name it is.
677    ///
678    /// The entry point from outside: a level read off a document's own map, or
679    /// handed in by whoever is addressing the store. Nothing is checked here
680    /// beyond what the type says, because a name is any string at all - the
681    /// empty one included, which every format this library writes lets a
682    /// member carry.
683    pub fn named(name: &'a str) -> Self {
684        Self(Name::Borrowed(name))
685    }
686
687    /// The name as characters, for handing to a document or an engine.
688    pub fn as_str(&self) -> &str {
689        match &self.0 {
690            Name::Borrowed(name) => name,
691            Name::Held(name) => name,
692        }
693    }
694
695    /// The name with a life of its own.
696    pub fn into_owned(self) -> Level<'static> {
697        Level(match self.0 {
698            Name::Borrowed(name) => Name::Held(SmolStr::new(name)),
699            Name::Held(name) => Name::Held(name),
700        })
701    }
702
703    fn from_cow(name: Cow<'a, str>) -> Self {
704        Self(match name {
705            Cow::Borrowed(name) => Name::Borrowed(name),
706            Cow::Owned(name) => Name::Held(SmolStr::new(name)),
707        })
708    }
709}
710
711impl Level<'static> {
712    /// A name already held the way a level holds one, taken without copying it
713    /// again.
714    ///
715    /// For a caller that has spelled the name itself - see
716    /// [`SmolStrBuilder`](smol_str::SmolStrBuilder), which writes a short one
717    /// into the inline form and never reaches the heap.
718    pub fn held(name: SmolStr) -> Self {
719        Self(Name::Held(name))
720    }
721}
722
723/// By the name, whichever form holds it, so a level that was assembled answers
724/// like the one that was borrowed.
725impl PartialEq for Level<'_> {
726    fn eq(&self, other: &Self) -> bool {
727        self.as_str() == other.as_str()
728    }
729}
730
731impl Eq for Level<'_> {}
732
733impl Ord for Level<'_> {
734    fn cmp(&self, other: &Self) -> Ordering {
735        self.as_str().cmp(other.as_str())
736    }
737}
738
739impl PartialOrd for Level<'_> {
740    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
741        Some(self.cmp(other))
742    }
743}
744
745impl Hash for Level<'_> {
746    fn hash<H: Hasher>(&self, state: &mut H) {
747        self.as_str().hash(state);
748    }
749}
750
751/// A level is its name, and equality, order and hashing all answer from it -
752/// which is what lets a collection keyed by a level be looked up by one.
753impl std::borrow::Borrow<str> for Level<'_> {
754    fn borrow(&self) -> &str {
755        self.as_str()
756    }
757}
758
759impl fmt::Debug for Level<'_> {
760    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
761        write!(f, "Level({:?})", self.as_str())
762    }
763}
764
765impl fmt::Display for Level<'_> {
766    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
767        f.write_str(self.as_str())
768    }
769}
770
771/// A level is a name, and [`StorePath::from_segments`] takes names.
772impl AsRef<str> for Level<'_> {
773    fn as_ref(&self) -> &str {
774        self.as_str()
775    }
776}
777
778impl PartialEq<str> for Level<'_> {
779    fn eq(&self, other: &str) -> bool {
780        self.as_str() == other
781    }
782}
783
784impl PartialEq<&str> for Level<'_> {
785    fn eq(&self, other: &&str) -> bool {
786        self.as_str() == *other
787    }
788}
789
790/// The name a document holds a child under.
791///
792/// Two different things that are the same characters. A tree's level is held
793/// under its own name; the plane holds a whole path under the spelling of it,
794/// because the plane's whole point is that a path is one key. Which of the two
795/// a caller means is said here, by the constructor it reached for, instead of
796/// being left to whoever reads the `&str` to work out - and it is the seam an
797/// obfuscated key would be introduced along, because that is exactly the day
798/// the two stop being the same characters.
799#[derive(Debug, Clone, Copy, PartialEq, Eq)]
800pub struct Stored<'a>(&'a str);
801
802impl<'a> Stored<'a> {
803    /// A level of a tree, held under its own name.
804    pub fn level(name: &'a Level<'_>) -> Self {
805        Self(name.as_str())
806    }
807
808    /// A key of the plane, held under the whole path's spelling.
809    pub fn whole(path: PathRef<'a>) -> Self {
810        Self(path.as_str())
811    }
812
813    /// A name a document read out of its own map, taken as it stands.
814    ///
815    /// The entry point from the file: what is there is what is there, and
816    /// nothing about it has been decided yet.
817    pub fn read(name: &'a str) -> Self {
818        Self(name)
819    }
820
821    /// The characters, for the format's own library to look up.
822    pub fn as_str(&self) -> &'a str {
823        self.0
824    }
825}
826
827impl fmt::Display for Stored<'_> {
828    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
829        f.write_str(self.0)
830    }
831}
832
833/// Where a key read back from a store sits relative to the prefix it was
834/// scanned from.
835#[derive(Debug, Clone, PartialEq, Eq)]
836pub enum Under<'a> {
837    /// The key is the prefix itself.
838    Prefix,
839
840    /// The key is exactly one level below, named here.
841    Entry(Level<'a>),
842
843    /// The key is more than one level below. The name is the level directly
844    /// under the prefix; the rest of the key is inside it.
845    Deeper(Level<'a>),
846
847    /// The key is not under the prefix.
848    Outside,
849}
850
851/// Where `key` sits under `prefix`, without building a path for it.
852///
853/// What a scan's caller usually wants from a key: a map keyed by the level
854/// below its own path reads every key once and asks for one name from each, so
855/// building a [`StorePath`] to answer that is a string and a walk thrown away
856/// per entry.
857///
858/// [`Under::Entry`] and [`Under::Deeper`] are separate because a map owns the
859/// level below it and nothing further: a key two levels down is somebody else's
860/// and reading it as an entry hands back the wrong value under the wrong name.
861///
862/// The key is still checked, because a key this library did not write has to
863/// be refused where it is read rather than believed.
864pub fn level_under<'a>(key: &'a str, prefix: &StorePath) -> Result<Under<'a>, StorePathError> {
865    validate_joined(key)?;
866    Ok(level_below(key, prefix.as_str(), prefix.is_root()))
867}
868
869/// The level after `head` in `whole`, both joined.
870fn level_below<'a>(whole: &'a str, head: &str, head_is_root: bool) -> Under<'a> {
871    // Nothing left once the prefix is off means the key is the prefix - but
872    // only where nothing was taken off after it. A separator was consumed
873    // below, and what follows one is a level however short it is, the empty
874    // name included.
875    let rest = if head_is_root {
876        match whole.is_empty() {
877            true => return Under::Prefix,
878            false => whole,
879        }
880    } else if whole == head {
881        return Under::Prefix;
882    } else {
883        match whole
884            .strip_prefix(head)
885            .and_then(|rest| rest.strip_prefix(SEPARATOR))
886        {
887            Some(rest) => rest,
888            None => return Under::Outside,
889        }
890    };
891
892    let mut escaped = false;
893    for (at, ch) in rest.char_indices() {
894        match ch {
895            _ if escaped => escaped = false,
896            ESCAPE => escaped = true,
897            SEPARATOR => return Under::Deeper(Level::from_cow(unescape(&rest[..at]))),
898            _ => {}
899        }
900    }
901
902    Under::Entry(Level::from_cow(unescape(rest)))
903}
904
905/// Whether a joined key is one this type could have written, without building
906/// anything to find out.
907///
908/// The same walk the split does, counting levels instead of collecting them.
909/// It runs eagerly where the split does not, because a key that will not parse
910/// has to be refused where it is read rather than wherever someone first asks
911/// for its levels.
912fn validate_joined(joined: &str) -> Result<(), StorePathError> {
913    let mut escaped = false;
914
915    for ch in joined.chars() {
916        match ch {
917            _ if escaped => {
918                if ch != SEPARATOR && ch != ESCAPE {
919                    return Err(StorePathError::DanglingEscape);
920                }
921                escaped = false;
922            }
923            ESCAPE => escaped = true,
924            _ => {}
925        }
926    }
927
928    match escaped {
929        true => Err(StorePathError::DanglingEscape),
930        false => Ok(()),
931    }
932}
933
934/// One level as it was written, borrowing when nothing was escaped.
935fn unescape(level: &str) -> Cow<'_, str> {
936    if !level.contains(ESCAPE) {
937        return Cow::Borrowed(level);
938    }
939
940    let mut out = String::with_capacity(level.len());
941    let mut escaped = false;
942    for ch in level.chars() {
943        match ch {
944            _ if escaped => {
945                out.push(ch);
946                escaped = false;
947            }
948            ESCAPE => escaped = true,
949            _ => out.push(ch),
950        }
951    }
952    Cow::Owned(out)
953}
954
955/// Why a set of segments is not a path.
956#[derive(Debug, Clone, PartialEq, Eq)]
957pub enum StorePathError {
958    /// An escape that escapes nothing. No key this type wrote holds one, and
959    /// reading it leniently would let two different keys name one path.
960    DanglingEscape,
961
962    /// No levels at all.
963    ///
964    /// A path is built from a list, and a list computed at run time can come
965    /// out empty - a filter that removed everything, a split of an empty
966    /// string. Such a list names the whole store, and a write through it would
967    /// replace all of it in code that never mentions the root.
968    ///
969    /// [`StorePath::root`] is how to say it on purpose.
970    EmptyPath,
971}
972
973impl fmt::Display for StorePathError {
974    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
975        match self {
976            StorePathError::DanglingEscape => {
977                f.write_str("an escape must be followed by a separator or another escape")
978            }
979            StorePathError::EmptyPath => f.write_str(
980                "a path of no levels names the whole store - say `StorePath::root()` to mean it",
981            ),
982        }
983    }
984}
985
986impl std::error::Error for StorePathError {}
987
988/// What a call can be given where a path is wanted.
989///
990/// A list of levels, or a path already built. Deliberately not `&str`: a string
991/// is a name, and letting one stand in for a path is the confusion this type
992/// exists to end - `store.get(["ui", "width"])` and `store.get("ui.width")`
993/// would otherwise look alike and mean different things.
994#[diagnostic::on_unimplemented(
995    message = "a path is a list of levels, and `{Self}` is not one",
996    label = "expected something like `[\"ui\", \"width\"]`",
997    note = "a string is a name, not a path: `\"ui.width\"` would be one level \
998            whose name holds a separator, which is somewhere else entirely. \
999            Spell the nesting out, or use `StorePath::parse_joined` to read a \
1000            path out of a string on purpose",
1001    note = "addressing one name rather than a path is what `Kv` is for, and it \
1002            does take `&str`"
1003)]
1004pub trait IntoStorePath {
1005    fn into_store_path(self) -> Result<StorePath, StorePathError>;
1006}
1007
1008impl IntoStorePath for StorePath {
1009    fn into_store_path(self) -> Result<StorePath, StorePathError> {
1010        Ok(self)
1011    }
1012}
1013
1014impl IntoStorePath for &StorePath {
1015    fn into_store_path(self) -> Result<StorePath, StorePathError> {
1016        Ok(self.clone())
1017    }
1018}
1019
1020impl<S: AsRef<str>, const N: usize> IntoStorePath for [S; N] {
1021    fn into_store_path(self) -> Result<StorePath, StorePathError> {
1022        StorePath::try_from_segments(self)
1023    }
1024}
1025
1026impl<S: AsRef<str>> IntoStorePath for &[S] {
1027    fn into_store_path(self) -> Result<StorePath, StorePathError> {
1028        StorePath::try_from_segments(self)
1029    }
1030}
1031
1032impl<S: AsRef<str>> IntoStorePath for Vec<S> {
1033    fn into_store_path(self) -> Result<StorePath, StorePathError> {
1034        StorePath::try_from_segments(self)
1035    }
1036}
1037
1038const SEPARATOR_BYTE: u8 = SEPARATOR as u8;
1039const ESCAPE_BYTE: u8 = ESCAPE as u8;
1040
1041/// Ends a level, and occurs nowhere else: a level's own `0x00` is written as
1042/// [`STANDS_IN`] `0x01`, so an encoded level holds no byte below `0x01` at all.
1043///
1044/// That is what makes a subtree a byte prefix and nothing more. An escape that
1045/// merely *followed* the terminator would not: `["ui\0x"]` would begin with the
1046/// encoding of `["ui"]` and read as a key under it.
1047const ENDS_A_LEVEL: u8 = 0x00;
1048
1049/// Stands in front of a byte the encoding needs back, and is written the same
1050/// way itself.
1051///
1052/// `0x00` becomes `0x01 0x01` and `0x01` becomes `0x01 0x02`, which keeps the
1053/// order the bytes were in: both land under `0x02`, where they belong, and
1054/// above each other in the right order.
1055const STANDS_IN: u8 = 0x01;
1056
1057/// A path as a flat engine stores it: every level, in order, each one ended.
1058///
1059/// The engines that hold a path as one key had no way to see where a level
1060/// ended, so the key carried the levels joined by a separator and every name
1061/// escaped to keep the separator out of it - and escaping does not preserve
1062/// order, which is why the names had to be compared through a function of their
1063/// own and why a scan's upper bound had to be guessed at and then filtered.
1064///
1065/// A terminator that cannot occur inside a level says the same thing without
1066/// any of that. Byte order over these keys *is* the order of the level lists
1067/// they came from, so a subtree is a byte prefix, exactly and with nothing left
1068/// over to check.
1069/// Held inline up to the length a path of the depth this library is built for
1070/// reaches: eight levels with names of ordinary length come to about sixty
1071/// bytes, and a key is built once per read and once per write rather than once
1072/// per scan. Past that it spills to the heap and costs what any buffer would.
1073pub type KeyBytes = SmallVec<[u8; 64]>;
1074
1075#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
1076pub struct Key(KeyBytes);
1077
1078impl StorePath {
1079    /// This path as a key.
1080    pub fn key(&self) -> Key {
1081        let mut out = KeyBytes::new();
1082        self.write_key(&mut out);
1083        Key(out)
1084    }
1085
1086    /// The same, into a buffer the caller keeps between keys.
1087    pub fn write_key(&self, out: &mut KeyBytes) {
1088        for level in self.segments() {
1089            for &byte in level.as_str().as_bytes() {
1090                match byte {
1091                    ENDS_A_LEVEL => out.extend_from_slice(&[STANDS_IN, STANDS_IN]),
1092                    STANDS_IN => out.extend_from_slice(&[STANDS_IN, STANDS_IN + 1]),
1093                    byte => out.push(byte),
1094                }
1095            }
1096            out.push(ENDS_A_LEVEL);
1097        }
1098    }
1099}
1100
1101impl Key {
1102    /// The root, which every key is under and which is no bytes at all.
1103    pub fn root() -> Self {
1104        Key(KeyBytes::new())
1105    }
1106
1107    /// A key as a store hands it back.
1108    ///
1109    /// Takes the bytes as they are; [`Key::path`] is where a key that this type
1110    /// did not write is refused, because that is where the answer has somewhere
1111    /// to go.
1112    pub fn from_bytes(bytes: &[u8]) -> Self {
1113        Key(KeyBytes::from_slice(bytes))
1114    }
1115
1116    pub fn as_bytes(&self) -> &[u8] {
1117        &self.0
1118    }
1119
1120    pub fn is_root(&self) -> bool {
1121        self.0.is_empty()
1122    }
1123
1124    /// The path this key spells.
1125    ///
1126    /// Refuses what this type does not write: bytes that are not text, a level
1127    /// left unterminated, and an escape that escapes nothing. Reading any of
1128    /// those leniently would let two keys name one path.
1129    pub fn path(&self) -> Result<StorePath, StorePathError> {
1130        let mut names: Vec<SmolStr> = Vec::new();
1131        let mut level = KeyBytes::new();
1132        let mut at = 0;
1133
1134        while at < self.0.len() {
1135            let byte = self.0[at];
1136            at += 1;
1137
1138            match byte {
1139                ENDS_A_LEVEL => {
1140                    let Ok(name) = std::str::from_utf8(&level) else {
1141                        return Err(not_a_key_this_wrote());
1142                    };
1143
1144                    names.push(SmolStr::new(name));
1145                    level.clear();
1146                }
1147                STANDS_IN => {
1148                    let held = self.0.get(at).copied();
1149                    at += 1;
1150
1151                    match held {
1152                        Some(STANDS_IN) => level.push(ENDS_A_LEVEL),
1153                        Some(byte) if byte == STANDS_IN + 1 => level.push(STANDS_IN),
1154                        _ => return Err(not_a_key_this_wrote()),
1155                    }
1156                }
1157                byte => level.push(byte),
1158            }
1159        }
1160
1161        if !level.is_empty() {
1162            return Err(not_a_key_this_wrote());
1163        }
1164
1165        match names.is_empty() {
1166            true => Ok(StorePath::root()),
1167            false => Ok(StorePath::from_checked(names)),
1168        }
1169    }
1170
1171    /// Whether this key names `prefix` or something under it.
1172    ///
1173    /// One test, because a level's terminator cannot occur inside a level: a
1174    /// key under `pot` starts with `pot\0`, `potato` does not, and there is no
1175    /// third answer for a caller to filter afterwards.
1176    pub fn under(&self, prefix: &Key) -> bool {
1177        self.0.starts_with(&prefix.0)
1178    }
1179
1180    /// The half-open range this key's subtree occupies, both ends exact. The
1181    /// root has no top.
1182    ///
1183    /// The top is this key with its last byte raised, which reaches everything
1184    /// under it because that byte is a terminator - the lowest there is, so
1185    /// raising it lands past every level the key could hold and short of its
1186    /// next sibling.
1187    pub fn subtree(&self) -> (&[u8], Option<KeyBytes>) {
1188        let mut top = self.0.clone();
1189
1190        while let Some(last) = top.pop() {
1191            if last < u8::MAX {
1192                top.push(last + 1);
1193                return (&self.0, Some(top));
1194            }
1195        }
1196
1197        (&[], None)
1198    }
1199}
1200
1201/// Refusals a decode makes, kept out of line.
1202///
1203/// Every key a store hands back is one this library wrote, so these arms are
1204/// taken by a hand edit or an older build and by nothing else - and a decode
1205/// runs once per key of every scan. `#[cold]` is what says so on a stable
1206/// compiler: `hint::cold_path` is not one yet.
1207#[cold]
1208#[inline(never)]
1209fn not_a_key_this_wrote() -> StorePathError {
1210    StorePathError::DanglingEscape
1211}
1212
1213impl fmt::Debug for Key {
1214    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1215        match self.path() {
1216            Ok(path) => write!(f, "Key({path})"),
1217            Err(_) => write!(f, "Key({:?})", &self.0),
1218        }
1219    }
1220}
1221
1222impl From<&StorePath> for Key {
1223    fn from(path: &StorePath) -> Self {
1224        path.key()
1225    }
1226}
1227
1228const fn check_static(segments: &[&str], joined: &str) {
1229    if has_empty_segment(segments) {
1230        panic!("a path segment cannot be empty");
1231    }
1232
1233    if !joins_to(segments, joined) {
1234        panic!(
1235            "the joined form of a static path must be what StorePath::as_str writes for its segments"
1236        );
1237    }
1238}
1239
1240const fn has_empty_segment(segments: &[&str]) -> bool {
1241    let mut at = 0;
1242
1243    while at < segments.len() {
1244        if segments[at].is_empty() {
1245            return true;
1246        }
1247        at += 1;
1248    }
1249
1250    false
1251}
1252
1253const fn joins_to(segments: &[&str], joined: &str) -> bool {
1254    let key = joined.as_bytes();
1255    let mut at = 0;
1256    let mut i = 0;
1257
1258    while at < segments.len() {
1259        if at > 0 {
1260            if i >= key.len() || key[i] != SEPARATOR_BYTE {
1261                return false;
1262            }
1263            i += 1;
1264        }
1265
1266        let segment = segments[at].as_bytes();
1267        let mut j = 0;
1268
1269        while j < segment.len() {
1270            let byte = segment[j];
1271
1272            if byte == SEPARATOR_BYTE || byte == ESCAPE_BYTE {
1273                if i >= key.len() || key[i] != ESCAPE_BYTE {
1274                    return false;
1275                }
1276                i += 1;
1277            }
1278
1279            if i >= key.len() || key[i] != byte {
1280                return false;
1281            }
1282            i += 1;
1283            j += 1;
1284        }
1285
1286        at += 1;
1287    }
1288
1289    i == key.len()
1290}
1291
1292fn join(segments: &[SmolStr]) -> SmolStr {
1293    let mut out = String::new();
1294
1295    for (i, segment) in segments.iter().enumerate() {
1296        if i > 0 {
1297            out.push(SEPARATOR);
1298        }
1299        for ch in segment.chars() {
1300            if ch == SEPARATOR || ch == ESCAPE {
1301                out.push(ESCAPE);
1302            }
1303            out.push(ch);
1304        }
1305    }
1306
1307    SmolStr::new(out)
1308}
1309
1310/// Equal paths are the ones that address the same place, which is the ones
1311/// holding the same levels - the same question [`Ord`] answers, asked of the
1312/// same form, so the two cannot drift apart.
1313impl PartialEq for StorePath {
1314    fn eq(&self, other: &Self) -> bool {
1315        self.len() == other.len() && self.segments().eq(other.segments())
1316    }
1317}
1318
1319impl Eq for StorePath {}
1320
1321/// The order a store lists in: by the levels, outermost first.
1322///
1323/// The flat engines range over [`Key`], whose byte order is this order, so
1324/// anything sorting paths for itself agrees with them by construction.
1325///
1326/// Not by the joined spelling. Joining puts a separator between levels and
1327/// escapes it inside them, and a name holding a byte below that separator then
1328/// sorts on the wrong side of a boundary: `["a!"]` comes after `["a", "b"]` by
1329/// levels and before it by spelling. The levels are what a path *is*, and
1330/// [`Key`] encodes them so that a flat engine's byte order says the same thing.
1331impl Ord for StorePath {
1332    fn cmp(&self, other: &Self) -> Ordering {
1333        self.segments().cmp(other.segments())
1334    }
1335}
1336
1337impl PartialOrd for StorePath {
1338    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
1339        Some(self.cmp(other))
1340    }
1341}
1342
1343/// By the levels, so it agrees with `Eq` however the path arrived.
1344///
1345/// `str`'s own `Hash` ends what it writes, so two levels cannot run together
1346/// into the hash of one - `["a", "b"]` and `["ab"]` are different paths and
1347/// hash differently.
1348impl Hash for StorePath {
1349    fn hash<H: Hasher>(&self, state: &mut H) {
1350        state.write_usize(self.len());
1351        for level in self.segments() {
1352            level.as_str().hash(state);
1353        }
1354    }
1355}
1356
1357impl fmt::Debug for StorePath {
1358    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1359        write!(f, "StorePath({})", self)
1360    }
1361}
1362
1363/// The joined spelling, written a level at a time rather than built and then
1364/// written: printing a path is not a reason to make one hold a second form of
1365/// itself.
1366impl fmt::Display for StorePath {
1367    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1368        for (at, level) in self.segments().enumerate() {
1369            if at > 0 {
1370                f.write_char(SEPARATOR)?;
1371            }
1372
1373            for ch in level.as_str().chars() {
1374                if ch == SEPARATOR || ch == ESCAPE {
1375                    f.write_char(ESCAPE)?;
1376                }
1377                f.write_char(ch)?;
1378            }
1379        }
1380
1381        Ok(())
1382    }
1383}
1384
1385/// A path both of whose halves the compiler knows: the levels and the joined
1386/// form, checked against each other where they are written.
1387///
1388/// [`StorePath`] holds a run-time path behind an `Arc`, which costs two things
1389/// a declaration cannot pay. It has a destructor, so a `static` built from one
1390/// cannot use `..` to fill in the rest of a struct; and its joined form cannot
1391/// be read in a `const`, which is where the checks over a declaration run.
1392/// This type has neither: it is `Copy`, it owns nothing, and everything about
1393/// it is available while the code is compiled.
1394///
1395/// It is what a declaration carries. Turn it into a `StorePath` with
1396/// [`StaticPath::path`] wherever a run-time path is wanted.
1397#[derive(Clone, Copy, PartialEq, Eq)]
1398pub struct StaticPath {
1399    segments: &'static [&'static str],
1400    joined: &'static str,
1401}
1402
1403impl StaticPath {
1404    /// # Panics
1405    ///
1406    /// If any level is empty, or if `joined` is not the joined form of
1407    /// `segments` - at compile time, which is the only place this is meant to
1408    /// be called. See [`StorePath::from_static`], which checks the same pair.
1409    pub const fn new(segments: &'static [&'static str], joined: &'static str) -> Self {
1410        check_static(segments, joined);
1411
1412        Self { segments, joined }
1413    }
1414
1415    /// The path that is under nothing.
1416    pub const fn root() -> Self {
1417        Self {
1418            segments: &[],
1419            joined: "",
1420        }
1421    }
1422
1423    /// The whole path as one string, where a `const` can read it.
1424    pub const fn as_str(&self) -> &'static str {
1425        self.joined
1426    }
1427
1428    pub const fn is_root(&self) -> bool {
1429        self.segments.is_empty()
1430    }
1431
1432    /// The levels of the path, where a `const` can read them.
1433    pub const fn segments(&self) -> &'static [&'static str] {
1434        self.segments
1435    }
1436
1437    /// The same path, as the type the store addresses by. Allocates nothing:
1438    /// both halves are already `'static`.
1439    pub fn path(&self) -> StorePath {
1440        StorePath::from_static(self.segments, self.joined)
1441    }
1442}
1443
1444impl From<StaticPath> for StorePath {
1445    fn from(at: StaticPath) -> Self {
1446        at.path()
1447    }
1448}
1449
1450impl PartialEq<str> for StaticPath {
1451    fn eq(&self, other: &str) -> bool {
1452        self.joined == other
1453    }
1454}
1455
1456impl AsRef<str> for StaticPath {
1457    fn as_ref(&self) -> &str {
1458        self.joined
1459    }
1460}
1461
1462impl fmt::Debug for StaticPath {
1463    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1464        write!(f, "StaticPath({:?})", self.joined)
1465    }
1466}
1467
1468impl fmt::Display for StaticPath {
1469    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1470        f.write_str(self.joined)
1471    }
1472}
1473
1474/// Written as the joined form, which is the only form a document holds.
1475impl serde::Serialize for StorePath {
1476    fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
1477        serializer.serialize_str(self.as_str())
1478    }
1479}
1480
1481/// Read back through [`StorePath::parse_joined`], so the invariants are checked
1482/// where the document is read rather than wherever somebody first asks for a
1483/// level: a key with a nameless level or a dangling escape is refused here and
1484/// cannot reach anything that would have to answer for it later.
1485impl<'de> serde::Deserialize<'de> for StorePath {
1486    fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
1487        let joined = <Cow<'de, str>>::deserialize(deserializer)?;
1488        StorePath::parse_joined(&joined).map_err(serde::de::Error::custom)
1489    }
1490}
1491
1492#[cfg(test)]
1493mod tests {
1494    use super::*;
1495    use proptest::prelude::*;
1496
1497    fn shares_a_key_by_levels(a: &StorePath, b: &StorePath) -> bool {
1498        let a: Vec<_> = a.segments().collect();
1499        let b: Vec<_> = b.segments().collect();
1500        let common = a.len().min(b.len());
1501        a[..common] == b[..common]
1502    }
1503
1504    #[test]
1505    fn two_subtrees_overlap_exactly_when_one_holds_the_other() {
1506        let cases: &[(&[&str], &[&str], bool, &str)] = &[
1507            (&["ui"], &["ui"], true, "the same path"),
1508            (&["ui"], &["ui", "theme"], true, "a child"),
1509            (
1510                &["ui"],
1511                &["ui", "a", "b"],
1512                true,
1513                "a grandchild - a subtree is not one level",
1514            ),
1515            (&["ui", "theme"], &["ui", "width"], false, "siblings"),
1516            (&["ui"], &["net"], false, "strangers"),
1517            (
1518                &["ui"],
1519                &["uix"],
1520                false,
1521                "a string prefix is not a level prefix",
1522            ),
1523            (
1524                &["ui"],
1525                &["uix", "width"],
1526                false,
1527                "and neither is its subtree",
1528            ),
1529            (
1530                &["ui"],
1531                &["ui!x"],
1532                false,
1533                "`!` sorts below the separator, and is still not under `ui`",
1534            ),
1535            (
1536                &["ui.theme"],
1537                &["ui"],
1538                false,
1539                "one level literally named `ui.theme`, escaped as `ui\\.theme`",
1540            ),
1541            (
1542                &["ui.theme"],
1543                &["ui", "theme"],
1544                false,
1545                "which is a different place from two levels",
1546            ),
1547            (&[], &["ui", "theme"], true, "the root holds everything"),
1548        ];
1549
1550        for (a, b, want, why) in cases {
1551            let (a, b) = (StorePath::from_segments(*a), StorePath::from_segments(*b));
1552            assert_eq!(a.overlaps(&b), *want, "{why}: {a} vs {b}");
1553            assert_eq!(
1554                b.overlaps(&a),
1555                *want,
1556                "{why}, the other way round: {b} vs {a}"
1557            );
1558        }
1559    }
1560
1561    use crate::strategies::{key as key_strategy, name_holding_the_separator, segment};
1562
1563    fn segment_strategy() -> impl Strategy<Value = String> {
1564        segment()
1565    }
1566
1567    fn dotted_segment() -> impl Strategy<Value = String> {
1568        name_holding_the_separator().prop_map(|(_, _, joined)| joined)
1569    }
1570
1571    fn path_strategy() -> impl Strategy<Value = Vec<String>> {
1572        prop::collection::vec(segment_strategy(), 1..5)
1573    }
1574
1575    fn hash_of(value: &impl Hash) -> u64 {
1576        let mut hasher = std::collections::hash_map::DefaultHasher::new();
1577        value.hash(&mut hasher);
1578        hasher.finish()
1579    }
1580
1581    proptest! {
1582        #[test]
1583        fn two_subtrees_meet_exactly_when_one_starts_the_other(
1584            a in path_strategy(),
1585            b in path_strategy(),
1586        ) {
1587            let (a, b) = (StorePath::from_segments(&a), StorePath::from_segments(&b));
1588
1589            prop_assert_eq!(
1590                a.overlaps(&b),
1591                shares_a_key_by_levels(&a, &b),
1592                "the joined form and the levels disagree: {} vs {}", a, b
1593            );
1594            prop_assert_eq!(a.overlaps(&b), b.overlaps(&a), "{} vs {}", a, b);
1595
1596            // Nested means the deeper one is itself the shared key.
1597            if a.overlaps(&b) {
1598                let deeper = if a.len() >= b.len() { &a } else { &b };
1599                prop_assert!(deeper.starts_with(&a));
1600                prop_assert!(deeper.starts_with(&b));
1601            }
1602        }
1603
1604        #[test]
1605        fn a_path_meets_everything_grown_from_it(
1606            head in path_strategy(),
1607            tail in prop::collection::vec(segment_strategy(), 0..4),
1608        ) {
1609            let a = StorePath::from_segments(&head);
1610            let b = StorePath::from_segments(head.iter().chain(&tail));
1611
1612            prop_assert!(a.overlaps(&b), "{} does not hold {}", a, b);
1613            prop_assert!(b.starts_with(&a), "{} vs {}", a, b);
1614        }
1615
1616        #[test]
1617        fn two_branches_of_one_level_never_meet(
1618            head in path_strategy(),
1619            left in segment_strategy(),
1620            right in segment_strategy(),
1621            left_tail in prop::collection::vec(segment_strategy(), 0..3),
1622            right_tail in prop::collection::vec(segment_strategy(), 0..3),
1623        ) {
1624            prop_assume!(left != right);
1625
1626            let a = StorePath::from_segments(head.iter().chain([&left]).chain(&left_tail));
1627            let b = StorePath::from_segments(head.iter().chain([&right]).chain(&right_tail));
1628
1629            prop_assert!(!a.overlaps(&b), "siblings met: {} vs {}", a, b);
1630        }
1631
1632        #[test]
1633        fn a_separator_inside_a_name_is_never_a_level(
1634            segments in prop::collection::vec(dotted_segment(), 1..16)
1635        ) {
1636            let path = StorePath::from_segments(&segments);
1637
1638            prop_assert_eq!(path.len(), segments.len());
1639            prop_assert_eq!(
1640                path.segments().map(|s| s.to_string()).collect::<Vec<_>>(),
1641                segments.clone()
1642            );
1643
1644            let split: Vec<&str> = segments.iter().flat_map(|s| s.split('.')).collect();
1645            if let Ok(taken_apart) = StorePath::try_from_segments(&split) {
1646                prop_assert_ne!(taken_apart, path);
1647            }
1648        }
1649        #[test]
1650        fn the_joined_form_round_trips(segments in path_strategy()) {
1651            let path = StorePath::from_segments(&segments);
1652            prop_assert_eq!(StorePath::parse_joined(path.as_str()).unwrap(), path);
1653        }
1654
1655        #[test]
1656        fn different_levels_never_join_to_one_key(a in path_strategy(), b in path_strategy()) {
1657            let pa = StorePath::from_segments(&a);
1658            let pb = StorePath::from_segments(&b);
1659
1660            prop_assert_eq!(a == b, pa.as_str() == pb.as_str());
1661        }
1662
1663        #[test]
1664        fn a_prefix_is_stripped_back_off(head in path_strategy(), tail in path_strategy()) {
1665            let prefix = StorePath::from_segments(&head);
1666            let full = prefix.join(&StorePath::from_segments(&tail));
1667
1668            prop_assert!(full.starts_with(&prefix));
1669            prop_assert_eq!(
1670                full.strip_prefix(&prefix).unwrap(),
1671                StorePath::from_segments(&tail)
1672            );
1673        }
1674
1675        #[test]
1676        fn a_path_does_not_remember_how_it_was_built(segments in path_strategy()) {
1677            let all_at_once = StorePath::from_segments(&segments);
1678
1679            let mut one_at_a_time = StorePath::root();
1680            for segment in &segments {
1681                one_at_a_time = one_at_a_time.push(segment);
1682            }
1683
1684            let by_joining = segments.iter().fold(StorePath::root(), |acc, segment| {
1685                acc.join(&StorePath::segment(segment))
1686            });
1687
1688            prop_assert_eq!(&one_at_a_time, &all_at_once);
1689            prop_assert_eq!(&by_joining, &all_at_once);
1690            prop_assert_eq!(one_at_a_time.as_str(), all_at_once.as_str());
1691        }
1692
1693        #[test]
1694        fn any_spelling_of_the_levels_gives_the_same_path(segments in path_strategy()) {
1695            let built = StorePath::from_segments(&segments);
1696            let refs: Vec<&str> = segments.iter().map(String::as_str).collect();
1697
1698            prop_assert_eq!(refs.as_slice().into_store_path().unwrap(), built.clone());
1699            prop_assert_eq!(segments.clone().into_store_path().unwrap(), built.clone());
1700            prop_assert_eq!((&built).into_store_path().unwrap(), built.clone());
1701            prop_assert_eq!(built.clone().into_store_path().unwrap(), built);
1702        }
1703
1704        #[test]
1705        fn every_path_is_under_the_root(segments in path_strategy()) {
1706            let path = StorePath::from_segments(&segments);
1707            let root = StorePath::root();
1708
1709            prop_assert!(path.starts_with(&root));
1710            prop_assert_eq!(path.strip_prefix(&root).unwrap(), path.clone());
1711            prop_assert!(!root.starts_with(&path));
1712        }
1713
1714        #[test]
1715        fn a_level_with_no_name_is_a_level_like_any_other(
1716            segments in path_strategy(),
1717            at in 0usize..8
1718        ) {
1719            let mut with_a_hole = segments.clone();
1720            let at = at % (with_a_hole.len() + 1);
1721            with_a_hole.insert(at, String::new());
1722
1723            let path = StorePath::try_from_segments(&with_a_hole).unwrap();
1724            let held = path.segment_at(at).unwrap();
1725
1726            prop_assert_eq!(path.len(), with_a_hole.len());
1727            prop_assert_eq!(held.as_str(), "");
1728
1729            let pushed = StorePath::from_segments(&segments).push("");
1730            let last = pushed.segment_at(segments.len()).unwrap();
1731
1732            prop_assert_eq!(pushed.len(), segments.len() + 1);
1733            prop_assert_eq!(last.as_str(), "");
1734        }
1735
1736        #[test]
1737        fn every_separator_inside_a_name_is_escaped(segments in path_strategy()) {
1738            let path = StorePath::from_segments(&segments);
1739
1740            let mut unescaped = 0usize;
1741            let mut escaped = false;
1742            for ch in path.as_str().chars() {
1743                match ch {
1744                    _ if escaped => escaped = false,
1745                    ESCAPE => escaped = true,
1746                    SEPARATOR => unescaped += 1,
1747                    _ => {}
1748                }
1749            }
1750
1751            prop_assert_eq!(unescaped, segments.len() - 1);
1752        }
1753
1754        #[test]
1755        fn growing_a_name_never_makes_it_a_prefix(
1756            head in path_strategy(),
1757            extra in segment_strategy()
1758        ) {
1759            let base = StorePath::from_segments(&head);
1760
1761            let mut grown = head.clone();
1762            grown.last_mut().unwrap().push_str(&extra);
1763            let longer = StorePath::from_segments(&grown);
1764
1765            prop_assert!(longer.as_str().starts_with(base.as_str()));
1766
1767            prop_assert_ne!(&longer, &base);
1768            prop_assert!(!longer.starts_with(&base));
1769            prop_assert!(!base.starts_with(&longer));
1770        }
1771
1772        #[test]
1773        fn stripping_succeeds_exactly_when_the_prefix_matches(
1774            a in path_strategy(),
1775            b in path_strategy()
1776        ) {
1777            let path = StorePath::from_segments(&a);
1778            let candidate = StorePath::from_segments(&b);
1779
1780            prop_assert_eq!(
1781                path.strip_prefix(&candidate).is_some(),
1782                path.starts_with(&candidate)
1783            );
1784        }
1785
1786        #[test]
1787        fn a_key_that_parses_joins_back_to_itself(key in key_strategy()) {
1788            if let Ok(path) = StorePath::parse_joined(&key) {
1789                prop_assert_eq!(path.as_str(), key.as_str());
1790            }
1791        }
1792
1793        #[test]
1794        fn a_key_with_a_nameless_level_reads_that_level_as_nameless(
1795            head in path_strategy(),
1796            tail in path_strategy(),
1797            at in 0usize..3
1798        ) {
1799            let head = StorePath::from_segments(&head);
1800            let tail = StorePath::from_segments(&tail);
1801
1802            let (key, hole, levels) = match at % 3 {
1803                0 => (format!("{SEPARATOR}{tail}"), 0, 1 + tail.len()),
1804                1 => (format!("{head}{SEPARATOR}"), head.len(), head.len() + 1),
1805                _ => (
1806                    format!("{head}{SEPARATOR}{SEPARATOR}{tail}"),
1807                    head.len(),
1808                    head.len() + 1 + tail.len(),
1809                ),
1810            };
1811
1812            let read = StorePath::parse_joined(&key).unwrap();
1813            let empty = read.segment_at(hole).unwrap();
1814
1815            prop_assert_eq!(read.len(), levels, "key: {:?}", key);
1816            prop_assert_eq!(empty.as_str(), "", "key: {:?}", key);
1817        }
1818
1819
1820        #[test]
1821        fn a_borrowed_path_answers_like_the_owned_one(
1822            key in path_strategy(),
1823            prefix in path_strategy()
1824        ) {
1825            let owned = StorePath::from_segments(&key);
1826            let under = StorePath::from_segments(&prefix);
1827            let borrowed = PathRef::from(&owned);
1828
1829            prop_assert_eq!(borrowed.as_str(), owned.as_str());
1830            prop_assert_eq!(borrowed.is_root(), owned.is_root());
1831            prop_assert_eq!(borrowed.level_under(&under), owned.level_under(&under));
1832            prop_assert_eq!(borrowed.name_under(&under), owned.name_under(&under));
1833            prop_assert_eq!(borrowed.to_path(), owned);
1834        }
1835
1836        #[test]
1837        fn a_key_borrows_exactly_when_it_parses(key in key_strategy()) {
1838            prop_assert_eq!(
1839                PathRef::parse(&key).is_ok(),
1840                StorePath::parse_joined(&key).is_ok(),
1841                "key: {:?}", key
1842            );
1843
1844            if let Ok(borrowed) = PathRef::parse(&key) {
1845                let owned = borrowed.to_path();
1846
1847                prop_assert_eq!(borrowed.as_str(), key.as_str());
1848                prop_assert_eq!(owned.as_str(), key.as_str());
1849            }
1850        }
1851
1852        #[test]
1853        fn the_range_a_key_gives_holds_exactly_the_subtree(
1854            head in path_strategy(),
1855            other in path_strategy()
1856        ) {
1857            let prefix = StorePath::from_segments(&head);
1858            let key = StorePath::from_segments(&other);
1859
1860            let under = prefix.key();
1861            let (low, high) = under.subtree();
1862            let bytes = key.key();
1863            let bytes = bytes.as_bytes();
1864
1865            let in_range = bytes >= low && high.as_ref().is_none_or(|h| bytes < h.as_slice());
1866
1867            prop_assert_eq!(
1868                in_range,
1869                key.starts_with(&prefix),
1870                "{} against the subtree of {}", key, prefix
1871            );
1872        }
1873
1874        #[test]
1875        fn everything_grown_from_a_path_is_in_its_range(
1876            head in path_strategy(),
1877            tail in prop::collection::vec(segment_strategy(), 0..4)
1878        ) {
1879            let prefix = StorePath::from_segments(&head);
1880            let under = prefix.join(&StorePath::from_segments(&tail));
1881
1882            prop_assert!(
1883                under.key().under(&prefix.key()),
1884                "{} is under {} and the range left it out", under, prefix
1885            );
1886        }
1887
1888        #[test]
1889        fn a_key_is_under_a_path_exactly_when_it_starts_with_it_and_a_separator(
1890            head in path_strategy(),
1891            tail in path_strategy(),
1892            other in path_strategy()
1893        ) {
1894            let prefix = StorePath::from_segments(&head);
1895            let boundary = format!("{}{}", prefix.as_str(), SEPARATOR);
1896
1897            let candidates = [
1898                prefix.join(&StorePath::from_segments(&tail)),
1899                StorePath::from_segments(&other),
1900                prefix.clone(),
1901                StorePath::from_segments(&tail).join(&prefix),
1902            ];
1903
1904            for candidate in candidates {
1905                let under = candidate.starts_with(&prefix) && candidate != prefix;
1906                prop_assert_eq!(under, candidate.as_str().starts_with(&boundary));
1907            }
1908        }
1909
1910        #[test]
1911        fn no_call_makes_a_path_from_segments_would_refuse(
1912            a in path_strategy(),
1913            b in path_strategy()
1914        ) {
1915            let pa = StorePath::from_segments(&a);
1916            let pb = StorePath::from_segments(&b);
1917
1918            let mut derived = vec![
1919                pa.join(&pb),
1920                pa.push("x"),
1921                StorePath::parse_joined(pa.as_str()).unwrap(),
1922                pa.join(&StorePath::root()),
1923            ];
1924            derived.extend(pa.parent());
1925            derived.extend(pa.join(&pb).strip_prefix(&pa));
1926
1927            for path in derived {
1928                let rebuilt = match StorePath::try_from_segments(path.segments()) {
1929                    Ok(rebuilt) => rebuilt,
1930                    Err(StorePathError::EmptyPath) => {
1931                        prop_assert!(path.is_root());
1932                        continue;
1933                    }
1934                    Err(other) => {
1935                        return Err(TestCaseError::fail(format!(
1936                            "a derived path holds an empty level: {other}"
1937                        )));
1938                    }
1939                };
1940                prop_assert_eq!(&rebuilt, &path);
1941                prop_assert_eq!(rebuilt.as_str(), path.as_str());
1942            }
1943        }
1944
1945        #[test]
1946        fn comparing_names_answers_what_comparing_their_keys_answers(
1947            a in segment_strategy(),
1948            b in segment_strategy()
1949        ) {
1950            let under = StorePath::segment("m");
1951            let ka = under.push(&a);
1952            let kb = under.push(&b);
1953
1954            prop_assert_eq!(
1955                a.cmp(&b),
1956                ka.key().as_bytes().cmp(kb.key().as_bytes()),
1957                "names {:?} and {:?} became keys {:?} and {:?}",
1958                a, b, ka, kb
1959            );
1960        }
1961
1962        #[test]
1963        fn equality_hashing_and_the_key_agree(a in path_strategy(), b in path_strategy()) {
1964            let pa = StorePath::from_segments(&a);
1965            let pb = StorePath::from_segments(&b);
1966
1967            let left = [pa.clone(), pa.parent().unwrap_or_else(StorePath::root), StorePath::root()];
1968            let right = [pb.clone(), pa.join(&pb).strip_prefix(&pa).unwrap(), StorePath::root()];
1969
1970            for x in &left {
1971                for y in &right {
1972                    prop_assert_eq!(x == y, x.as_str() == y.as_str());
1973                    if x == y {
1974                        prop_assert_eq!(hash_of(x), hash_of(y));
1975                    }
1976                }
1977            }
1978        }
1979
1980        #[test]
1981        fn the_const_check_accepts_what_the_join_writes(segments in path_strategy()) {
1982            let path = StorePath::from_segments(&segments);
1983            let levels: Vec<&str> = segments.iter().map(String::as_str).collect();
1984
1985            prop_assert!(
1986                joins_to(&levels, path.as_str()),
1987                "refused its own join: {:?}", path.as_str()
1988            );
1989
1990            let joined = path.as_str();
1991            let perturbed = [
1992                format!("{joined}{SEPARATOR}"),
1993                format!("{SEPARATOR}{joined}"),
1994                format!("{joined}{ESCAPE}"),
1995                format!("{joined}x"),
1996            ];
1997
1998            for candidate in perturbed {
1999                prop_assert!(
2000                    !joins_to(&levels, &candidate),
2001                    "accepted {:?} as the join of {:?}", candidate, levels
2002                );
2003            }
2004
2005            if levels.len() > 1 {
2006                let shorter = StorePath::from_segments(&segments[..segments.len() - 1]);
2007                prop_assert!(!joins_to(&levels, shorter.as_str()));
2008            }
2009        }
2010    }
2011
2012    #[test]
2013    fn the_encoding_is_a_backslash_before_the_separator() {
2014        assert_eq!(StorePath::segment("dark.mode").as_str(), "dark\\.mode");
2015        assert_eq!(
2016            StorePath::from_segments(["dark", "mode"]).as_str(),
2017            "dark.mode"
2018        );
2019    }
2020
2021    #[test]
2022    fn a_key_that_is_not_a_path_is_refused_at_both_doors() {
2023        let refused = [
2024            ("a\\", StorePathError::DanglingEscape),
2025            ("a\\x", StorePathError::DanglingEscape),
2026        ];
2027
2028        for (key, why) in refused {
2029            assert_eq!(
2030                StorePath::parse_joined(key).unwrap_err(),
2031                why,
2032                "parse_joined took {key:?}"
2033            );
2034            assert!(
2035                PathRef::parse(key).is_err(),
2036                "PathRef::parse took {key:?}, and the two doors must agree"
2037            );
2038        }
2039    }
2040
2041    #[test]
2042    fn a_subtree_is_one_run_with_nothing_of_anyone_elses_in_it() {
2043        let pot = StorePath::segment("pot");
2044        let luck = StorePath::segment("pot!luck");
2045        let ato = StorePath::from_segments(["pot", "ato"]);
2046        let potato = StorePath::segment("potato");
2047
2048        let mut sorted = [&potato, &luck, &ato, &pot];
2049        sorted.sort();
2050
2051        assert_eq!(
2052            sorted,
2053            [&pot, &ato, &luck, &potato],
2054            "everything under `pot` has to come directly after it"
2055        );
2056
2057        assert!(ato.starts_with(&pot));
2058        assert!(!luck.starts_with(&pot));
2059        assert!(!potato.starts_with(&pot));
2060    }
2061
2062    #[test]
2063    fn a_shared_prefix_answers_as_the_path_it_spells() {
2064        let full = StorePath::from_segments(["ui", "panels", "left", "width"]);
2065
2066        for (up, spelled) in [(1, "ui.panels.left"), (2, "ui.panels"), (3, "ui"), (4, "")] {
2067            let mut at = full.clone();
2068            for _ in 0..up {
2069                at = at.parent().expect("there is a level to drop");
2070            }
2071
2072            let built = StorePath::parse_joined(spelled).expect("the spelling parses");
2073
2074            assert_eq!(at.as_str(), spelled);
2075            assert_eq!(at, built);
2076            assert_eq!(at.len(), built.len());
2077            assert_eq!(at.is_root(), built.is_root());
2078            assert_eq!(at.key().as_bytes(), built.key().as_bytes());
2079            assert_eq!(
2080                at.segments().collect::<Vec<_>>(),
2081                built.segments().collect::<Vec<_>>()
2082            );
2083        }
2084    }
2085
2086    #[test]
2087    fn a_prefix_of_a_name_holding_the_separator_cuts_where_the_level_does() {
2088        let full = StorePath::from_segments(["dark.mode", "width"]);
2089        let up = full.parent().expect("there is a level to drop");
2090
2091        assert_eq!(up.len(), 1);
2092        assert_eq!(up.name().expect("one level").as_str(), "dark.mode");
2093        assert_eq!(up.as_str(), "dark\\.mode");
2094        assert_eq!(up, StorePath::segment("dark.mode"));
2095    }
2096
2097    #[test]
2098    fn walking_upwards_ends_at_the_root_and_holds_the_path_itself() {
2099        let full = StorePath::from_segments(["a", "b", "c"]);
2100
2101        let seen: Vec<String> = full.upwards().map(|at| at.as_str().to_string()).collect();
2102
2103        assert_eq!(seen, ["a.b.c", "a.b", "a", ""]);
2104        assert_eq!(StorePath::root().upwards().count(), 1);
2105    }
2106
2107    #[test]
2108    fn the_root_is_empty_and_stays_empty() {
2109        let root = StorePath::root();
2110
2111        assert!(root.is_root());
2112        assert_eq!(root.as_str(), "");
2113        assert_eq!(StorePath::parse_joined("").unwrap(), root);
2114        assert_eq!(root.parent(), None);
2115        assert_eq!(root.name(), None);
2116    }
2117
2118    #[test]
2119    fn a_key_no_join_could_have_written_is_not_a_second_name_for_one() {
2120        assert_ne!(
2121            StorePath::parse_joined("a\\b").ok(),
2122            StorePath::parse_joined("ab").ok()
2123        );
2124        assert_ne!(
2125            StorePath::parse_joined("a\\").ok(),
2126            StorePath::parse_joined("a").ok()
2127        );
2128    }
2129
2130    #[test]
2131    fn the_root_has_no_separator_boundary() {
2132        let root = StorePath::root();
2133        let child = StorePath::from_segments(["ui", "width"]);
2134
2135        assert!(child.starts_with(&root));
2136        assert_eq!(format!("{}{}", root.as_str(), SEPARATOR), ".");
2137        assert!(!child.as_str().starts_with(SEPARATOR));
2138        assert!(
2139            !StorePath::segment(".hidden")
2140                .as_str()
2141                .starts_with(SEPARATOR)
2142        );
2143    }
2144
2145    #[test]
2146    fn a_subtree_boundary_does_not_cover_the_node_itself() {
2147        let node = StorePath::segment("ui");
2148        let boundary = format!("{}{}", node.as_str(), SEPARATOR);
2149
2150        assert!(node.push("width").as_str().starts_with(&boundary));
2151        assert!(!node.as_str().starts_with(&boundary));
2152        assert!(node.starts_with(&node));
2153    }
2154
2155    #[test]
2156    fn a_key_carries_what_a_glob_pattern_reads() {
2157        let path = StorePath::segment("a*b[c]?\u{0}d");
2158
2159        assert_eq!(path.as_str(), "a*b[c]?\u{0}d");
2160    }
2161
2162    #[test]
2163    fn a_path_hashes_by_its_levels_however_it_arrived() {
2164        let built = StorePath::from_segments(["ui", "dark.mode"]);
2165        let parsed = StorePath::parse_joined("ui.dark\\.mode").expect("the spelling parses");
2166
2167        assert_eq!(built, parsed);
2168        assert_eq!(hash_of(&built), hash_of(&parsed));
2169    }
2170
2171    #[test]
2172    fn two_levels_do_not_hash_as_the_one_they_spell() {
2173        let two = StorePath::from_segments(["a", "b"]);
2174        let one = StorePath::segment("ab");
2175
2176        assert_ne!(two, one);
2177        assert_ne!(hash_of(&two), hash_of(&one));
2178    }
2179
2180    #[test]
2181    fn a_path_flattened_to_one_level_is_named_by_what_it_spells() {
2182        let flat = StorePath::from_segments(["ui", "theme"]).as_one_level();
2183
2184        assert_eq!(flat.len(), 1);
2185        assert_eq!(flat.name().expect("one level has a name"), "ui.theme");
2186    }
2187
2188    #[test]
2189    fn a_level_holding_a_separator_flattens_apart_from_the_two_it_reads_as() {
2190        let one = StorePath::segment("ui.theme").as_one_level();
2191        let two = StorePath::from_segments(["ui", "theme"]).as_one_level();
2192
2193        assert_ne!(one, two);
2194        assert_eq!(
2195            StorePath::parse_joined(one.name().expect("one level has a name").as_str())
2196                .expect("the spelling parses"),
2197            StorePath::segment("ui.theme")
2198        );
2199    }
2200
2201    #[test]
2202    fn two_spellings_of_one_name_are_two_paths() {
2203        let precomposed = StorePath::segment("caf\u{e9}");
2204        let decomposed = StorePath::segment("cafe\u{301}");
2205
2206        assert_ne!(precomposed, decomposed);
2207        assert_ne!(precomposed.as_str(), decomposed.as_str());
2208        assert_eq!(precomposed.to_string().chars().count(), 4);
2209        assert_eq!(decomposed.to_string().chars().count(), 5);
2210    }
2211
2212    #[test]
2213    fn walking_a_document_borrows_each_level() {
2214        let path = StorePath::from_segments(["ui", "window", "width"]);
2215
2216        assert_eq!(
2217            path.segments()
2218                .map(|l| l.as_str().to_string())
2219                .collect::<Vec<_>>(),
2220            ["ui", "window", "width"]
2221        );
2222        assert_eq!(
2223            path.segment_at(1).as_ref().map(Level::as_str),
2224            Some("window")
2225        );
2226        assert_eq!(path.segment_at(3), None);
2227    }
2228
2229    #[test]
2230    fn a_static_path_is_the_same_path() {
2231        static UI_WIDTH: StorePath = StorePath::from_static(&["ui", "width"], "ui.width");
2232
2233        assert_eq!(UI_WIDTH, StorePath::from_segments(["ui", "width"]));
2234        assert_eq!(UI_WIDTH.as_str(), "ui.width");
2235        assert_eq!(UI_WIDTH.name().as_ref().map(Level::as_str), Some("width"));
2236        assert!(UI_WIDTH.starts_with(&StorePath::from_static(&["ui"], "ui")));
2237    }
2238
2239    #[test]
2240    fn a_static_path_carries_what_a_name_holds() {
2241        static ODD: StorePath =
2242            StorePath::from_static(&["dark.mode", "a\\b"], "dark\\.mode.a\\\\b");
2243        static NOTHING: StorePath = StorePath::from_static(&[], "");
2244
2245        assert_eq!(ODD, StorePath::from_segments(["dark.mode", "a\\b"]));
2246        assert_eq!(
2247            ODD.as_str(),
2248            StorePath::from_segments(["dark.mode", "a\\b"]).as_str()
2249        );
2250        assert_eq!(NOTHING, StorePath::root());
2251    }
2252}