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