Skip to main content

morphix/
path.rs

1use std::borrow::Cow;
2use std::fmt::{Debug, Display};
3use std::ops::{Deref, DerefMut};
4
5/// A segment of a mutation path.
6///
7/// [`PathSegment`] represents a single step in navigating to a nested value:
8/// - [`String`](PathSegment::String): Access an object / struct field by name
9/// - [`Positive`](PathSegment::Positive): Access an array / vec element by index from the start
10/// - [`Negative`](PathSegment::Negative): Access an array / vec element by index from the end
11#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
12pub enum PathSegment {
13    /// A string key for accessing object/struct fields.
14    String(Cow<'static, str>),
15    /// A positive index for accessing elements from the start (0-based).
16    Positive(usize),
17    /// A negative index for accessing elements from the end (1-based, where 1 is the last element).
18    Negative(usize),
19}
20
21impl From<usize> for PathSegment {
22    fn from(n: usize) -> Self {
23        Self::Positive(n)
24    }
25}
26
27impl From<&'static str> for PathSegment {
28    fn from(s: &'static str) -> Self {
29        Self::String(Cow::Borrowed(s))
30    }
31}
32
33impl From<String> for PathSegment {
34    fn from(s: String) -> Self {
35        Self::String(Cow::Owned(s))
36    }
37}
38
39impl From<Cow<'static, str>> for PathSegment {
40    fn from(s: Cow<'static, str>) -> Self {
41        Self::String(s)
42    }
43}
44
45impl Display for PathSegment {
46    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
47        match self {
48            PathSegment::String(s) => write!(f, ".{s}"),
49            PathSegment::Positive(n) => write!(f, "[{n}]"),
50            PathSegment::Negative(n) => write!(f, "[-{n}]"),
51        }
52    }
53}
54
55/// A path to a nested value within a data structure.
56///
57/// [`Path`] is a sequence of [`PathSegment`]s that describes how to navigate from a root value to a
58/// nested value. The const parameter `REV` controls the internal storage order:
59/// - `Path<false>`: Segments stored in natural order (root to leaf)
60/// - `Path<true>`: Segments stored in reverse order (leaf to root), optimized for efficient `push`
61///   and `pop` operations during mutation collection
62#[derive(Default, Clone, PartialEq, Eq)]
63pub struct Path<const REV: bool>(Vec<PathSegment>);
64
65impl<const REV: bool> Path<REV> {
66    /// Creates a new empty path.
67    pub fn new() -> Self {
68        Self::default()
69    }
70}
71
72impl<const REV: bool> From<Vec<PathSegment>> for Path<REV> {
73    fn from(mut segments: Vec<PathSegment>) -> Self {
74        if REV {
75            segments.reverse();
76        }
77        Self(segments)
78    }
79}
80
81impl<const REV: bool> Display for Path<REV> {
82    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
83        if REV {
84            for segment in self.0.iter().rev() {
85                write!(f, "{segment}")?;
86            }
87        } else {
88            for segment in self.0.iter() {
89                write!(f, "{segment}")?;
90            }
91        };
92        Ok(())
93    }
94}
95
96impl<const REV: bool> Debug for Path<REV> {
97    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
98        f.debug_tuple("Path").field(&self.to_string()).finish()
99    }
100}
101
102impl<const REV: bool> Deref for Path<REV> {
103    type Target = Vec<PathSegment>;
104
105    fn deref(&self) -> &Self::Target {
106        &self.0
107    }
108}
109
110impl<const REV: bool> DerefMut for Path<REV> {
111    fn deref_mut(&mut self) -> &mut Self::Target {
112        &mut self.0
113    }
114}
115
116impl<const REV: bool> FromIterator<PathSegment> for Path<REV> {
117    fn from_iter<T: IntoIterator<Item = PathSegment>>(iter: T) -> Self {
118        let mut segments: Vec<PathSegment> = iter.into_iter().collect();
119        if REV {
120            segments.reverse();
121        }
122        Self(segments)
123    }
124}