1use std::fmt::{self, Display};
7
8#[derive(Debug, Clone, PartialEq, Eq, Hash)]
12pub enum PathSegment {
13 Field(String),
15 Index(usize),
17}
18
19impl PathSegment {
20 pub fn field(name: impl Into<String>) -> Self {
22 PathSegment::Field(name.into())
23 }
24
25 pub fn index(idx: usize) -> Self {
27 PathSegment::Index(idx)
28 }
29}
30
31#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)]
49pub struct JsonPath {
50 segments: Vec<PathSegment>,
51}
52
53impl JsonPath {
54 pub fn root() -> Self {
56 Self::default()
57 }
58
59 pub fn from_field(name: impl Into<String>) -> Self {
61 Self {
62 segments: vec![PathSegment::Field(name.into())],
63 }
64 }
65
66 pub fn from_index(idx: usize) -> Self {
68 Self {
69 segments: vec![PathSegment::Index(idx)],
70 }
71 }
72
73 pub fn push_field(&self, name: impl Into<String>) -> Self {
77 let mut segments = self.segments.clone();
78 segments.push(PathSegment::Field(name.into()));
79 Self { segments }
80 }
81
82 pub fn push_index(&self, index: usize) -> Self {
86 let mut segments = self.segments.clone();
87 segments.push(PathSegment::Index(index));
88 Self { segments }
89 }
90
91 pub fn is_root(&self) -> bool {
93 self.segments.is_empty()
94 }
95
96 pub fn len(&self) -> usize {
98 self.segments.len()
99 }
100
101 pub fn is_empty(&self) -> bool {
103 self.segments.is_empty()
104 }
105
106 pub fn segments(&self) -> impl Iterator<Item = &PathSegment> {
108 self.segments.iter()
109 }
110
111 pub fn parent(&self) -> Option<Self> {
113 if self.segments.is_empty() {
114 None
115 } else {
116 Some(Self {
117 segments: self.segments[..self.segments.len() - 1].to_vec(),
118 })
119 }
120 }
121
122 pub fn last(&self) -> Option<&PathSegment> {
124 self.segments.last()
125 }
126}
127
128impl Display for JsonPath {
129 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
130 for (i, segment) in self.segments.iter().enumerate() {
131 match segment {
132 PathSegment::Field(name) => {
133 if i > 0 {
134 write!(f, ".")?;
135 }
136 write!(f, "{}", name)?;
137 }
138 PathSegment::Index(idx) => write!(f, "[{}]", idx)?,
139 }
140 }
141 Ok(())
142 }
143}
144
145#[cfg(test)]
146mod tests {
147 use super::*;
148
149 #[test]
150 fn test_root_path_is_empty() {
151 let path = JsonPath::root();
152 assert!(path.is_root());
153 assert!(path.is_empty());
154 assert_eq!(path.len(), 0);
155 assert_eq!(path.to_string(), "");
156 }
157
158 #[test]
159 fn test_single_field() {
160 let path = JsonPath::root().push_field("user");
161 assert_eq!(path.to_string(), "user");
162 assert_eq!(path.len(), 1);
163 }
164
165 #[test]
166 fn test_single_index() {
167 let path = JsonPath::root().push_index(0);
168 assert_eq!(path.to_string(), "[0]");
169 }
170
171 #[test]
172 fn test_nested_fields() {
173 let path = JsonPath::root().push_field("user").push_field("email");
174 assert_eq!(path.to_string(), "user.email");
175 }
176
177 #[test]
178 fn test_field_with_index() {
179 let path = JsonPath::root().push_field("users").push_index(0);
180 assert_eq!(path.to_string(), "users[0]");
181 }
182
183 #[test]
184 fn test_complex_path() {
185 let path = JsonPath::root()
186 .push_field("users")
187 .push_index(0)
188 .push_field("email");
189 assert_eq!(path.to_string(), "users[0].email");
190 }
191
192 #[test]
193 fn test_deeply_nested() {
194 let path = JsonPath::root()
195 .push_field("body")
196 .push_field("data")
197 .push_index(42)
198 .push_field("items")
199 .push_index(0)
200 .push_field("name");
201 assert_eq!(path.to_string(), "body.data[42].items[0].name");
202 }
203
204 #[test]
205 fn test_path_immutability() {
206 let base = JsonPath::root().push_field("users");
207 let path_a = base.push_index(0);
208 let path_b = base.push_index(1);
209
210 assert_eq!(base.to_string(), "users");
211 assert_eq!(path_a.to_string(), "users[0]");
212 assert_eq!(path_b.to_string(), "users[1]");
213 }
214
215 #[test]
216 fn test_parent_path() {
217 let path = JsonPath::root()
218 .push_field("users")
219 .push_index(0)
220 .push_field("email");
221
222 let parent = path.parent().unwrap();
223 assert_eq!(parent.to_string(), "users[0]");
224
225 let grandparent = parent.parent().unwrap();
226 assert_eq!(grandparent.to_string(), "users");
227
228 let root = grandparent.parent().unwrap();
229 assert!(root.is_root());
230
231 assert!(root.parent().is_none());
232 }
233
234 #[test]
235 fn test_from_constructors() {
236 let field_path = JsonPath::from_field("name");
237 assert_eq!(field_path.to_string(), "name");
238
239 let index_path = JsonPath::from_index(5);
240 assert_eq!(index_path.to_string(), "[5]");
241 }
242
243 #[test]
244 fn test_last_segment() {
245 let path = JsonPath::root().push_field("users").push_index(0);
246 assert_eq!(path.last(), Some(&PathSegment::Index(0)));
247
248 let root = JsonPath::root();
249 assert_eq!(root.last(), None);
250 }
251
252 #[test]
253 fn test_segments_iterator() {
254 let path = JsonPath::root()
255 .push_field("a")
256 .push_index(1)
257 .push_field("b");
258
259 let segments: Vec<_> = path.segments().collect();
260 assert_eq!(segments.len(), 3);
261 assert_eq!(segments[0], &PathSegment::Field("a".to_string()));
262 assert_eq!(segments[1], &PathSegment::Index(1));
263 assert_eq!(segments[2], &PathSegment::Field("b".to_string()));
264 }
265
266 #[test]
267 fn test_equality() {
268 let path1 = JsonPath::root().push_field("a").push_index(0);
269 let path2 = JsonPath::root().push_field("a").push_index(0);
270 let path3 = JsonPath::root().push_field("a").push_index(1);
271
272 assert_eq!(path1, path2);
273 assert_ne!(path1, path3);
274 }
275
276 #[test]
277 fn test_clone() {
278 let path = JsonPath::root().push_field("test");
279 let cloned = path.clone();
280 assert_eq!(path, cloned);
281 }
282}