1use crate::error;
4use std::{cmp::Ordering, convert::TryFrom, fmt, hash::Hash};
5
6#[derive(Debug, Clone, PartialEq, Eq, Hash)]
8pub enum Element {
9 Key(String),
10 Index(usize),
11}
12
13impl Element {
14 pub fn is_key(&self) -> bool {
15 matches!(self, Element::Key(_))
16 }
17
18 pub fn is_index(&self) -> bool {
19 matches!(self, Element::Index(_))
20 }
21}
22
23impl PartialOrd for Element {
24 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
25 Some(self.cmp(other))
26 }
27}
28
29impl Ord for Element {
30 fn cmp(&self, other: &Self) -> Ordering {
31 match (self, other) {
32 (Element::Key(this_key), Element::Key(other_key)) => this_key.cmp(other_key),
33 (Element::Index(this_idx), Element::Index(other_idx)) => this_idx.cmp(other_idx),
34 (Element::Key(_), Element::Index(_)) => Ordering::Less,
35 (Element::Index(_), Element::Key(_)) => Ordering::Greater,
36 }
37 }
38}
39
40impl fmt::Display for Element {
41 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
42 match self {
43 Self::Key(string) => write!(f, "{{\"{}\"}}", string),
44 Self::Index(idx) => write!(f, "[{}]", idx),
45 }
46 }
47}
48
49#[derive(Debug, Default, Clone, PartialEq, Eq, Hash)]
52pub struct Path {
53 path: Vec<Element>,
54}
55
56impl Path {
57 pub fn new() -> Self {
58 Self::default()
59 }
60
61 pub fn pop(&mut self) -> Option<Element> {
63 self.path.pop()
64 }
65
66 pub fn push(&mut self, element: Element) {
68 self.path.push(element);
69 }
70
71 pub fn depth(&self) -> usize {
73 self.path.len()
74 }
75
76 pub fn get_path(&self) -> &[Element] {
78 &self.path
79 }
80}
81
82#[derive(Debug, PartialEq)]
84enum PathState {
85 ElementStart,
86 Array,
87 ObjectStart,
88 Object(bool),
89 ObjectEnd,
90}
91
92impl TryFrom<&str> for Path {
93 type Error = error::Path;
94
95 fn try_from(path_str: &str) -> Result<Self, Self::Error> {
96 let mut state = PathState::ElementStart;
97 let mut path = Self::new();
98 let mut buffer = vec![];
99 for chr in path_str.chars() {
100 state = match state {
101 PathState::ElementStart => match chr {
102 '[' => PathState::Array,
103 '{' => PathState::ObjectStart,
104 _ => return Err(error::Path::new(path_str)),
105 },
106 PathState::Array => match chr {
107 '0'..='9' => {
108 buffer.push(chr);
109 PathState::Array
110 }
111 ']' => {
112 let idx: usize = buffer.drain(..).collect::<String>().parse().unwrap();
113 path.push(Element::Index(idx));
114 PathState::ElementStart
115 }
116 _ => return Err(error::Path::new(path_str)),
117 },
118 PathState::ObjectStart => {
119 if chr == '"' {
120 PathState::Object(false)
121 } else {
122 return Err(error::Path::new(path_str));
123 }
124 }
125 PathState::Object(escaped) => {
126 if escaped {
127 buffer.push(chr);
128 PathState::Object(false)
129 } else {
130 match chr {
131 '\\' => {
132 buffer.push(chr);
133 PathState::Object(true)
134 }
135 '"' => PathState::ObjectEnd,
136 _ => {
137 buffer.push(chr);
138 PathState::Object(false)
139 }
140 }
141 }
142 }
143 PathState::ObjectEnd => {
144 if chr == '}' {
145 let key: String = buffer.drain(..).collect();
146 path.push(Element::Key(key));
147 PathState::ElementStart
148 } else {
149 return Err(error::Path::new(path_str));
150 }
151 }
152 };
153 }
154 if state == PathState::ElementStart {
155 Ok(path)
156 } else {
157 Err(error::Path::new(path_str))
158 }
159 }
160}
161
162impl fmt::Display for Path {
163 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
164 for element in &self.path {
165 write!(f, "{}", element)?;
166 }
167 Ok(())
168 }
169}
170
171impl Ord for Path {
172 fn cmp(&self, other: &Self) -> Ordering {
173 for (a, b) in self.path.iter().zip(other.path.iter()) {
174 let res = a.cmp(b);
175 if res != Ordering::Equal {
176 return res;
177 }
178 }
179 match (self.path.len(), other.path.len()) {
180 (a_len, b_len) if a_len < b_len => Ordering::Less,
181 (a_len, b_len) if a_len == b_len => Ordering::Equal,
182 (a_len, b_len) if a_len > b_len => Ordering::Greater,
183 (_, _) => unreachable!(),
184 }
185 }
186}
187
188impl PartialOrd for Path {
189 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
190 Some(self.cmp(other))
191 }
192}
193
194#[cfg(test)]
195mod tests {
196 use super::{Element, Path};
197 use std::convert::TryFrom;
198
199 #[test]
200 fn test_path_from_string_empty() {
201 assert!(Path::try_from("").is_ok());
202 }
203
204 #[test]
205 fn test_path_from_string_array() {
206 let mut path = Path::new();
207 path.push(Element::Index(0));
208 assert_eq!(Path::try_from("[0]").unwrap(), path);
209 }
210
211 #[test]
212 fn test_path_from_string_object() {
213 let mut path = Path::new();
214 path.push(Element::Key(r#"my-ke\\y\" "#.into()));
215 assert_eq!(Path::try_from(r#"{"my-ke\\y\" "}"#).unwrap(), path);
216 }
217}