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, Field, 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 const 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 pub fn resolve_leaf_field(&self, shape: &'static Shape) -> Option<&'static Field> {
167 if self.steps.is_empty() {
168 return None;
169 }
170
171 let mut current_shape = shape;
172 let mut current_variant_idx: Option<usize> = None;
173
174 for step in &self.steps[..self.steps.len() - 1] {
176 match step {
177 PathStep::Field(idx) => {
178 let idx = *idx as usize;
179 current_shape =
180 get_field_shape_with_variant(current_shape, idx, current_variant_idx)?;
181 current_variant_idx = None;
182 }
183 PathStep::Index(_) => {
184 current_shape = get_element_shape(current_shape)?;
185 current_variant_idx = None;
186 }
187 PathStep::Variant(idx) => {
188 current_variant_idx = Some(*idx as usize);
190 }
191 PathStep::MapKey => {
192 current_shape = get_map_key_shape(current_shape)?;
193 current_variant_idx = None;
194 }
195 PathStep::MapValue => {
196 current_shape = get_map_value_shape(current_shape)?;
197 current_variant_idx = None;
198 }
199 PathStep::OptionSome => {
200 current_shape = get_option_inner_shape(current_shape)?;
201 current_variant_idx = None;
202 }
203 PathStep::Deref => {
204 current_shape = get_pointer_inner_shape(current_shape)?;
205 current_variant_idx = None;
206 }
207 }
208 }
209
210 if let Some(PathStep::Field(idx)) = self.steps.last() {
212 let idx = *idx as usize;
213 return get_field_with_variant(current_shape, idx, current_variant_idx);
214 }
215
216 None
217 }
218}
219
220fn get_field_with_variant(
222 shape: &Shape,
223 idx: usize,
224 variant_idx: Option<usize>,
225) -> Option<&'static Field> {
226 match shape.ty {
227 Type::User(UserType::Struct(sd)) => sd.fields.get(idx),
228 Type::User(UserType::Enum(ed)) => {
229 let variant_idx = variant_idx?;
230 let variant = ed.variants.get(variant_idx)?;
231 variant.data.fields.get(idx)
232 }
233 _ => None,
234 }
235}
236
237fn get_field_shape_with_variant(
239 shape: &Shape,
240 idx: usize,
241 variant_idx: Option<usize>,
242) -> Option<&'static Shape> {
243 get_field_with_variant(shape, idx, variant_idx).map(|f| f.shape())
244}
245
246fn get_field_name(shape: &Shape, idx: usize) -> Option<&'static str> {
248 match shape.ty {
249 Type::User(UserType::Struct(sd)) => sd.fields.get(idx).map(|f| f.name),
250 Type::User(UserType::Enum(_)) => {
251 None
253 }
254 _ => None,
255 }
256}
257
258fn get_field_shape(shape: &Shape, idx: usize) -> Option<&'static Shape> {
260 match shape.ty {
261 Type::User(UserType::Struct(sd)) => sd.fields.get(idx).map(|f| f.shape()),
262 _ => None,
263 }
264}
265
266fn get_element_shape(shape: &Shape) -> Option<&'static Shape> {
268 match shape.def {
269 Def::List(ld) => Some(ld.t()),
270 Def::Array(ad) => Some(ad.t()),
271 Def::Slice(sd) => Some(sd.t()),
272 _ => None,
273 }
274}
275
276fn get_variant_name(shape: &Shape, idx: usize) -> Option<&'static str> {
278 match shape.ty {
279 Type::User(UserType::Enum(ed)) => ed.variants.get(idx).map(|v| v.name),
280 _ => None,
281 }
282}
283
284fn get_variant_shape(shape: &Shape, idx: usize) -> Option<&'static Shape> {
286 match shape.ty {
287 Type::User(UserType::Enum(ed)) => {
288 let variant = ed.variants.get(idx)?;
289 if variant.data.kind == StructKind::Unit {
290 None
291 } else {
292 variant.data.fields.first().map(|f| f.shape())
293 }
294 }
295 _ => None,
296 }
297}
298
299fn get_map_key_shape(shape: &Shape) -> Option<&'static Shape> {
301 match shape.def {
302 Def::Map(md) => Some(md.k()),
303 _ => None,
304 }
305}
306
307fn get_map_value_shape(shape: &Shape) -> Option<&'static Shape> {
309 match shape.def {
310 Def::Map(md) => Some(md.v()),
311 _ => None,
312 }
313}
314
315fn get_option_inner_shape(shape: &Shape) -> Option<&'static Shape> {
317 match shape.def {
318 Def::Option(od) => Some(od.t()),
319 _ => None,
320 }
321}
322
323fn get_pointer_inner_shape(shape: &Shape) -> Option<&'static Shape> {
325 match shape.def {
326 Def::Pointer(pd) => pd.pointee(),
327 _ => None,
328 }
329}
330
331#[cfg(feature = "pretty")]
332pub mod pretty;
333
334#[cfg(test)]
335mod tests {
336 use super::*;
337
338 #[test]
339 fn test_path_step_size() {
340 assert_eq!(core::mem::size_of::<PathStep>(), 8);
342 }
343}