1use facet::{Def, Facet, PointerType, Type, UserType};
2use facet_reflect::{HasFields, Peek};
3
4#[derive(Facet, Debug, Clone, PartialEq, Eq, Hash)]
8pub struct FacetPath {
9 pub segments: Vec<String>,
10}
11
12impl FacetPath {
13 pub fn root() -> Self {
14 FacetPath {
15 segments: vec!["$".to_string()],
16 }
17 }
18
19 pub fn push(&mut self, path: &FacetPath) {
20 self.segments.extend(path.segments.iter().cloned());
21 }
22
23 pub fn join(&self, path: &FacetPath) -> Self {
24 let mut new_path = self.clone();
25 new_path.push(path);
26 new_path
27 }
28}
29
30impl From<&str> for FacetPath {
31 fn from(path: &str) -> Self {
32 FacetPath {
33 segments: path.split('.').map(|s| s.to_string()).collect(),
34 }
35 }
36}
37
38#[derive(Clone)]
42pub struct FacetIterator<'mem, 'facet> {
43 stack: Vec<(FacetPath, Peek<'mem, 'facet>)>,
44}
45
46impl<'mem, 'facet> Iterator for FacetIterator<'mem, 'facet> {
47 type Item = (FacetPath, Peek<'mem, 'facet>);
48
49 fn next(&mut self) -> Option<Self::Item> {
50 let Some((path, peek)) = self.stack.pop() else {
51 return None; };
53
54 let def = peek.shape().def;
55 let ty = peek.shape().ty;
56
57 match (def, ty) {
58 (Def::Scalar, _) => {} (Def::Array(_), _) | (Def::List(_), _) | (Def::Slice(_), _) => {
60 self.push_list_items_to_stack(
61 &path,
62 peek.into_list_like().expect("Expected a list").iter(),
63 );
64 }
65 (Def::Map(_), _) => {
66 self.push_map_items_to_stack(
67 &path,
68 peek.into_map().expect("Expected a map").iter(),
69 );
70 }
71 (_, Type::User(UserType::Struct(_))) => {
72 self.push_fields_to_stack(&path, peek.into_struct().expect("Expected a struct"));
73 }
74 (_, Type::User(UserType::Enum(_))) => {
75 self.push_fields_to_stack(&path, peek.into_enum().expect("Expected an enum"));
76 }
77 (_, Type::Sequence(_)) => {
78 self.push_list_items_to_stack(
80 &path,
81 peek.into_list_like().expect("Expected a sequence").iter(),
82 );
83 }
84 (_, Type::Pointer(PointerType::Reference(r))) => {
85 let target = (r.target)();
86 if let Type::Sequence(_) = target.ty {
87 self.push_list_items_to_stack(
88 &path,
89 peek.into_list_like().expect("Expected a sequence").iter(),
90 );
91 }
92 }
93 (_, _) => {
94 todo!(
96 "this type is not yet supported for inspection\ndef:{:?}\nty:{:?}",
97 def,
98 ty
99 );
100 }
101 }
102
103 Some((path, peek))
104 }
105}
106
107impl<'mem, 'facet> FacetIterator<'mem, 'facet> {
108 fn push_fields_to_stack(
109 &mut self,
110 parent_path: &FacetPath,
111 object: impl HasFields<'mem, 'facet>,
112 ) {
113 for (field, peek) in object.fields().rev() {
116 let new_path = parent_path.join(&field.name.into());
117 self.stack.push((new_path, peek));
118 }
119 }
120
121 fn push_list_items_to_stack(
122 &mut self,
123 parent_path: &FacetPath,
124 list: impl Iterator<Item = Peek<'mem, 'facet>>,
125 ) {
126 for (index, item) in list.enumerate() {
127 let new_path = parent_path.join(&FacetPath::from(index.to_string().as_str()));
128 self.stack.push((new_path, item));
129 }
130 }
131
132 fn push_map_items_to_stack(
133 &mut self,
134 parent_path: &FacetPath,
135 map: impl Iterator<Item = (Peek<'mem, 'facet>, Peek<'mem, 'facet>)>,
136 ) {
137 for (key, value_peek) in map {
138 let new_path = parent_path.join(&FacetPath::from(format!("{key}").as_str()));
139 self.stack.push((new_path, value_peek));
140 }
141 }
142}
143
144pub trait FacetInspect<'a>: Facet<'a> {
145 fn inspect(&'a self) -> FacetIterator<'a, 'a> {
149 FacetIterator {
150 stack: vec![(FacetPath::root(), Peek::new(self))], }
152 }
153
154 fn get(&'a self, path: &FacetPath) -> Option<Peek<'a, 'a>> {
158 self.inspect()
159 .find(|(p, _)| p == path)
160 .map(|(_, peek)| peek)
161 }
162}
163
164impl<'a, T: Facet<'a>> FacetInspect<'a> for T {}
165
166#[cfg(test)]
167mod tests {
168 use super::FacetInspect;
169 use super::*;
170 use std::collections::HashMap;
171
172 #[derive(Facet)]
173 struct TestFacet {
174 field1: u32,
175 field2: String,
176 }
177
178 #[derive(Facet)]
179 struct NestedFacet {
180 nested_field: TestFacet,
181 }
182
183 #[derive(Facet, Debug, PartialEq)]
184 #[repr(u8)]
185 enum MyEnum {
186 Unit,
187 Tuple(u32, String),
188 Struct { x: u32, y: String },
189 }
190
191 #[test]
192 fn test_facet_iterator_struct() {
193 let facet = NestedFacet {
194 nested_field: TestFacet {
195 field1: 42,
196 field2: "Hello".to_string(),
197 },
198 };
199
200 let mut iter = facet.inspect();
201
202 let iterator_contains_field1 = iter.any(|(path, peek)| {
203 path == FacetPath::from("$.nested_field.field1")
204 && matches!(
205 peek.partial_eq(&Peek::new(&facet.nested_field.field1)),
206 Some(true)
207 )
208 });
209
210 let iterator_contains_field2 = iter.any(|(path, peek)| {
211 path == FacetPath::from("$.nested_field.field2")
212 && matches!(
213 peek.partial_eq(&Peek::new(&facet.nested_field.field2)),
214 Some(true)
215 )
216 });
217
218 assert!(iterator_contains_field1);
219 assert!(iterator_contains_field2);
220 }
221
222 #[test]
223 fn test_get_peek_by_path_struct() {
224 let facet = TestFacet {
225 field1: 42,
226 field2: "Hello".to_string(),
227 };
228
229 let peek1 = facet.get(&FacetPath::from("$.field1")).unwrap();
230 let peek2 = facet.get(&FacetPath::from("$.field2")).unwrap();
231
232 assert_eq!(peek1.partial_eq(&Peek::new(&facet.field1)), Some(true));
233 assert_eq!(peek2.partial_eq(&Peek::new(&facet.field2)), Some(true));
234 }
235
236 #[test]
237 fn test_facet_iterator_enum_unit() {
238 let facet = MyEnum::Unit;
239 let mut iter = facet.inspect();
240 assert!(iter.any(|(p, _)| p == FacetPath::from("$")));
242 }
243
244 #[test]
245 fn test_facet_iterator_enum_tuple() {
246 let facet = MyEnum::Tuple(42, "hello".to_string());
247 let mut iter = facet.inspect();
248 assert!(iter.any(|(p, peek)| p == FacetPath::from("$.0")
250 && peek.partial_eq(&Peek::new(&42)).unwrap_or(false)));
251 assert!(iter.any(|(p, peek)| {
252 p == FacetPath::from("$.1")
253 && peek
254 .partial_eq(&Peek::new(&"hello".to_string()))
255 .unwrap_or(false)
256 }));
257 }
258
259 #[test]
260 fn test_facet_iterator_enum_struct() {
261 let facet = MyEnum::Struct {
262 x: 7,
263 y: "abc".to_string(),
264 };
265 let mut iter = facet.inspect();
266
267 assert!(iter.any(|(p, peek)| p == FacetPath::from("$.x")
268 && peek.partial_eq(&Peek::new(&7)).unwrap_or(false)));
269 assert!(iter.any(|(p, peek)| {
270 p == FacetPath::from("$.y")
271 && peek
272 .partial_eq(&Peek::new(&"abc".to_string()))
273 .unwrap_or(false)
274 }));
275 }
276
277 #[test]
278 fn test_get_peek_by_path_enum_variants() {
279 let facet = MyEnum::Struct {
281 x: 99,
282 y: "zzz".to_string(),
283 };
284 let peek_x = facet.get(&FacetPath::from("$.x")).unwrap();
285 let peek_y = facet.get(&FacetPath::from("$.y")).unwrap();
286 assert_eq!(peek_x.partial_eq(&Peek::new(&99)), Some(true));
287 assert_eq!(
288 peek_y.partial_eq(&Peek::new(&"zzz".to_string())),
289 Some(true)
290 );
291
292 let facet = MyEnum::Tuple(123, "tupleval".to_string());
294 let peek_0 = facet.get(&FacetPath::from("$.0")).unwrap();
295 let peek_1 = facet.get(&FacetPath::from("$.1")).unwrap();
296 assert_eq!(peek_0.partial_eq(&Peek::new(&123)), Some(true));
297 assert_eq!(
298 peek_1.partial_eq(&Peek::new(&"tupleval".to_string())),
299 Some(true)
300 );
301
302 let facet = MyEnum::Unit;
304 let peek_root = facet.get(&FacetPath::from("$")).unwrap();
305 assert!(peek_root.partial_eq(&Peek::new(&facet)).unwrap_or(false));
307 }
308
309 #[test]
310 fn test_facet_iterator_array() {
311 let arr = [10, 20, 30];
312 let iter = arr.inspect();
313
314 let items: Vec<_> = iter.clone().collect();
315 dbg!(items);
316
317 assert!(iter.clone().any(|(p, peek)| p == FacetPath::from("$.0")
318 && peek.partial_eq(&Peek::new(&10)).unwrap_or(false)));
319 assert!(iter.clone().any(|(p, peek)| p == FacetPath::from("$.1")
320 && peek.partial_eq(&Peek::new(&20)).unwrap_or(false)));
321 assert!(iter.clone().any(|(p, peek)| p == FacetPath::from("$.2")
322 && peek.partial_eq(&Peek::new(&30)).unwrap_or(false)));
323 }
324
325 #[test]
326 fn test_facet_iterator_vec() {
327 let vec = vec!["a".to_string(), "b".to_string()];
328 let iter = vec.inspect();
329 assert!(iter.clone().any(|(p, peek)| {
330 p == FacetPath::from("$.0")
331 && peek
332 .partial_eq(&Peek::new(&"a".to_string()))
333 .unwrap_or(false)
334 }));
335 assert!(iter.clone().any(|(p, peek)| {
336 p == FacetPath::from("$.1")
337 && peek
338 .partial_eq(&Peek::new(&"b".to_string()))
339 .unwrap_or(false)
340 }));
341 }
342
343 #[test]
344 fn test_facet_iterator_slice() {
345 let slice: &[u32] = &[5, 6, 7];
346 let iter = slice.inspect();
347
348 dbg!(iter.clone().collect::<Vec<_>>());
349
350 assert!(iter.clone().any(|(p, peek)| p == FacetPath::from("$.0")
351 && peek.partial_eq(&Peek::new(&5)).unwrap_or(false)));
352 assert!(iter.clone().any(|(p, peek)| p == FacetPath::from("$.1")
353 && peek.partial_eq(&Peek::new(&6)).unwrap_or(false)));
354 assert!(iter.clone().any(|(p, peek)| p == FacetPath::from("$.2")
355 && peek.partial_eq(&Peek::new(&7)).unwrap_or(false)));
356 }
357
358 #[test]
359 fn test_facet_iterator_map() {
360 let mut map = HashMap::new();
361 map.insert("foo".to_string(), 123);
362 map.insert("bar".to_string(), 456);
363 let iter = map.inspect();
364 assert!(iter.clone().any(|(p, peek)| p == FacetPath::from("$.foo")
365 && peek.partial_eq(&Peek::new(&123)).unwrap_or(false)));
366 assert!(iter.clone().any(|(p, peek)| p == FacetPath::from("$.bar")
367 && peek.partial_eq(&Peek::new(&456)).unwrap_or(false)));
368 }
369
370 #[test]
371 fn test_get_peek_by_path_array_vec_slice_map() {
372 let arr = [1, 2, 3];
373 assert_eq!(
374 arr.get(&FacetPath::from("$.0"))
375 .unwrap()
376 .partial_eq(&Peek::new(&1)),
377 Some(true)
378 );
379 assert_eq!(
380 arr.get(&FacetPath::from("$.2"))
381 .unwrap()
382 .partial_eq(&Peek::new(&3)),
383 Some(true)
384 );
385
386 let vec = vec![9, 8, 7];
387 assert_eq!(
388 vec.get(&FacetPath::from("$.1"))
389 .unwrap()
390 .partial_eq(&Peek::new(&8)),
391 Some(true)
392 );
393
394 let slice: &[u32] = &[4, 5];
395 assert_eq!(
396 FacetInspect::get(&slice, &FacetPath::from("$.0"))
397 .unwrap()
398 .partial_eq(&Peek::new(&4)),
399 Some(true)
400 );
401
402 let mut map = HashMap::new();
403 map.insert("k1".to_string(), 11);
404 map.insert("k2".to_string(), 22);
405 assert_eq!(
406 FacetInspect::get(&map, &FacetPath::from("$.k1"))
407 .unwrap()
408 .partial_eq(&Peek::new(&11)),
409 Some(true)
410 );
411 assert_eq!(
412 FacetInspect::get(&map, &FacetPath::from("$.k2"))
413 .unwrap()
414 .partial_eq(&Peek::new(&22)),
415 Some(true)
416 );
417 }
418}