1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
#![allow(dead_code)]
//! A representation of a JSON Pointer with associated operations, as per RFC 6901
//!
//!
use std::{borrow::Cow, collections::VecDeque, fmt::Display, ops::Add};

/// Each pointer is a series of segments delineated by a separator char
const PATH_SEPARATOR: char = '/';
/// As per the RFC, we need to encode any tilde characters as ~0
const ENCODED_TILDE: &str = "~0";
/// As per the RFC, we need to encode any slash characters as ~1
const ENCODED_SLASH: &str = "~1";

/// Each pointer is made of one of three different component types
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum JsonPointerComponent<'a> {
    /// Root element of a pointer
    Root,
    /// A named element within a pointer
    Name(Cow<'a, str>),
    /// An indexed element within a pointer
    Index(usize),
}

impl<'a> Display for JsonPointerComponent<'a> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Self::Root => write!(f, ""),
            Self::Name(s) => write!(
                f,
                "{}",
                &s.replace("~", ENCODED_TILDE).replace("/", ENCODED_SLASH)
            ),
            Self::Index(i) => write!(f, "{}", i),
        }
    }
}

/// A structure representing a complete pointer, comprising multiple [JsonPointerComponent]s
#[derive(Debug, Default, Hash, Eq)]
pub struct JsonPointer<'a> {
    /// The components that go together to make up the pointer
    components: VecDeque<JsonPointerComponent<'a>>,
}

impl<'a> PartialEq for JsonPointer<'a> {
    fn eq(&self, other: &Self) -> bool {
        self.matches(other)
    }
}

impl<'a> JsonPointer<'a> {
    /// Returns a root [JsonPointer] instance
    pub fn root() -> Self {
        let mut ptr = JsonPointer::default();
        ptr.components.push_back(JsonPointerComponent::Root);
        ptr
    }

    /// Checks to see if a pointer is just a top level root element
    pub fn is_root(&self) -> bool {
        return !self.components.is_empty()
            && self.components.len() == 1
            && self.components[0] == JsonPointerComponent::Root;
    }

    /// Returns the number of [JsonPointerComponent]s within the pointer
    pub fn len(&self) -> usize {
        self.components.len()
    }

    /// Checks whether the pointer is the empty pointer
    pub fn is_empty(&self) -> bool {
        self.components.is_empty()
    }

    /// Push a whole bunch of names onto the end of the path in order
    pub fn push_names(&mut self, names: &[&'a str]) {
        names.iter().for_each(|n| self.push_name(n.to_string()))
    }

    /// Push a whole bunch of indexes onto the end of the path in order
    pub fn push_indexes(&mut self, indexes: &[usize]) {
        indexes.iter().for_each(|i| self.push_index(*i))
    }

    /// Push a new [JsonPointerComponent::Name] onto the end of the pointer
    pub fn push_name(&mut self, name: String) {
        if self.is_empty() {
            self.components.push_back(JsonPointerComponent::Root)
        }
        self.components
            .push_back(JsonPointerComponent::Name(Cow::Owned(name)))
    }

    /// Push a new [JsonPointerComponent::Index] onto the end of the pointer
    pub fn push_index(&mut self, index: usize) {
        if self.is_empty() {
            self.components.push_back(JsonPointerComponent::Root)
        }
        self.components
            .push_back(JsonPointerComponent::Index(index))
    }

    /// Pop the last component off the back of the pointer
    pub fn pop(&mut self) -> Option<JsonPointerComponent<'a>> {
        self.components.pop_back()
    }

    /// Checks whether a path matches another path.
    pub fn matches(&self, rhs: &'a JsonPointer) -> bool {
        self.as_str() == rhs.as_str()
    }

    /// Serialise the pointer into a string representation that's compliant with RFC 6901
    pub fn as_str(&self) -> Cow<'a, str> {
        // check to see if we are a top level root
        if self.is_root() {
            return Cow::Owned("/".to_string());
        }

        // check to see if we are truly empty
        if self.is_empty() {
            return Cow::Owned("".to_string());
        }

        // otherwise, we have multiple components
        Cow::Owned(
            self.components
                .iter()
                .map(|c| c.to_string())
                .collect::<Vec<String>>()
                .join("/"),
        )
    }
}

impl<'a> Display for JsonPointer<'a> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", self.as_str())
    }
}

impl<'a> Add<&JsonPointer<'a>> for JsonPointer<'a> {
    type Output = Self;

    /// Concatenate two [JsonPointer] instances.
    fn add(mut self, rhs: &JsonPointer<'a>) -> Self {
        rhs.components
            .iter()
            .for_each(|c| self.components.push_back(c.clone()));
        self
    }
}

#[cfg(test)]
mod tests {
    use super::JsonPointer;

    #[test]
    fn an_empty_pointer_should_be_represented_by_an_empty_string() {
        let s = JsonPointer::default().as_str();
        assert_eq!(s, "")
    }

    #[test]
    fn a_root_pointer_should_be_represented_by_a_single_slash() {
        let s = JsonPointer::root().as_str();
        assert_eq!(s, "/")
    }

    #[test]
    fn pointers_should_serialise_correctly() {
        let mut s = JsonPointer::default();
        s.push_names(&vec!["a", "b"]);
        assert_eq!("/a/b", s.as_str())
    }

    #[test]
    fn pointers_should_serialise_with_escapes_correctly() {
        let mut s = JsonPointer::default();
        s.push_names(&vec!["a/b", "c~d"]);
        s.push_index(3);
        assert_eq!("/a~1b/c~0d/3", s.as_str())
    }

    #[test]
    fn popping_should_shorten_pointers_correctly() {
        let mut s = JsonPointer::default();
        s.push_names(&vec!["a", "b", "c"]);
        assert_eq!("/a/b/c", s.as_str());
        s.pop();
        assert_eq!("/a/b", s.as_str())
    }

    #[test]
    fn popping_all_components_should_result_in_a_root_pointer() {
        let mut s = JsonPointer::default();
        s.push_names(&vec!["a", "b", "c"]);
        s.pop();
        s.pop();
        s.pop();
        assert_eq!("/", s.as_str())
    }

    #[test]
    fn pointers_should_serialise_indices_correctly() {
        let mut s = JsonPointer::default();
        s.push_index(0);
        s.push_index(3);
        s.push_index(2);
        assert_eq!("/0/3/2", s.as_str())
    }

    #[test]
    fn pointers_should_match() {
        let mut s = JsonPointer::default();
        let mut t = JsonPointer::default();
        s.push_name("b".to_string());
        s.push_index(9);
        t.push_name("b".to_string());
        t.push_index(9);
        assert!(s.matches(&t))
    }

    #[test]
    fn pointers_should_match_using_equality_ops() {
        let mut s = JsonPointer::default();
        let mut t = JsonPointer::default();
        let mut u = JsonPointer::default();
        s.push_name("b".to_string());
        s.push_index(9);
        t.push_name("b".to_string());
        t.push_index(9);
        u.push_name("x".to_string());
        assert_eq!(s, t);
        assert_ne!(t, u);
        assert_ne!(s, u)
    }
}