facet_reflect/wip/
put_shape.rs

1use facet_core::{Def, PtrConst, PtrMut, SequenceType, Shape, Type, UserType};
2#[allow(unused_imports)]
3use owo_colors::OwoColorize;
4
5use crate::{ISet, ReflectError};
6use crate::{debug, trace};
7
8use super::Wip;
9
10impl<'facet, 'shape> Wip<'facet, 'shape> {
11    /// Puts a value from a `PtrConst` with the given shape into the current frame.
12    pub fn put_shape(
13        mut self,
14        src: PtrConst<'_>,
15        src_shape: &'shape Shape<'shape>,
16    ) -> Result<Wip<'facet, 'shape>, ReflectError<'shape>> {
17        let Some(frame) = self.frames.last_mut() else {
18            return Err(ReflectError::OperationFailed {
19                shape: src_shape,
20                operation: "tried to put a value but there was no frame to put into",
21            });
22        };
23
24        // Check that the type matches
25        if frame.shape != src_shape {
26            trace!(
27                "Trying to put a {} into a {}",
28                src_shape.yellow(),
29                frame.shape.magenta()
30            );
31
32            // Check if the frame's shape has an inner type (is a transparent wrapper)
33            if let Some(inner_fn) = frame.shape.inner {
34                // Get the inner shape
35                let inner_shape = inner_fn();
36
37                // If the source shape matches the inner shape, we need to build the outer (transparent) wrapper
38                if src_shape == inner_shape {
39                    // Look for a try_from_inner function in the vtable
40                    if let Some(try_from_fn) = frame.shape.vtable.try_from {
41                        match unsafe { (try_from_fn)(src, src_shape, frame.data) } {
42                            Ok(_) => {
43                                unsafe {
44                                    frame.mark_fully_initialized();
45                                }
46
47                                let shape = frame.shape;
48                                let index = frame.field_index_in_parent;
49
50                                // mark the field as initialized
51                                self.mark_field_as_initialized(shape, index)?;
52
53                                debug!(
54                                    "[{}] Just put a {} value into transparent type {}",
55                                    self.frames.len(),
56                                    src_shape.green(),
57                                    shape.blue()
58                                );
59
60                                return Ok(self);
61                            }
62                            Err(e) => {
63                                return Err(ReflectError::TryFromError {
64                                    inner: e,
65                                    src_shape,
66                                    dst_shape: frame.shape,
67                                });
68                            }
69                        }
70                    } else {
71                        // No try_from_inner function, try normal TryFrom
72                        debug!(
73                            "No try_from_inner function for transparent type, falling back to TryFrom"
74                        );
75                    }
76                }
77            }
78
79            // Maybe there's a `TryFrom` impl?
80            if let Some(try_from) = frame.shape.vtable.try_from {
81                match unsafe { try_from(src, src_shape, frame.data) } {
82                    Ok(_) => {
83                        unsafe {
84                            frame.mark_fully_initialized();
85                        }
86
87                        let shape = frame.shape;
88                        let index = frame.field_index_in_parent;
89
90                        // mark the field as initialized
91                        self.mark_field_as_initialized(shape, index)?;
92
93                        debug!("[{}] Just put a {} value", self.frames.len(), shape.green());
94
95                        return Ok(self);
96                    }
97                    Err(e) => {
98                        return Err(ReflectError::TryFromError {
99                            inner: e,
100                            src_shape,
101                            dst_shape: frame.shape,
102                        });
103                    }
104                }
105            }
106
107            // Maybe we're putting into an Option<T>?
108            // Handle Option<Inner>
109            if let Def::Option(od) = frame.shape.def {
110                // Check if inner type matches
111                if od.t() == src_shape {
112                    debug!("Putting into an Option<T>!");
113                    if frame.istate.fields.is_any_set() {
114                        let data = unsafe { frame.data.assume_init() };
115                        unsafe { (od.vtable.replace_with_fn)(data, Some(src)) };
116                    } else {
117                        let data = frame.data;
118                        unsafe { (od.vtable.init_some_fn)(data, src) };
119                    }
120                    unsafe {
121                        frame.mark_fully_initialized();
122                    }
123
124                    let shape = frame.shape;
125                    let index = frame.field_index_in_parent;
126
127                    // mark the field as initialized
128                    self.mark_field_as_initialized(shape, index)?;
129
130                    debug!("[{}] Just put a {} value", self.frames.len(), shape.green());
131
132                    return Ok(self);
133                }
134            }
135
136            // Maybe we're putting into a tuple struct, or a tuple, and it just so happens that the
137            // first non-initialized field has the right type?
138            {
139                let fields = match &frame.shape.ty {
140                    Type::User(UserType::Struct(sd)) => Some(sd.fields),
141                    Type::Sequence(SequenceType::Tuple(tt)) => Some(tt.fields),
142                    _ => None,
143                };
144
145                if let Some(fields) = fields {
146                    // Look for the first uninitialized field
147                    for (i, field) in fields.iter().enumerate() {
148                        if !frame.istate.fields.has(i) {
149                            // First, check for exact type match
150                            if field.shape() == src_shape {
151                                debug!(
152                                    "Found uninitialized field {} with matching type {}",
153                                    i.blue(),
154                                    src_shape.green()
155                                );
156
157                                // Copy the value to the field
158                                unsafe {
159                                    let field_data = frame.data.field_uninit_at(field.offset);
160                                    field_data.copy_from(src, field.shape()).map_err(|_| {
161                                        ReflectError::Unsized {
162                                            shape: field.shape(),
163                                        }
164                                    })?;
165                                    frame.istate.fields.set(i);
166                                }
167
168                                let shape = frame.shape;
169                                let index = frame.field_index_in_parent;
170
171                                // If all fields are now initialized, mark the struct itself as initialized
172                                if frame.is_fully_initialized() {
173                                    self.mark_field_as_initialized(shape, index)?;
174                                }
175
176                                debug!(
177                                    "[{}] Put a {} value into field {} of {}",
178                                    self.frames.len(),
179                                    src_shape.green(),
180                                    i.blue(),
181                                    shape.green()
182                                );
183
184                                return Ok(self);
185                            }
186
187                            // Then check if field's type has a try_from impl that can convert from src_shape
188                            if let Some(try_from) = field.shape().vtable.try_from {
189                                debug!(
190                                    "Found uninitialized field {} with try_from for type {}",
191                                    i.blue(),
192                                    src_shape.green()
193                                );
194
195                                // Try to convert the value and store it in the field
196                                let field_data =
197                                    unsafe { frame.data.field_uninit_at(field.offset) };
198                                match unsafe { try_from(src, src_shape, field_data) } {
199                                    Ok(_) => {
200                                        frame.istate.fields.set(i);
201
202                                        let shape = frame.shape;
203                                        let index = frame.field_index_in_parent;
204
205                                        // If all fields are now initialized, mark the struct itself as initialized
206                                        if frame.is_fully_initialized() {
207                                            self.mark_field_as_initialized(shape, index)?;
208                                        }
209
210                                        debug!(
211                                            "[{}] Put a {} value (converted) into field {} of {}",
212                                            self.frames.len(),
213                                            src_shape.green(),
214                                            i.blue(),
215                                            shape.green()
216                                        );
217
218                                        return Ok(self);
219                                    }
220                                    Err(_) => {
221                                        // Conversion failed, try the next field
222                                        continue;
223                                    }
224                                }
225                            }
226                        }
227                    }
228                }
229            }
230
231            // Maybe we're putting into an enum, which has a variant selected, which has tuple-like fields,
232            // and the first field that is uninitialized just so happens to be the right type?
233            if let Type::User(UserType::Enum(_)) = frame.shape.ty {
234                // Check if we're putting into an enum with a selected variant
235                if let Some(variant) = &frame.istate.variant {
236                    // Look for the first uninitialized field in the variant
237                    for (i, field) in variant.data.fields.iter().enumerate() {
238                        if !frame.istate.fields.has(i) {
239                            // First, check for exact type match
240                            if field.shape() == src_shape {
241                                debug!(
242                                    "Found uninitialized field {} in enum variant '{}' with matching type {}",
243                                    i.blue(),
244                                    variant.name.bright_yellow(),
245                                    src_shape.green()
246                                );
247
248                                // Copy the value to the field
249                                unsafe {
250                                    let field_data = frame.data.field_uninit_at(field.offset);
251                                    field_data.copy_from(src, field.shape()).map_err(|_| {
252                                        ReflectError::Unsized {
253                                            shape: field.shape(),
254                                        }
255                                    })?;
256                                    frame.istate.fields.set(i);
257                                }
258
259                                let shape = frame.shape;
260                                let index = frame.field_index_in_parent;
261
262                                #[allow(unused)]
263                                let variant_name = variant.name;
264
265                                // If all fields are now initialized, mark the enum itself as initialized
266                                if frame.is_fully_initialized() {
267                                    self.mark_field_as_initialized(shape, index)?;
268                                }
269
270                                debug!(
271                                    "[{}] Put a {} value into field {} of variant '{}' in enum {}",
272                                    self.frames.len(),
273                                    src_shape.green(),
274                                    i.blue(),
275                                    variant_name.bright_yellow(),
276                                    shape.green()
277                                );
278
279                                return Ok(self);
280                            }
281
282                            // Then check if field's type has a try_from impl that can convert from src_shape
283                            if let Some(try_from) = field.shape().vtable.try_from {
284                                debug!(
285                                    "Found uninitialized field {} in enum variant '{}' with try_from for type {}",
286                                    i.blue(),
287                                    variant.name.bright_yellow(),
288                                    src_shape.green()
289                                );
290
291                                // Try to convert the value and store it in the field
292                                let field_data =
293                                    unsafe { frame.data.field_uninit_at(field.offset) };
294                                match unsafe { try_from(src, src_shape, field_data) } {
295                                    Ok(_) => {
296                                        frame.istate.fields.set(i);
297
298                                        let shape = frame.shape;
299                                        let index = frame.field_index_in_parent;
300
301                                        #[allow(unused_variables)]
302                                        let variant_name = variant.name;
303
304                                        // If all fields are now initialized, mark the enum itself as initialized
305                                        if frame.is_fully_initialized() {
306                                            self.mark_field_as_initialized(shape, index)?;
307                                        }
308
309                                        debug!(
310                                            "[{}] Put a {} value (converted) into field {} of variant '{}' in enum {}",
311                                            self.frames.len(),
312                                            src_shape.green(),
313                                            i.blue(),
314                                            variant_name.bright_yellow(),
315                                            shape.green()
316                                        );
317
318                                        return Ok(self);
319                                    }
320                                    Err(_) => {
321                                        // Conversion failed, try the next field
322                                        continue;
323                                    }
324                                }
325                            }
326                        }
327                    }
328                }
329            }
330
331            return Err(ReflectError::WrongShape {
332                expected: frame.shape,
333                actual: src_shape,
334            });
335        }
336
337        // de-initialize partially initialized fields, if any
338        if frame.istate.variant.is_some() || frame.istate.fields.is_any_set() {
339            debug!("De-initializing partially initialized {:?}", frame.yellow());
340
341            match frame.shape.ty {
342                Type::User(UserType::Struct(sd)) => {
343                    for (i, field) in sd.fields.iter().enumerate() {
344                        if frame.istate.fields.has(i) {
345                            if let Some(drop_fn) = field.shape().vtable.drop_in_place {
346                                unsafe {
347                                    let field_ptr = frame.data.as_mut_byte_ptr().add(field.offset);
348                                    drop_fn(PtrMut::new(field_ptr));
349                                }
350                            }
351                        }
352                    }
353                }
354                Type::User(UserType::Enum(_)) => {
355                    if let Some(variant) = &frame.istate.variant {
356                        for (i, field) in variant.data.fields.iter().enumerate() {
357                            if frame.istate.fields.has(i) {
358                                if let Some(drop_fn) = field.shape().vtable.drop_in_place {
359                                    unsafe {
360                                        let field_ptr =
361                                            frame.data.as_mut_byte_ptr().add(field.offset);
362                                        drop_fn(PtrMut::new(field_ptr));
363                                    }
364                                }
365                            }
366                        }
367                    }
368                }
369                _ => {
370                    // For scalar types and other non-struct/enum, attempt to drop the field in place if initialized
371                    if frame.istate.fields.is_any_set() {
372                        debug!("Scalar type was set...");
373                        if let Some(drop_fn) = frame.shape.vtable.drop_in_place {
374                            debug!("And it has a drop fn, dropping now!");
375                            unsafe {
376                                drop_fn(frame.data.assume_init());
377                            }
378                        }
379                    }
380                }
381            }
382
383            // Reset initialization state
384            frame.istate.variant = None;
385            ISet::clear(&mut frame.istate.fields);
386        }
387
388        unsafe {
389            // Copy the contents from src to destination
390            frame
391                .data
392                .copy_from(src, frame.shape)
393                .map_err(|_| ReflectError::Unsized { shape: frame.shape })?;
394            frame.mark_fully_initialized();
395        }
396
397        let shape = frame.shape;
398        let index = frame.field_index_in_parent;
399
400        // mark the field as initialized
401        self.mark_field_as_initialized(shape, index)?;
402
403        debug!("[{}] Just put a {} value", self.frames.len(), shape.green());
404
405        Ok(self)
406    }
407}