facet_reflect/
spanned.rs

1//! Types for tracking source span information during deserialization.
2
3use core::{mem, ops::Deref};
4
5use facet_core::{
6    Def, Facet, FieldBuilder, Shape, StructKind, TypeOpsDirect, type_ops_direct, vtable_direct,
7};
8
9/// Source span with offset and length.
10///
11/// This type tracks a byte offset and length within a source document,
12/// useful for error reporting that can point back to the original source.
13#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
14pub struct Span {
15    /// Byte offset from start of source.
16    pub offset: usize,
17    /// Length in bytes.
18    pub len: usize,
19}
20
21impl Span {
22    /// Create a new span with the given offset and length.
23    pub const fn new(offset: usize, len: usize) -> Self {
24        Self { offset, len }
25    }
26
27    /// Check if this span is unknown (zero offset and length).
28    pub fn is_unknown(&self) -> bool {
29        self.offset == 0 && self.len == 0
30    }
31
32    /// Get the end offset (offset + len).
33    pub fn end(&self) -> usize {
34        self.offset + self.len
35    }
36}
37
38#[cfg(feature = "miette")]
39impl From<Span> for miette::SourceSpan {
40    fn from(span: Span) -> Self {
41        miette::SourceSpan::new(span.offset.into(), span.len)
42    }
43}
44
45#[cfg(feature = "miette")]
46impl From<miette::SourceSpan> for Span {
47    fn from(span: miette::SourceSpan) -> Self {
48        Self {
49            offset: span.offset(),
50            len: span.len(),
51        }
52    }
53}
54
55// SAFETY: Span is a simple struct with two usize fields, properly laid out
56unsafe impl Facet<'_> for Span {
57    const SHAPE: &'static Shape = &const {
58        static FIELDS: [facet_core::Field; 2] = [
59            FieldBuilder::new(
60                "offset",
61                facet_core::shape_of::<usize>,
62                mem::offset_of!(Span, offset),
63            )
64            .build(),
65            FieldBuilder::new(
66                "len",
67                facet_core::shape_of::<usize>,
68                mem::offset_of!(Span, len),
69            )
70            .build(),
71        ];
72
73        const VTABLE: facet_core::VTableDirect = vtable_direct!(Span => Debug, PartialEq);
74        const TYPE_OPS: TypeOpsDirect = type_ops_direct!(Span => Default, Clone);
75
76        Shape::builder_for_sized::<Span>("Span")
77            .vtable_direct(&VTABLE)
78            .type_ops_direct(&TYPE_OPS)
79            .ty(facet_core::Type::struct_builder(StructKind::Struct, &FIELDS).build())
80            .def(Def::Undefined)
81            .build()
82    };
83}
84
85/// A value with source span information.
86///
87/// This struct wraps a value along with the source location (offset and length)
88/// where it was parsed from. This is useful for error reporting that can point
89/// back to the original source.
90#[derive(Debug)]
91pub struct Spanned<T> {
92    /// The wrapped value.
93    pub value: T,
94    /// The source span (offset and length).
95    pub span: Span,
96}
97
98impl<T> Spanned<T> {
99    /// Create a new spanned value.
100    pub const fn new(value: T, span: Span) -> Self {
101        Self { value, span }
102    }
103
104    /// Get the source span.
105    pub fn span(&self) -> Span {
106        self.span
107    }
108
109    /// Get a reference to the inner value.
110    pub fn value(&self) -> &T {
111        &self.value
112    }
113
114    /// Unwrap into the inner value, discarding span information.
115    pub fn into_inner(self) -> T {
116        self.value
117    }
118}
119
120impl<T> Deref for Spanned<T> {
121    type Target = T;
122    fn deref(&self) -> &Self::Target {
123        &self.value
124    }
125}
126
127impl<T: Default> Default for Spanned<T> {
128    fn default() -> Self {
129        Self {
130            value: T::default(),
131            span: Span::default(),
132        }
133    }
134}
135
136impl<T: Clone> Clone for Spanned<T> {
137    fn clone(&self) -> Self {
138        Self {
139            value: self.value.clone(),
140            span: self.span,
141        }
142    }
143}
144
145impl<T: PartialEq> PartialEq for Spanned<T> {
146    fn eq(&self, other: &Self) -> bool {
147        // Only compare the value, not the span
148        self.value == other.value
149    }
150}
151
152impl<T: Eq> Eq for Spanned<T> {}
153
154// SAFETY: Spanned<T> is a simple struct with a value and span field, properly laid out
155unsafe impl<'a, T: Facet<'a>> Facet<'a> for Spanned<T> {
156    const SHAPE: &'static Shape = &const {
157        use facet_core::{TypeOpsIndirect, TypeParam, VTableIndirect};
158
159        unsafe fn drop_in_place<T>(ox: facet_core::OxPtrMut) {
160            // SAFETY: The caller guarantees ox points to a valid Spanned<T>
161            unsafe { core::ptr::drop_in_place(ox.ptr().as_byte_ptr() as *mut Spanned<T>) };
162        }
163
164        Shape::builder_for_sized::<Spanned<T>>("Spanned")
165            .vtable_indirect(&VTableIndirect::EMPTY)
166            .type_ops_indirect(
167                &const {
168                    TypeOpsIndirect {
169                        drop_in_place: drop_in_place::<T>,
170                        default_in_place: None,
171                        clone_into: None,
172                        is_truthy: None,
173                    }
174                },
175            )
176            .type_params(
177                &const {
178                    [TypeParam {
179                        name: "T",
180                        shape: T::SHAPE,
181                    }]
182                },
183            )
184            .ty(facet_core::Type::struct_builder(
185                StructKind::Struct,
186                &const {
187                    [
188                        FieldBuilder::new(
189                            "value",
190                            facet_core::shape_of::<T>,
191                            mem::offset_of!(Spanned<T>, value),
192                        )
193                        .build(),
194                        FieldBuilder::new(
195                            "span",
196                            facet_core::shape_of::<Span>,
197                            mem::offset_of!(Spanned<T>, span),
198                        )
199                        // Mark span as metadata - excluded from structural hashing/equality
200                        // Deserializers that support span metadata will populate this field
201                        .metadata("span")
202                        .build(),
203                    ]
204                },
205            )
206            .build())
207            .def(Def::Undefined)
208            .type_name(|_shape, f, opts| {
209                write!(f, "Spanned")?;
210                if let Some(opts) = opts.for_children() {
211                    write!(f, "<")?;
212                    if let Some(type_name_fn) = T::SHAPE.type_name {
213                        type_name_fn(T::SHAPE, f, opts)?;
214                    } else {
215                        write!(f, "{}", T::SHAPE.type_identifier)?;
216                    }
217                    write!(f, ">")?;
218                } else {
219                    write!(f, "<…>")?;
220                }
221                Ok(())
222            })
223            .build()
224    };
225}
226
227/// Check if a shape represents a type with span metadata (like `Spanned<T>`).
228///
229/// Returns `true` if the shape is a struct with:
230/// - At least one non-metadata field (the actual value)
231/// - A field with `#[facet(metadata = span)]` for storing source location
232///
233/// This allows any struct to be "spanned" by adding the metadata attribute,
234/// not just the built-in `Spanned<T>` wrapper.
235pub fn is_spanned_shape(shape: &Shape) -> bool {
236    use facet_core::{Type, UserType};
237
238    if let Type::User(UserType::Struct(struct_def)) = &shape.ty {
239        let has_span_metadata = struct_def
240            .fields
241            .iter()
242            .any(|f| f.metadata_kind() == Some("span"));
243        let has_value_field = struct_def.fields.iter().any(|f| !f.is_metadata());
244        return has_span_metadata && has_value_field;
245    }
246    false
247}
248
249/// Find the span metadata field in a struct shape.
250///
251/// Returns the field with `#[facet(metadata = span)]` if present.
252pub fn find_span_metadata_field(shape: &Shape) -> Option<&'static facet_core::Field> {
253    use facet_core::{Type, UserType};
254
255    if let Type::User(UserType::Struct(struct_def)) = &shape.ty {
256        return struct_def
257            .fields
258            .iter()
259            .find(|f| f.metadata_kind() == Some("span"));
260    }
261    None
262}
263
264/// Extract the inner value shape from a Spanned-like struct.
265///
266/// For a struct with span metadata, this returns the shape of the first
267/// non-metadata field (typically the `value` field in `Spanned<T>`).
268///
269/// This is useful when you need to look through a Spanned wrapper to
270/// determine the actual type being wrapped, such as when matching
271/// untagged enum variants against scalar values.
272///
273/// Returns `None` if the shape is not spanned or has no value fields.
274pub fn get_spanned_inner_shape(shape: &Shape) -> Option<&'static Shape> {
275    use facet_core::{Type, UserType};
276
277    if !is_spanned_shape(shape) {
278        return None;
279    }
280
281    if let Type::User(UserType::Struct(struct_def)) = &shape.ty {
282        // Find the first non-metadata field (the actual value)
283        struct_def
284            .fields
285            .iter()
286            .find(|f| !f.is_metadata())
287            .map(|f| f.shape.get())
288    } else {
289        None
290    }
291}