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