1#![allow(dead_code)]
5
6mod rc_list;
7use std::borrow::Cow;
8
9use compact_str::{CompactString, ToCompactString};
10use smallvec::SmallVec;
11
12use self::rc_list::List;
13
14#[derive(Clone, Debug)]
22#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
23pub struct Report {
24 errors: Vec<(Path, Error)>,
25}
26
27impl Report {
28 #[allow(clippy::new_without_default)]
30 pub fn new() -> Self {
31 Self { errors: Vec::new() }
32 }
33
34 pub fn append(&mut self, path: Path, error: Error) {
36 self.errors.push((path, error));
37 }
38
39 pub fn iter(&self) -> impl Iterator<Item = &(Path, Error)> {
41 self.errors.iter()
42 }
43
44 pub fn is_empty(&self) -> bool {
46 self.errors.is_empty()
47 }
48
49 pub fn into_inner(self) -> Vec<(Path, Error)> {
51 self.errors
52 }
53}
54
55impl std::fmt::Display for Report {
56 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
57 for (path, error) in self.iter() {
58 if path.is_empty() {
59 writeln!(f, "{error}")?;
60 } else {
61 writeln!(f, "{path}: {error}")?;
62 }
63 }
64 Ok(())
65 }
66}
67
68impl std::error::Error for Report {}
69
70#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
71#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
72pub struct Error {
73 message: CompactString,
74}
75
76impl Error {
77 pub fn new(message: impl ToCompactString) -> Self {
78 Self {
79 message: message.to_compact_string(),
80 }
81 }
82
83 pub fn message(&self) -> &str {
84 self.message.as_ref()
85 }
86}
87
88impl std::fmt::Display for Error {
89 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
90 write!(f, "{}", self.message)
91 }
92}
93
94impl std::error::Error for Error {}
95
96#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
97pub struct Path {
98 components: List<(Kind, CompactString)>,
99}
100
101#[doc(hidden)]
102#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
103#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
104#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
105pub enum Kind {
106 None,
107 Key,
108 Index,
109}
110
111#[derive(Default)]
116pub struct NoKey(());
117
118impl std::fmt::Display for NoKey {
119 fn fmt(&self, _: &mut std::fmt::Formatter) -> std::fmt::Result {
120 Ok(())
121 }
122}
123
124pub trait PathComponentKind: std::fmt::Display + ToCompactString {
125 fn component_kind() -> Kind;
126}
127
128macro_rules! impl_path_component_kind {
129 ($(@$($G:lifetime)*;)? $T:ty => $which:ident) => {
130 impl $(<$($G),*>)? PathComponentKind for $T {
131 fn component_kind() -> Kind {
132 Kind::$which
133 }
134 }
135 }
136}
137
138impl_path_component_kind!(usize => Index);
139impl_path_component_kind!(@'a; &'a str => Key);
140impl_path_component_kind!(@'a; Cow<'a, str> => Key);
141impl_path_component_kind!(String => Key);
142impl_path_component_kind!(CompactString => Key);
143impl_path_component_kind!(NoKey => None);
144
145impl<T: PathComponentKind> PathComponentKind for &T {
146 fn component_kind() -> Kind {
147 T::component_kind()
148 }
149}
150
151impl Path {
152 pub fn empty() -> Self {
153 Self {
154 components: List::new(),
155 }
156 }
157
158 pub fn len(&self) -> usize {
159 self.components.len()
160 }
161
162 pub fn is_empty(&self) -> bool {
163 self.components.is_empty()
164 }
165
166 pub fn new<C: PathComponentKind>(component: C) -> Self {
167 Self {
168 components: List::new().append((C::component_kind(), component.to_compact_string())),
169 }
170 }
171
172 pub fn join<C: PathComponentKind>(&self, component: C) -> Self {
173 Self {
174 components: self
175 .components
176 .append((C::component_kind(), component.to_compact_string())),
177 }
178 }
179
180 #[doc(hidden)]
181 pub fn __iter(
182 &self,
183 ) -> impl DoubleEndedIterator<Item = (Kind, &CompactString)> + ExactSizeIterator {
184 let mut components = TempComponents::with_capacity(self.components.len());
185 for (kind, component) in self.components.iter() {
186 components.push((*kind, component));
187 }
188 components.into_iter()
189 }
190}
191
192type TempComponents<'a> = SmallVec<[(Kind, &'a CompactString); 8]>;
193
194impl std::fmt::Debug for Path {
195 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
196 struct Components<'a> {
197 path: &'a Path,
198 }
199
200 impl std::fmt::Debug for Components<'_> {
201 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
202 let mut list = f.debug_list();
203 list.entries(self.path.__iter().rev().map(|(_, c)| c))
204 .finish()
205 }
206 }
207
208 f.debug_struct("Path")
209 .field("components", &Components { path: self })
210 .finish()
211 }
212}
213
214impl std::fmt::Display for Path {
215 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
216 let mut components = self.__iter().rev().peekable();
217 let mut first = true;
218 while let Some((kind, component)) = components.next() {
219 if first && kind == Kind::Index {
220 f.write_str("[")?;
221 }
222 first = false;
223 f.write_str(component.as_str())?;
224 if kind == Kind::Index {
225 f.write_str("]")?;
226 }
227 if let Some((kind, _)) = components.peek() {
228 match kind {
229 Kind::None => {}
230 Kind::Key => f.write_str(".")?,
231 Kind::Index => f.write_str("[")?,
232 }
233 }
234 }
235
236 Ok(())
237 }
238}
239
240#[cfg(feature = "serde")]
241impl serde::Serialize for Path {
242 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
243 where
244 S: serde::Serializer,
245 {
246 use serde::ser::SerializeSeq as _;
247
248 let components = self.__iter().rev();
249 let mut seq = serializer.serialize_seq(Some(components.len()))?;
250 for component in components {
251 seq.serialize_element(&component)?;
252 }
253 seq.end()
254 }
255}
256
257#[cfg(feature = "serde")]
258impl<'de> serde::Deserialize<'de> for Path {
259 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
260 where
261 D: serde::Deserializer<'de>,
262 {
263 let mut components = List::new();
264 for v in SmallVec::<[(Kind, CompactString); 8]>::deserialize(deserializer)? {
265 components = components.append(v);
266 }
267 Ok(Path { components })
268 }
269}
270
271#[cfg(test)]
272mod tests {
273 use super::*;
274
275 const _: () = {
276 fn assert<T: Send>() {}
277 let _ = assert::<Report>;
278 };
279
280 #[test]
281 fn path_join() {
282 let path = Path::new("a").join("b").join("c");
283 assert_eq!(path.to_string(), "a.b.c");
284 }
285
286 #[test]
287 fn report_select() {
288 let mut report = Report::new();
289 report.append(Path::new("a").join("b"), Error::new("lol"));
290 report.append(
291 Path::new("a").join("b").join("c"),
292 Error::new("that seems wrong"),
293 );
294 report.append(Path::new("a").join("b").join("c"), Error::new("pog"));
295 report.append(Path::new("array").join("0").join("c"), Error::new("pog"));
296
297 assert_eq!(
298 crate::select!(report, a.b.c).collect::<Vec<_>>(),
299 [&Error::new("that seems wrong"), &Error::new("pog")]
300 );
301
302 assert_eq!(
303 crate::select!(report, array[0].c).collect::<Vec<_>>(),
304 [&Error::new("pog")]
305 );
306 }
307
308 #[cfg(feature = "serde")]
309 mod serde {
310 use super::*;
311
312 #[test]
313 fn roundtrip_serde() {
314 let mut report = Report::new();
315 report.append(Path::new("a").join(0), Error::new("lorem"));
316 report.append(Path::new("a").join(1), Error::new("ispum"));
317 report.append(Path::new("a").join(2), Error::new("dolor"));
318 report.append(Path::new("b").join("c"), Error::new("dolor"));
319
320 let de: Report =
321 serde_json::from_str(&serde_json::to_string(&report).unwrap()).unwrap();
322
323 assert_eq!(report.errors, de.errors);
324 }
325 }
326}