chisel_json/pointers/
pointer.rs1use std::{borrow::Cow, collections::VecDeque, fmt::Display, ops::Add};
5
6const PATH_SEPARATOR: char = '/';
8const ENCODED_TILDE: &str = "~0";
10const ENCODED_SLASH: &str = "~1";
12
13#[derive(Debug, Clone, PartialEq, Eq, Hash)]
15pub enum JsonPointerComponent<'a> {
16 Root,
18 Name(Cow<'a, str>),
20 Index(usize),
22}
23
24impl<'a> Display for JsonPointerComponent<'a> {
25 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
26 match self {
27 Self::Root => write!(f, ""),
28 Self::Name(s) => write!(
29 f,
30 "{}",
31 &s.replace("~", ENCODED_TILDE).replace("/", ENCODED_SLASH)
32 ),
33 Self::Index(i) => write!(f, "{}", i),
34 }
35 }
36}
37
38#[derive(Debug, Default, Hash, Eq)]
40pub struct JsonPointer<'a> {
41 components: VecDeque<JsonPointerComponent<'a>>,
43}
44
45impl<'a> PartialEq for JsonPointer<'a> {
46 fn eq(&self, other: &Self) -> bool {
47 self.matches(other)
48 }
49}
50
51impl<'a> JsonPointer<'a> {
52 pub fn root() -> Self {
54 let mut ptr = JsonPointer::default();
55 ptr.components.push_back(JsonPointerComponent::Root);
56 ptr
57 }
58
59 pub fn is_root(&self) -> bool {
61 return !self.components.is_empty()
62 && self.components.len() == 1
63 && self.components[0] == JsonPointerComponent::Root;
64 }
65
66 pub fn len(&self) -> usize {
68 self.components.len()
69 }
70
71 pub fn is_empty(&self) -> bool {
73 self.components.is_empty()
74 }
75
76 pub fn push_names(&mut self, names: &[&'a str]) {
78 names.iter().for_each(|n| self.push_name(n.to_string()))
79 }
80
81 pub fn push_indexes(&mut self, indexes: &[usize]) {
83 indexes.iter().for_each(|i| self.push_index(*i))
84 }
85
86 pub fn push_name(&mut self, name: String) {
88 if self.is_empty() {
89 self.components.push_back(JsonPointerComponent::Root)
90 }
91 self.components
92 .push_back(JsonPointerComponent::Name(Cow::Owned(name)))
93 }
94
95 pub fn push_index(&mut self, index: usize) {
97 if self.is_empty() {
98 self.components.push_back(JsonPointerComponent::Root)
99 }
100 self.components
101 .push_back(JsonPointerComponent::Index(index))
102 }
103
104 pub fn pop(&mut self) -> Option<JsonPointerComponent<'a>> {
106 self.components.pop_back()
107 }
108
109 pub fn matches(&self, rhs: &'a JsonPointer) -> bool {
111 self.as_str() == rhs.as_str()
112 }
113
114 pub fn as_str(&self) -> Cow<'a, str> {
116 if self.is_root() {
118 return Cow::Owned("/".to_string());
119 }
120
121 if self.is_empty() {
123 return Cow::Owned("".to_string());
124 }
125
126 Cow::Owned(
128 self.components
129 .iter()
130 .map(|c| c.to_string())
131 .collect::<Vec<String>>()
132 .join("/"),
133 )
134 }
135}
136
137impl<'a> Display for JsonPointer<'a> {
138 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
139 write!(f, "{}", self.as_str())
140 }
141}
142
143impl<'a> Add<&JsonPointer<'a>> for JsonPointer<'a> {
144 type Output = Self;
145
146 fn add(mut self, rhs: &JsonPointer<'a>) -> Self {
148 rhs.components
149 .iter()
150 .for_each(|c| self.components.push_back(c.clone()));
151 self
152 }
153}
154
155#[cfg(test)]
156mod tests {
157 use super::JsonPointer;
158
159 #[test]
160 fn an_empty_pointer_should_be_represented_by_an_empty_string() {
161 let s = JsonPointer::default().as_str();
162 assert_eq!(s, "")
163 }
164
165 #[test]
166 fn a_root_pointer_should_be_represented_by_a_single_slash() {
167 let s = JsonPointer::root().as_str();
168 assert_eq!(s, "/")
169 }
170
171 #[test]
172 fn pointers_should_serialise_correctly() {
173 let mut s = JsonPointer::default();
174 s.push_names(&vec!["a", "b"]);
175 assert_eq!("/a/b", s.as_str())
176 }
177
178 #[test]
179 fn pointers_should_serialise_with_escapes_correctly() {
180 let mut s = JsonPointer::default();
181 s.push_names(&vec!["a/b", "c~d"]);
182 s.push_index(3);
183 assert_eq!("/a~1b/c~0d/3", s.as_str())
184 }
185
186 #[test]
187 fn popping_should_shorten_pointers_correctly() {
188 let mut s = JsonPointer::default();
189 s.push_names(&vec!["a", "b", "c"]);
190 assert_eq!("/a/b/c", s.as_str());
191 s.pop();
192 assert_eq!("/a/b", s.as_str())
193 }
194
195 #[test]
196 fn popping_all_components_should_result_in_a_root_pointer() {
197 let mut s = JsonPointer::default();
198 s.push_names(&vec!["a", "b", "c"]);
199 s.pop();
200 s.pop();
201 s.pop();
202 assert_eq!("/", s.as_str())
203 }
204
205 #[test]
206 fn pointers_should_serialise_indices_correctly() {
207 let mut s = JsonPointer::default();
208 s.push_index(0);
209 s.push_index(3);
210 s.push_index(2);
211 assert_eq!("/0/3/2", s.as_str())
212 }
213
214 #[test]
215 fn pointers_should_match() {
216 let mut s = JsonPointer::default();
217 let mut t = JsonPointer::default();
218 s.push_name("b".to_string());
219 s.push_index(9);
220 t.push_name("b".to_string());
221 t.push_index(9);
222 assert!(s.matches(&t))
223 }
224
225 #[test]
226 fn pointers_should_match_using_equality_ops() {
227 let mut s = JsonPointer::default();
228 let mut t = JsonPointer::default();
229 let mut u = JsonPointer::default();
230 s.push_name("b".to_string());
231 s.push_index(9);
232 t.push_name("b".to_string());
233 t.push_index(9);
234 u.push_name("x".to_string());
235 assert_eq!(s, t);
236 assert_ne!(t, u);
237 assert_ne!(s, u)
238 }
239}