1#![warn(missing_docs)]
2#![cfg_attr(not(feature = "std"), no_std)]
3extern crate alloc;
8
9use alloc::string::String;
10use alloc::vec::Vec;
11use core::fmt::Write;
12
13use facet_core::{Def, Shape, StructKind, Type, UserType};
14
15#[derive(Debug, Clone, Copy, PartialEq, Eq)]
20pub enum PathStep {
21 Field(u32),
23 Index(u32),
25 Variant(u32),
27 MapKey,
29 MapValue,
31 OptionSome,
33 Deref,
35}
36
37#[derive(Debug, Clone, Default)]
43pub struct Path {
44 steps: Vec<PathStep>,
45}
46
47impl Path {
48 pub fn new() -> Self {
50 Self { steps: Vec::new() }
51 }
52
53 pub fn with_capacity(capacity: usize) -> Self {
55 Self {
56 steps: Vec::with_capacity(capacity),
57 }
58 }
59
60 pub fn push(&mut self, step: PathStep) {
62 self.steps.push(step);
63 }
64
65 pub fn pop(&mut self) -> Option<PathStep> {
67 self.steps.pop()
68 }
69
70 pub fn steps(&self) -> &[PathStep] {
72 &self.steps
73 }
74
75 pub fn len(&self) -> usize {
77 self.steps.len()
78 }
79
80 pub fn is_empty(&self) -> bool {
82 self.steps.is_empty()
83 }
84
85 pub fn format_with_shape(&self, shape: &'static Shape) -> String {
89 let mut result = String::new();
90 let mut current_shape = shape;
91
92 for step in &self.steps {
93 match step {
94 PathStep::Field(idx) => {
95 let idx = *idx as usize;
96 if let Some(field_name) = get_field_name(current_shape, idx) {
97 if !result.is_empty() {
98 result.push('.');
99 }
100 result.push_str(field_name);
101 if let Some(field_shape) = get_field_shape(current_shape, idx) {
102 current_shape = field_shape;
103 }
104 }
105 }
106 PathStep::Index(idx) => {
107 write!(result, "[{}]", idx).unwrap();
108 if let Some(elem_shape) = get_element_shape(current_shape) {
109 current_shape = elem_shape;
110 }
111 }
112 PathStep::Variant(idx) => {
113 let idx = *idx as usize;
114 if let Some(variant_name) = get_variant_name(current_shape, idx) {
115 result.push_str("::");
116 result.push_str(variant_name);
117 if let Some(variant_shape) = get_variant_shape(current_shape, idx) {
118 current_shape = variant_shape;
119 }
120 }
121 }
122 PathStep::MapKey => {
123 result.push_str("[key]");
124 if let Some(key_shape) = get_map_key_shape(current_shape) {
125 current_shape = key_shape;
126 }
127 }
128 PathStep::MapValue => {
129 result.push_str("[value]");
130 if let Some(value_shape) = get_map_value_shape(current_shape) {
131 current_shape = value_shape;
132 }
133 }
134 PathStep::OptionSome => {
135 if let Some(inner_shape) = get_option_inner_shape(current_shape) {
136 current_shape = inner_shape;
137 }
138 }
139 PathStep::Deref => {
140 if let Some(inner_shape) = get_pointer_inner_shape(current_shape) {
141 current_shape = inner_shape;
142 }
143 }
144 }
145 }
146
147 if result.is_empty() {
148 result.push_str("<root>");
149 }
150
151 result
152 }
153}
154
155fn get_field_name(shape: &Shape, idx: usize) -> Option<&'static str> {
157 match shape.ty {
158 Type::User(UserType::Struct(sd)) => sd.fields.get(idx).map(|f| f.name),
159 Type::User(UserType::Enum(_)) => {
160 None
162 }
163 _ => None,
164 }
165}
166
167fn get_field_shape(shape: &Shape, idx: usize) -> Option<&'static Shape> {
169 match shape.ty {
170 Type::User(UserType::Struct(sd)) => sd.fields.get(idx).map(|f| f.shape()),
171 _ => None,
172 }
173}
174
175fn get_element_shape(shape: &Shape) -> Option<&'static Shape> {
177 match shape.def {
178 Def::List(ld) => Some(ld.t()),
179 Def::Array(ad) => Some(ad.t()),
180 Def::Slice(sd) => Some(sd.t()),
181 _ => None,
182 }
183}
184
185fn get_variant_name(shape: &Shape, idx: usize) -> Option<&'static str> {
187 match shape.ty {
188 Type::User(UserType::Enum(ed)) => ed.variants.get(idx).map(|v| v.name),
189 _ => None,
190 }
191}
192
193fn get_variant_shape(shape: &Shape, idx: usize) -> Option<&'static Shape> {
195 match shape.ty {
196 Type::User(UserType::Enum(ed)) => {
197 let variant = ed.variants.get(idx)?;
198 if variant.data.kind == StructKind::Unit {
199 None
200 } else {
201 variant.data.fields.first().map(|f| f.shape())
202 }
203 }
204 _ => None,
205 }
206}
207
208fn get_map_key_shape(shape: &Shape) -> Option<&'static Shape> {
210 match shape.def {
211 Def::Map(md) => Some(md.k()),
212 _ => None,
213 }
214}
215
216fn get_map_value_shape(shape: &Shape) -> Option<&'static Shape> {
218 match shape.def {
219 Def::Map(md) => Some(md.v()),
220 _ => None,
221 }
222}
223
224fn get_option_inner_shape(shape: &Shape) -> Option<&'static Shape> {
226 match shape.def {
227 Def::Option(od) => Some(od.t()),
228 _ => None,
229 }
230}
231
232fn get_pointer_inner_shape(shape: &Shape) -> Option<&'static Shape> {
234 match shape.def {
235 Def::Pointer(pd) => pd.pointee(),
236 _ => None,
237 }
238}
239
240#[cfg(feature = "pretty")]
241pub mod pretty;
242
243#[cfg(test)]
244mod tests {
245 use super::*;
246
247 #[test]
248 fn test_path_step_size() {
249 assert_eq!(core::mem::size_of::<PathStep>(), 8);
251 }
252}