1#![warn(missing_docs)]
2#![cfg_attr(not(feature = "std"), no_std)]
3#![doc = include_str!("../README.md")]
4
5extern crate alloc;
6
7use alloc::string::String;
8use alloc::vec::Vec;
9use core::fmt::Write;
10
11use facet_core::{Def, Field, Shape, Type, UserType};
12
13pub mod access;
14pub use access::PathAccessError;
15
16pub mod walk;
17pub use walk::{ShapeVisitor, VisitDecision, WalkStatus, walk_shape};
18
19#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
24pub enum PathStep {
25 Field(u32),
27 Index(u32),
29 Variant(u32),
31 MapKey(u32),
34 MapValue(u32),
37 OptionSome,
39 Deref,
41 Inner,
43 Proxy,
48}
49
50#[derive(Debug, Clone)]
56pub struct Path {
57 pub shape: &'static Shape,
59
60 pub steps: Vec<PathStep>,
62}
63
64impl PartialEq for Path {
65 fn eq(&self, other: &Self) -> bool {
66 core::ptr::eq(self.shape, other.shape) && self.steps == other.steps
68 }
69}
70
71impl Eq for Path {}
72
73impl PartialOrd for Path {
74 fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
75 Some(self.cmp(other))
76 }
77}
78
79impl Ord for Path {
80 fn cmp(&self, other: &Self) -> core::cmp::Ordering {
81 let shape_cmp =
83 (self.shape as *const Shape as usize).cmp(&(other.shape as *const Shape as usize));
84 if shape_cmp != core::cmp::Ordering::Equal {
85 return shape_cmp;
86 }
87 self.steps.cmp(&other.steps)
88 }
89}
90
91impl core::hash::Hash for Path {
92 fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
93 (self.shape as *const Shape as usize).hash(state);
95 self.steps.hash(state);
96 }
97}
98
99impl Path {
100 pub const fn new(shape: &'static Shape) -> Self {
102 Self {
103 shape,
104 steps: Vec::new(),
105 }
106 }
107
108 pub fn with_capacity(shape: &'static Shape, capacity: usize) -> Self {
110 Self {
111 shape,
112 steps: Vec::with_capacity(capacity),
113 }
114 }
115
116 pub fn push(&mut self, step: PathStep) {
118 self.steps.push(step);
119 }
120
121 pub fn pop(&mut self) -> Option<PathStep> {
123 self.steps.pop()
124 }
125
126 pub fn steps(&self) -> &[PathStep] {
128 &self.steps
129 }
130
131 pub const fn len(&self) -> usize {
133 self.steps.len()
134 }
135
136 pub const fn is_empty(&self) -> bool {
138 self.steps.is_empty()
139 }
140
141 pub fn format(&self) -> String {
145 self.format_with_shape(self.shape)
146 }
147
148 pub fn format_with_shape(&self, shape: &'static Shape) -> String {
152 let mut result = String::new();
153 let mut current_shape = shape;
154 let mut current_variant_idx: Option<usize> = None;
155
156 for step in &self.steps {
157 match step {
158 PathStep::Field(idx) => {
159 let idx = *idx as usize;
160 if let Some(field_name) =
161 get_field_name_with_variant(current_shape, idx, current_variant_idx)
162 {
163 if !result.is_empty() {
164 result.push('.');
165 }
166 result.push_str(field_name);
167 }
168 if let Some(field_shape) =
169 get_field_shape_with_variant(current_shape, idx, current_variant_idx)
170 {
171 current_shape = field_shape;
172 }
173 current_variant_idx = None;
174 }
175 PathStep::Index(idx) => {
176 write!(result, "[{}]", idx).unwrap();
177 if let Some(elem_shape) = get_element_shape(current_shape) {
178 current_shape = elem_shape;
179 }
180 current_variant_idx = None;
181 }
182 PathStep::Variant(idx) => {
183 let idx = *idx as usize;
184 if let Some(variant_name) = get_variant_name(current_shape, idx) {
185 result.push_str("::");
186 result.push_str(variant_name);
187 }
188 current_variant_idx = Some(idx);
191 }
192 PathStep::MapKey(idx) => {
193 write!(result, "[key#{}]", idx).unwrap();
194 if let Some(key_shape) = get_map_key_shape(current_shape) {
195 current_shape = key_shape;
196 }
197 current_variant_idx = None;
198 }
199 PathStep::MapValue(idx) => {
200 write!(result, "[value#{}]", idx).unwrap();
201 if let Some(value_shape) = get_map_value_shape(current_shape) {
202 current_shape = value_shape;
203 }
204 current_variant_idx = None;
205 }
206 PathStep::OptionSome => {
207 result.push_str("::Some");
208 if let Some(inner_shape) = get_option_inner_shape(current_shape) {
209 current_shape = inner_shape;
210 }
211 current_variant_idx = None;
212 }
213 PathStep::Deref => {
214 if let Some(inner_shape) = get_pointer_inner_shape(current_shape) {
215 current_shape = inner_shape;
216 }
217 current_variant_idx = None;
218 }
219 PathStep::Inner => {
220 if let Some(inner_shape) = get_inner_shape(current_shape) {
221 current_shape = inner_shape;
222 }
223 current_variant_idx = None;
224 }
225 PathStep::Proxy => {
226 if let Some(proxy_def) = current_shape.effective_proxy(None) {
227 current_shape = proxy_def.shape;
228 }
229 current_variant_idx = None;
230 }
231 }
232 }
233
234 if result.is_empty() {
235 result.push_str("<root>");
236 }
237
238 result
239 }
240
241 pub fn resolve_leaf_field(&self, shape: &'static Shape) -> Option<&'static Field> {
254 if self.steps.is_empty() {
255 return None;
256 }
257
258 let mut current_shape = shape;
259 let mut current_variant_idx: Option<usize> = None;
260
261 for step in &self.steps[..self.steps.len() - 1] {
263 match step {
264 PathStep::Field(idx) => {
265 let idx = *idx as usize;
266 current_shape =
267 get_field_shape_with_variant(current_shape, idx, current_variant_idx)?;
268 current_variant_idx = None;
269 }
270 PathStep::Index(_) => {
271 current_shape = get_element_shape(current_shape)?;
272 current_variant_idx = None;
273 }
274 PathStep::Variant(idx) => {
275 current_variant_idx = Some(*idx as usize);
277 }
278 PathStep::MapKey(_) => {
279 current_shape = get_map_key_shape(current_shape)?;
280 current_variant_idx = None;
281 }
282 PathStep::MapValue(_) => {
283 current_shape = get_map_value_shape(current_shape)?;
284 current_variant_idx = None;
285 }
286 PathStep::OptionSome => {
287 current_shape = get_option_inner_shape(current_shape)?;
288 current_variant_idx = None;
289 }
290 PathStep::Deref => {
291 current_shape = get_pointer_inner_shape(current_shape)?;
292 current_variant_idx = None;
293 }
294 PathStep::Inner => {
295 current_shape = get_inner_shape(current_shape)?;
296 current_variant_idx = None;
297 }
298 PathStep::Proxy => {
299 let proxy_def = current_shape.effective_proxy(None)?;
300 current_shape = proxy_def.shape;
301 current_variant_idx = None;
302 }
303 }
304 }
305
306 if let Some(PathStep::Field(idx)) = self.steps.last() {
308 let idx = *idx as usize;
309 return get_field_with_variant(current_shape, idx, current_variant_idx);
310 }
311
312 None
313 }
314}
315
316impl core::fmt::Display for Path {
317 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
318 f.write_str(&self.format())
319 }
320}
321
322fn get_field_with_variant(
324 shape: &Shape,
325 idx: usize,
326 variant_idx: Option<usize>,
327) -> Option<&'static Field> {
328 match shape.ty {
329 Type::User(UserType::Struct(sd)) => sd.fields.get(idx),
330 Type::User(UserType::Enum(ed)) => {
331 let variant_idx = variant_idx?;
332 let variant = ed.variants.get(variant_idx)?;
333 variant.data.fields.get(idx)
334 }
335 _ => None,
336 }
337}
338
339fn get_field_shape_with_variant(
341 shape: &Shape,
342 idx: usize,
343 variant_idx: Option<usize>,
344) -> Option<&'static Shape> {
345 get_field_with_variant(shape, idx, variant_idx).map(|f| f.shape())
346}
347
348fn get_field_name_with_variant(
350 shape: &Shape,
351 idx: usize,
352 variant_idx: Option<usize>,
353) -> Option<&'static str> {
354 get_field_with_variant(shape, idx, variant_idx).map(|f| f.name)
355}
356
357const fn get_element_shape(shape: &Shape) -> Option<&'static Shape> {
359 match shape.def {
360 Def::List(ld) => Some(ld.t()),
361 Def::Array(ad) => Some(ad.t()),
362 Def::Slice(sd) => Some(sd.t()),
363 _ => None,
364 }
365}
366
367fn get_variant_name(shape: &Shape, idx: usize) -> Option<&'static str> {
369 match shape.ty {
370 Type::User(UserType::Enum(ed)) => ed.variants.get(idx).map(|v| v.name),
371 _ => None,
372 }
373}
374
375const fn get_map_key_shape(shape: &Shape) -> Option<&'static Shape> {
377 match shape.def {
378 Def::Map(md) => Some(md.k()),
379 _ => None,
380 }
381}
382
383const fn get_map_value_shape(shape: &Shape) -> Option<&'static Shape> {
385 match shape.def {
386 Def::Map(md) => Some(md.v()),
387 _ => None,
388 }
389}
390
391const fn get_option_inner_shape(shape: &Shape) -> Option<&'static Shape> {
393 match shape.def {
394 Def::Option(od) => Some(od.t()),
395 _ => None,
396 }
397}
398
399const fn get_pointer_inner_shape(shape: &Shape) -> Option<&'static Shape> {
401 match shape.def {
402 Def::Pointer(pd) => pd.pointee(),
403 _ => None,
404 }
405}
406
407const fn get_inner_shape(shape: &Shape) -> Option<&'static Shape> {
409 shape.inner
410}
411
412#[cfg(test)]
413mod tests {
414 use super::*;
415
416 #[test]
417 fn test_path_step_size() {
418 assert_eq!(core::mem::size_of::<PathStep>(), 8);
420 }
421}