facet_reflect/partial/partial_api/
fields.rs

1use super::*;
2
3////////////////////////////////////////////////////////////////////////////////////////////////////
4// Field selection
5////////////////////////////////////////////////////////////////////////////////////////////////////
6impl<const BORROW: bool> Partial<'_, BORROW> {
7    /// Find the index of a field by name in the current struct
8    ///
9    /// If the current frame isn't a struct or an enum (with a selected variant)
10    /// then this returns `None` for sure.
11    pub fn field_index(&self, field_name: &str) -> Option<usize> {
12        let frame = self.frames().last()?;
13
14        match frame.shape.ty {
15            Type::User(UserType::Struct(struct_def)) => {
16                struct_def.fields.iter().position(|f| f.name == field_name)
17            }
18            Type::User(UserType::Enum(_)) => {
19                // If we're in an enum variant, check its fields
20                if let Tracker::Enum { variant, .. } = &frame.tracker {
21                    variant
22                        .data
23                        .fields
24                        .iter()
25                        .position(|f| f.name == field_name)
26                } else {
27                    None
28                }
29            }
30            _ => None,
31        }
32    }
33
34    /// Check if a struct field at the given index has been set
35    pub fn is_field_set(&self, index: usize) -> Result<bool, ReflectError> {
36        let frame = self.frames().last().ok_or(ReflectError::NoActiveFrame)?;
37
38        match &frame.tracker {
39            Tracker::Scalar => Ok(frame.is_init),
40            Tracker::Struct { iset, .. } => Ok(iset.get(index)),
41            Tracker::Enum { data, variant, .. } => {
42                // Check if the field is already marked as set
43                if data.get(index) {
44                    return Ok(true);
45                }
46
47                // For enum variant fields that are empty structs, they are always initialized
48                if let Some(field) = variant.data.fields.get(index)
49                    && let Type::User(UserType::Struct(field_struct)) = field.shape().ty
50                    && field_struct.fields.is_empty()
51                {
52                    return Ok(true);
53                }
54
55                Ok(false)
56            }
57            Tracker::Option { building_inner } => {
58                // For Options, index 0 represents the inner value
59                if index == 0 {
60                    Ok(!building_inner)
61                } else {
62                    Err(ReflectError::InvalidOperation {
63                        operation: "is_field_set",
64                        reason: "Option only has one field (index 0)",
65                    })
66                }
67            }
68            Tracker::Result { building_inner, .. } => {
69                // For Results, index 0 represents the inner value (Ok or Err)
70                if index == 0 {
71                    Ok(!building_inner)
72                } else {
73                    Err(ReflectError::InvalidOperation {
74                        operation: "is_field_set",
75                        reason: "Result only has one field (index 0)",
76                    })
77                }
78            }
79            _ => Err(ReflectError::InvalidOperation {
80                operation: "is_field_set",
81                reason: "Current frame is not a struct, enum variant, option, or result",
82            }),
83        }
84    }
85
86    /// Selects a field (by name) of a struct or enum data.
87    ///
88    /// For enums, the variant needs to be selected first, see [Self::select_nth_variant]
89    /// and friends.
90    pub fn begin_field(self, field_name: &str) -> Result<Self, ReflectError> {
91        let frame = self.frames().last().unwrap();
92        let fields = self.get_fields()?;
93        let Some(idx) = fields.iter().position(|f| f.name == field_name) else {
94            return Err(ReflectError::FieldError {
95                shape: frame.shape,
96                field_error: facet_core::FieldError::NoSuchField,
97            });
98        };
99        self.begin_nth_field(idx)
100    }
101
102    /// Begins the nth field of a struct, enum variant, or array, by index.
103    ///
104    /// On success, this pushes a new frame which must be ended with a call to [Partial::end]
105    pub fn begin_nth_field(mut self, idx: usize) -> Result<Self, ReflectError> {
106        // In deferred mode, get the field name for path tracking and check for stored frames.
107        // Only track the path if we're at a "navigable" level - i.e., the path length matches
108        // the expected depth (frames.len() - 1). If we're inside a collection item, the path
109        // will be shorter than expected, so we shouldn't add to it.
110        let field_name = self.get_field_name_for_path(idx);
111
112        // Update current_path in deferred mode
113        if let FrameMode::Deferred {
114            stack,
115            start_depth,
116            current_path,
117            stored_frames,
118            ..
119        } = &mut self.mode
120        {
121            // Only track path if we're at the expected navigable depth
122            // Path should have (frames.len() - start_depth) entries before we add this field
123            let relative_depth = stack.len() - *start_depth;
124            let should_track = current_path.len() == relative_depth;
125
126            if let Some(name) = field_name
127                && should_track
128            {
129                current_path.push(name);
130
131                // Check if we have a stored frame for this path
132                if let Some(stored_frame) = stored_frames.remove(current_path) {
133                    trace!("begin_nth_field: Restoring stored frame for path {current_path:?}");
134
135                    // Update parent's current_child tracking
136                    let frame = stack.last_mut().unwrap();
137                    frame.tracker.set_current_child(idx);
138
139                    stack.push(stored_frame);
140                    return Ok(self);
141                }
142            }
143        }
144
145        let frame = self.frames_mut().last_mut().unwrap();
146
147        let next_frame = match frame.shape.ty {
148            Type::User(user_type) => match user_type {
149                UserType::Struct(struct_type) => {
150                    Self::begin_nth_struct_field(frame, struct_type, idx)?
151                }
152                UserType::Enum(_) => {
153                    // Check if we have a variant selected
154                    match &frame.tracker {
155                        Tracker::Enum { variant, .. } => {
156                            Self::begin_nth_enum_field(frame, variant, idx)?
157                        }
158                        _ => {
159                            return Err(ReflectError::OperationFailed {
160                                shape: frame.shape,
161                                operation: "must call select_variant before selecting enum fields",
162                            });
163                        }
164                    }
165                }
166                UserType::Union(_) => {
167                    return Err(ReflectError::OperationFailed {
168                        shape: frame.shape,
169                        operation: "cannot select a field from a union",
170                    });
171                }
172                UserType::Opaque => {
173                    return Err(ReflectError::OperationFailed {
174                        shape: frame.shape,
175                        operation: "cannot select a field from an opaque type",
176                    });
177                }
178            },
179            Type::Sequence(sequence_type) => match sequence_type {
180                SequenceType::Array(array_type) => {
181                    Self::begin_nth_array_element(frame, array_type, idx)?
182                }
183                SequenceType::Slice(_) => {
184                    return Err(ReflectError::OperationFailed {
185                        shape: frame.shape,
186                        operation: "cannot select a field from slices yet",
187                    });
188                }
189            },
190            _ => {
191                return Err(ReflectError::OperationFailed {
192                    shape: frame.shape,
193                    operation: "cannot select a field from this type",
194                });
195            }
196        };
197
198        self.frames_mut().push(next_frame);
199        Ok(self)
200    }
201
202    /// Get the field name for path tracking (used in deferred mode)
203    fn get_field_name_for_path(&self, idx: usize) -> Option<&'static str> {
204        let frame = self.frames().last()?;
205        match frame.shape.ty {
206            Type::User(UserType::Struct(struct_type)) => {
207                struct_type.fields.get(idx).map(|f| f.name)
208            }
209            Type::User(UserType::Enum(_)) => {
210                if let Tracker::Enum { variant, .. } = &frame.tracker {
211                    variant.data.fields.get(idx).map(|f| f.name)
212                } else {
213                    None
214                }
215            }
216            // For arrays, we could use index as string, but for now return None
217            _ => None,
218        }
219    }
220
221    /// Sets the given field to its default value, preferring:
222    ///
223    ///   * A `default = some_fn()` function
224    ///   * The field's `Default` implementation if any
225    ///
226    /// But without going all the way up to the parent struct's `Default` impl.
227    ///
228    /// Errors out if idx is out of bound, if the field has no default method or Default impl.
229    pub fn set_nth_field_to_default(mut self, idx: usize) -> Result<Self, ReflectError> {
230        let frame = self.frames().last().unwrap();
231        let fields = self.get_fields()?;
232
233        if idx >= fields.len() {
234            return Err(ReflectError::OperationFailed {
235                shape: frame.shape,
236                operation: "field index out of bounds",
237            });
238        }
239
240        let field = fields[idx];
241
242        // Check for field-level default first, then type-level default
243        if let Some(default_source) = field.default {
244            self = self.begin_nth_field(idx)?;
245            match default_source {
246                facet_core::DefaultSource::Custom(field_default_fn) => {
247                    // Custom default function provided via #[facet(default = expr)]
248                    self = unsafe {
249                        self.set_from_function(|ptr| {
250                            field_default_fn(ptr);
251                            Ok(())
252                        })?
253                    };
254                }
255                facet_core::DefaultSource::FromTrait => {
256                    // Use the type's Default trait via #[facet(default)]
257                    self = self.set_default()?;
258                }
259            }
260            self.end()
261        } else if field.shape().is(Characteristic::Default) {
262            self = self.begin_nth_field(idx)?;
263            self = self.set_default()?;
264            self.end()
265        } else {
266            Err(ReflectError::DefaultAttrButNoDefaultImpl {
267                shape: field.shape(),
268            })
269        }
270    }
271}