Skip to main content

lens/
path.rs

1//
2// Copyright (c) 2015 Plausible Labs Cooperative, Inc.
3// All rights reserved.
4//
5//
6// Copyright (c) 2025 Julius Foitzik on derivative work
7// All rights reserved.
8//
9
10use std::fmt;
11
12/// An element in a `LensPath`.
13#[derive(PartialEq, Eq, PartialOrd, Ord, Debug, Clone, Copy)]
14pub struct LensPathElement {
15    id: u64,
16}
17
18impl LensPathElement {
19    pub fn new(id: u64) -> LensPathElement {
20        LensPathElement { id }
21    }
22}
23
24/// Describes a lens relative to a source data structure.
25#[derive(PartialEq, Eq, PartialOrd, Ord, Clone)]
26pub struct LensPath {
27    /// The path elements.
28    pub elements: Vec<LensPathElement>,
29}
30
31impl LensPath {
32    /// Creates a new `LensPath` with no elements.
33    pub fn empty() -> LensPath {
34        LensPath { elements: vec![] }
35    }
36
37    /// Creates a new `LensPath` with a single element.
38    pub fn new(id: u64) -> LensPath {
39        LensPath {
40            elements: vec![LensPathElement::new(id)],
41        }
42    }
43
44    /// Creates a new `LensPath` with a single index (for an indexed type such as `Vec`).
45    pub fn from_index(index: usize) -> LensPath {
46        LensPath::new(index as u64)
47    }
48
49    /// Creates a new `LensPath` with two elements.
50    pub fn from_pair(id0: u64, id1: u64) -> LensPath {
51        LensPath {
52            elements: vec![LensPathElement::new(id0), LensPathElement::new(id1)],
53        }
54    }
55
56    /// Creates a new `LensPath` from a vector of element identifiers.
57    pub fn from_vec(ids: Vec<u64>) -> LensPath {
58        LensPath {
59            elements: ids.into_iter().map(LensPathElement::new).collect(),
60        }
61    }
62
63    /// Creates a new `LensPath` that is the concatenation of the two paths.
64    pub fn concat(lhs: LensPath, rhs: LensPath) -> LensPath {
65        let mut elements = lhs.elements;
66        elements.extend(rhs.elements);
67        LensPath { elements }
68    }
69}
70
71impl fmt::Debug for LensPath {
72    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
73        write!(
74            f,
75            "[{}]",
76            self.elements
77                .iter()
78                .map(|elem| elem.id.to_string())
79                .collect::<Vec<_>>()
80                .join(", ")
81        )
82    }
83}
84
85#[cfg(test)]
86mod tests {
87    use super::{LensPath, LensPathElement};
88
89    #[test]
90    fn lens_path_constructors_should_work() {
91        assert_eq!(LensPath::empty().elements, Vec::<LensPathElement>::new());
92        assert_eq!(LensPath::new(4).elements, vec![LensPathElement::new(4)]);
93        assert_eq!(LensPath::from_index(2), LensPath::new(2));
94        assert_eq!(
95            LensPath::from_pair(1, 3).elements,
96            vec![LensPathElement::new(1), LensPathElement::new(3)]
97        );
98        assert_eq!(
99            LensPath::from_vec(vec![1, 2, 3]).elements,
100            vec![
101                LensPathElement::new(1),
102                LensPathElement::new(2),
103                LensPathElement::new(3),
104            ]
105        );
106    }
107
108    #[test]
109    fn lens_path_concat_should_work() {
110        let p0 = LensPath::from_vec(vec![1, 2, 3]);
111        let p1 = LensPath::from_vec(vec![4, 5]);
112        let p2 = LensPath::concat(p0, p1);
113        assert_eq!(p2, LensPath::from_vec(vec![1, 2, 3, 4, 5]));
114    }
115
116    #[test]
117    fn lens_path_debug_should_work() {
118        let path = LensPath::from_vec(vec![1, 2, 3, 4, 5]);
119        assert_eq!(format!("{path:?}"), "[1, 2, 3, 4, 5]");
120    }
121}