shapely_core/
shape.rs

1use std::{alloc::Layout, any::TypeId, collections::HashSet, fmt::Formatter};
2
3use nonmax::NonMaxU32;
4
5/// Schema for reflection of a type
6#[derive(Clone, Copy)]
7pub struct Shape {
8    /// A descriptive name for the type, e.g. `u64`, or `Person`
9    pub name: &'static str,
10
11    /// The typeid of the underlying type
12    pub typeid: TypeId,
13
14    /// Size & alignment
15    pub layout: Layout,
16
17    /// Details/contents of the value
18    pub innards: Innards,
19
20    /// Set the value at a given address to the default value for this type
21    pub set_to_default: Option<fn(*mut u8)>,
22
23    /// Drop the value at a given address
24    ///
25    /// # Safety
26    ///
27    /// This function should be called only for initialized values.
28    /// It's the caller's responsibility to ensure the address points to a valid value.
29    pub drop_in_place: Option<DropFunction>,
30}
31
32impl PartialEq for Shape {
33    fn eq(&self, other: &Self) -> bool {
34        self.typeid == other.typeid
35    }
36}
37
38impl Eq for Shape {}
39
40impl std::hash::Hash for Shape {
41    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
42        self.typeid.hash(state);
43    }
44}
45
46impl Shape {
47    const INDENT: usize = 2;
48
49    /// Pretty-print this shape, recursively.
50    pub fn pretty_print_recursive(&self, f: &mut Formatter) -> std::fmt::Result {
51        self.pretty_print_recursive_internal(f, &mut HashSet::new(), 0)
52    }
53
54    fn pretty_print_recursive_internal(
55        &self,
56        f: &mut Formatter,
57        printed_schemas: &mut HashSet<Shape>,
58        indent: usize,
59    ) -> std::fmt::Result {
60        if !printed_schemas.insert(*self) {
61            writeln!(
62                f,
63                "{:indent$}\x1b[1;33m{}\x1b[0m (\x1b[1;31malready printed\x1b[0m)",
64                "",
65                self.name,
66                indent = indent
67            )?;
68            return Ok(());
69        }
70
71        writeln!(
72            f,
73            "{:indent$}\x1b[1;33m{}\x1b[0m (size: \x1b[1;34m{}\x1b[0m, align: \x1b[1;35m{}\x1b[0m)",
74            "",
75            self.name,
76            self.layout.size(),
77            self.layout.align(),
78            indent = indent
79        )?;
80
81        match &self.innards {
82            Innards::Struct { fields } => {
83                for field in *fields {
84                    writeln!(
85                        f,
86                        "{:indent$}\x1b[1;32m{}\x1b[0m: ",
87                        "",
88                        field.name,
89                        indent = indent + Self::INDENT
90                    )?;
91                    field.shape.get().pretty_print_recursive_internal(
92                        f,
93                        printed_schemas,
94                        indent + Self::INDENT * 2,
95                    )?;
96                }
97            }
98            Innards::HashMap { value_shape } => {
99                writeln!(
100                    f,
101                    "{:indent$}\x1b[1;36mHashMap with arbitrary keys and value shape:\x1b[0m",
102                    "",
103                    indent = indent + Self::INDENT
104                )?;
105                value_shape.get().pretty_print_recursive_internal(
106                    f,
107                    printed_schemas,
108                    indent + Self::INDENT * 2,
109                )?;
110            }
111            Innards::Array(elem_schema) => {
112                write!(
113                    f,
114                    "{:indent$}\x1b[1;36mArray of:\x1b[0m ",
115                    "",
116                    indent = indent + Self::INDENT
117                )?;
118                elem_schema.get().pretty_print_recursive_internal(
119                    f,
120                    printed_schemas,
121                    indent + Self::INDENT * 2,
122                )?;
123            }
124            Innards::Transparent(inner_schema) => {
125                write!(
126                    f,
127                    "{:indent$}\x1b[1;36mTransparent wrapper for:\x1b[0m ",
128                    "",
129                    indent = indent + Self::INDENT
130                )?;
131                inner_schema.get().pretty_print_recursive_internal(
132                    f,
133                    printed_schemas,
134                    indent + Self::INDENT * 2,
135                )?;
136            }
137            Innards::Scalar(scalar) => {
138                writeln!(
139                    f,
140                    "{:indent$}\x1b[1;36mScalar:\x1b[0m \x1b[1;33m{:?}\x1b[0m",
141                    "",
142                    scalar,
143                    indent = indent
144                )?;
145            }
146        }
147
148        Ok(())
149    }
150}
151
152impl std::fmt::Debug for Shape {
153    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
154        self.pretty_print_recursive(f)
155    }
156}
157
158/// The shape of a schema: is it more map-shaped, array-shaped, scalar-shaped?
159#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
160pub enum Innards {
161    /// Struct with statically-known fields
162    Struct { fields: &'static [Field<'static>] },
163
164    /// HashMap — keys are dynamic, values are homogeneous
165    HashMap { value_shape: ShapeDesc },
166
167    /// Ordered list of heterogenous values, variable size
168    Array(ShapeDesc),
169
170    /// Transparent — forwards to another known schema
171    Transparent(ShapeDesc),
172
173    /// Scalar — known based type
174    Scalar(Scalar),
175}
176
177impl Innards {
178    /// Returns a reference to the fields of this map
179    pub fn static_fields(&self) -> &'static [Field<'static>] {
180        match self {
181            Innards::Struct { fields } => fields,
182            _ => &[],
183        }
184    }
185}
186#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
187pub struct Field<'s> {
188    /// key for the map field
189    pub name: &'s str,
190
191    /// schema of the inner type
192    pub shape: ShapeDesc,
193
194    /// offset of the field in the map, if known.
195    ///
196    /// For example, when deserializing a self-descriptive format like JSON, we're going to get
197    /// some map fields with dynamically discovered field names, and they're not going to have
198    /// an offset.
199    ///
200    /// However, when deserializing formats that are non-self descriptive and working from an
201    /// existing shape, then their map fields are probably going to have offsets, especially if
202    /// they're using derived macros.
203    pub offset: Option<NonMaxU32>,
204}
205
206/// The outcome of trying to set a field on a map
207#[derive(Debug, Clone, Copy)]
208pub enum SetFieldOutcome {
209    /// The field was successfully set
210    Accepted,
211
212    /// The field was rejected (unknown field set in a struct, for example)
213    Rejected,
214}
215
216#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
217#[non_exhaustive]
218pub enum Scalar {
219    // Valid utf-8
220    String,
221
222    // Not valid utf-8 🤷
223    Bytes,
224
225    I8,
226    I16,
227    I32,
228    I64,
229    I128,
230
231    U8,
232    U16,
233    U32,
234    U64,
235    U128,
236
237    F32,
238    F64,
239
240    Boolean,
241
242    /// An empty tuple, null, undefined, whatever you wish
243    Nothing,
244}
245
246/// A function that drops a value at a specific memory address
247pub type DropFunction = fn(*mut u8);
248
249/// A function that returns a shape. There should only be one of these per concrete type in a
250/// program. This enables optimizations.
251#[derive(Clone, Copy)]
252pub struct ShapeDesc(pub fn() -> Shape);
253
254impl From<fn() -> Shape> for ShapeDesc {
255    fn from(f: fn() -> Shape) -> Self {
256        Self(f)
257    }
258}
259
260impl ShapeDesc {
261    /// Build the inner shape
262    pub fn get(&self) -> Shape {
263        (self.0)()
264    }
265}
266
267impl PartialEq for ShapeDesc {
268    fn eq(&self, other: &Self) -> bool {
269        if std::ptr::eq(self.0 as *const (), other.0 as *const ()) {
270            true
271        } else {
272            let self_shape = self.0();
273            let other_shape = other.0();
274            self_shape == other_shape
275        }
276    }
277}
278
279impl Eq for ShapeDesc {}
280
281impl std::hash::Hash for ShapeDesc {
282    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
283        // Hash the function pointer
284        (self.0 as *const ()).hash(state);
285    }
286}
287
288impl std::fmt::Debug for ShapeDesc {
289    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
290        self.get().fmt(f)
291    }
292}