1mod error;
29mod segment;
30
31pub use error::*;
32pub use segment::*;
33
34#[derive(Clone, Debug, PartialEq, Eq)]
53pub struct MetaPath {
54 segments: Vec<Segment>,
55}
56
57impl MetaPath {
58 pub fn parse(s: &str) -> Result<Self, ParseError> {
72 if s.is_empty() {
73 return Err(ParseError::Empty);
74 }
75
76 let mut segments = Vec::new();
77 let mut chars = s.chars().peekable();
78 let mut buf = String::new();
79
80 while let Some(&ch) = chars.peek() {
81 match ch {
82 '.' => {
83 chars.next();
84
85 if !buf.is_empty() {
86 segments.push(Segment::Key(std::mem::take(&mut buf)));
87 }
88 }
89 '[' => {
90 chars.next();
91
92 if !buf.is_empty() {
93 segments.push(Segment::Key(std::mem::take(&mut buf)));
94 }
95
96 let mut num = String::new();
97 let mut closed = false;
98
99 while let Some(&c) = chars.peek() {
100 if c == ']' {
101 chars.next();
102 closed = true;
103 break;
104 }
105
106 num.push(c);
107 chars.next();
108 }
109
110 if !closed {
111 return Err(ParseError::UnclosedBracket);
112 }
113
114 let index = num
115 .parse::<usize>()
116 .map_err(|_| ParseError::InvalidIndex(num))?;
117
118 segments.push(Segment::Index(index));
119 }
120 _ => {
121 buf.push(ch);
122 chars.next();
123 }
124 }
125 }
126
127 if !buf.is_empty() {
128 segments.push(Segment::Key(buf));
129 }
130
131 Ok(Self { segments })
132 }
133
134 pub fn segments(&self) -> &[Segment] {
136 &self.segments
137 }
138
139 pub fn len(&self) -> usize {
141 self.segments.len()
142 }
143
144 pub fn is_empty(&self) -> bool {
146 self.segments.is_empty()
147 }
148
149 pub fn first(&self) -> Option<&Segment> {
151 self.segments.first()
152 }
153
154 pub fn tail(&self) -> Self {
166 Self {
167 segments: self.segments.get(1..).unwrap_or_default().to_vec(),
168 }
169 }
170}
171
172impl std::str::FromStr for MetaPath {
173 type Err = ParseError;
174
175 fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
176 Self::parse(s)
177 }
178}
179
180impl std::fmt::Display for MetaPath {
181 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
182 let mut first = true;
183
184 for seg in &self.segments {
185 match seg {
186 Segment::Key(k) => {
187 if !first {
188 write!(f, ".")?;
189 }
190
191 write!(f, "{}", k)?;
192 }
193 Segment::Index(i) => {
194 write!(f, "[{}]", i)?;
195 }
196 }
197
198 first = false;
199 }
200
201 Ok(())
202 }
203}
204
205#[cfg(test)]
206mod tests {
207 use super::*;
208
209 mod parse {
210 use super::*;
211
212 #[test]
213 fn single_key() {
214 let path = MetaPath::parse("serde").unwrap();
215 assert_eq!(path.segments(), &[Segment::Key("serde".into())]);
216 }
217
218 #[test]
219 fn dotted_keys() {
220 let path = MetaPath::parse("serde.rename").unwrap();
221 assert_eq!(
222 path.segments(),
223 &[Segment::Key("serde".into()), Segment::Key("rename".into()),],
224 );
225 }
226
227 #[test]
228 fn index_after_key() {
229 let path = MetaPath::parse("derive[0]").unwrap();
230 assert_eq!(
231 path.segments(),
232 &[Segment::Key("derive".into()), Segment::Index(0)],
233 );
234 }
235
236 #[test]
237 fn mixed_path() {
238 let path = MetaPath::parse("a.b[2].c").unwrap();
239 assert_eq!(
240 path.segments(),
241 &[
242 Segment::Key("a".into()),
243 Segment::Key("b".into()),
244 Segment::Index(2),
245 Segment::Key("c".into()),
246 ],
247 );
248 }
249
250 #[test]
251 fn empty_string_is_err() {
252 assert_eq!(MetaPath::parse(""), Err(ParseError::Empty));
253 }
254
255 #[test]
256 fn unclosed_bracket_is_err() {
257 assert_eq!(MetaPath::parse("a[0"), Err(ParseError::UnclosedBracket));
258 }
259
260 #[test]
261 fn invalid_index_is_err() {
262 assert!(matches!(
263 MetaPath::parse("a[abc]"),
264 Err(ParseError::InvalidIndex(_)),
265 ));
266 }
267 }
268
269 mod display {
270 use super::*;
271
272 #[test]
273 fn round_trip_dotted() {
274 let path = MetaPath::parse("serde.rename").unwrap();
275 assert_eq!(path.to_string(), "serde.rename");
276 }
277
278 #[test]
279 fn round_trip_indexed() {
280 let path = MetaPath::parse("derive[0]").unwrap();
281 assert_eq!(path.to_string(), "derive[0]");
282 }
283
284 #[test]
285 fn round_trip_mixed() {
286 let path = MetaPath::parse("a.b[2].c").unwrap();
287 assert_eq!(path.to_string(), "a.b[2].c");
288 }
289 }
290
291 mod tail {
292 use super::*;
293
294 #[test]
295 fn removes_first_segment() {
296 let path = MetaPath::parse("a.b.c").unwrap();
297 let tail = path.tail();
298 assert_eq!(
299 tail.segments(),
300 &[Segment::Key("b".into()), Segment::Key("c".into())],
301 );
302 }
303
304 #[test]
305 fn single_segment_gives_empty() {
306 let path = MetaPath::parse("a").unwrap();
307 let tail = path.tail();
308 assert!(tail.is_empty());
309 }
310 }
311}