Skip to main content

capnp/
introspect.rs

1//! Traits and types to support run-time type introspection, i.e. reflection.
2
3use crate::private::layout::ElementSize;
4use crate::schema::{EnumSchema, StructSchema};
5
6/// A type that supports reflection. All types that can appear in a Cap'n Proto message
7/// implement this trait.
8pub trait Introspect {
9    /// Retrieves a description of the type.
10    fn introspect() -> Type;
11}
12
13/// A description of a Cap'n Proto type. The representation is
14/// optimized to avoid heap allocation.
15///
16/// To examine a `Type`, you should call the `which()` method.
17#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
18pub struct Type {
19    /// The type, minus any outer `List( )`.
20    base: BaseType,
21
22    /// How many times `base` is wrapped in `List( )`.
23    list_count: usize,
24}
25
26impl Type {
27    /// Constructs a new `Type` that is not a list.
28    fn new_base(base: BaseType) -> Self {
29        Self {
30            base,
31            list_count: 0,
32        }
33    }
34
35    /// Constructs a new `Type` that is a list wrapping some other `Type`.
36    pub fn list_of(mut element_type: Type) -> Self {
37        element_type.list_count += 1;
38        element_type
39    }
40
41    /// Unfolds a single layer of the `Type`, to allow for pattern matching.
42    pub fn which(&self) -> TypeVariant {
43        if self.list_count > 0 {
44            TypeVariant::List(Type {
45                base: self.base,
46                list_count: self.list_count - 1,
47            })
48        } else {
49            match self.base {
50                BaseType::Void => TypeVariant::Void,
51                BaseType::Bool => TypeVariant::Bool,
52                BaseType::Int8 => TypeVariant::Int8,
53                BaseType::Int16 => TypeVariant::Int16,
54                BaseType::Int32 => TypeVariant::Int32,
55                BaseType::Int64 => TypeVariant::Int64,
56                BaseType::UInt8 => TypeVariant::UInt8,
57                BaseType::UInt16 => TypeVariant::UInt16,
58                BaseType::UInt32 => TypeVariant::UInt32,
59                BaseType::UInt64 => TypeVariant::UInt64,
60                BaseType::Float32 => TypeVariant::Float32,
61                BaseType::Float64 => TypeVariant::Float64,
62                BaseType::Text => TypeVariant::Text,
63                BaseType::Data => TypeVariant::Data,
64                BaseType::Enum(re) => TypeVariant::Enum(re),
65                BaseType::Struct(rs) => TypeVariant::Struct(rs),
66                BaseType::AnyPointer => TypeVariant::AnyPointer,
67                BaseType::Capability => TypeVariant::Capability,
68            }
69        }
70    }
71
72    /// If this type T appears as List(T), then what is the expected
73    /// element size of the list?
74    pub(crate) fn expected_element_size(&self) -> ElementSize {
75        if self.list_count > 0 {
76            ElementSize::Pointer
77        } else {
78            match self.base {
79                BaseType::Void => ElementSize::Void,
80                BaseType::Bool => ElementSize::Bit,
81                BaseType::Int8 | BaseType::UInt8 => ElementSize::Byte,
82                BaseType::Int16 | BaseType::UInt16 | BaseType::Enum(_) => ElementSize::TwoBytes,
83                BaseType::Int32 | BaseType::UInt32 | BaseType::Float32 => ElementSize::FourBytes,
84                BaseType::Int64 | BaseType::UInt64 | BaseType::Float64 => ElementSize::EightBytes,
85                BaseType::Text | BaseType::Data | BaseType::AnyPointer | BaseType::Capability => {
86                    ElementSize::Pointer
87                }
88                BaseType::Struct(_) => ElementSize::InlineComposite,
89            }
90        }
91    }
92
93    /// Is the `Type` a pointer type?
94    pub fn is_pointer_type(&self) -> bool {
95        if self.list_count > 0 {
96            true
97        } else {
98            matches!(
99                self.base,
100                BaseType::Text
101                    | BaseType::Data
102                    | BaseType::AnyPointer
103                    | BaseType::Struct(_)
104                    | BaseType::Capability
105            )
106        }
107    }
108
109    /// Returns true if `self` is equal to `other` modulo
110    /// type parameters and interface types.
111    #[deprecated(
112        since = "0.27.0",
113        note = "Type now implements Eq. loose_equals ignores generics on structs, while Eq is more precise and most likely the one you want."
114    )]
115    pub fn loose_equals(&self, other: Self) -> bool {
116        match (self.which(), other.which()) {
117            (TypeVariant::Void, TypeVariant::Void) => true,
118            (TypeVariant::Bool, TypeVariant::Bool) => true,
119            (TypeVariant::UInt8, TypeVariant::UInt8) => true,
120            (TypeVariant::UInt16, TypeVariant::UInt16) => true,
121            (TypeVariant::UInt32, TypeVariant::UInt32) => true,
122            (TypeVariant::UInt64, TypeVariant::UInt64) => true,
123            (TypeVariant::Int8, TypeVariant::Int8) => true,
124            (TypeVariant::Int16, TypeVariant::Int16) => true,
125            (TypeVariant::Int32, TypeVariant::Int32) => true,
126            (TypeVariant::Int64, TypeVariant::Int64) => true,
127            (TypeVariant::Float32, TypeVariant::Float32) => true,
128            (TypeVariant::Float64, TypeVariant::Float64) => true,
129            (TypeVariant::Text, TypeVariant::Text) => true,
130            (TypeVariant::Data, TypeVariant::Data) => true,
131            (TypeVariant::Enum(es1), TypeVariant::Enum(es2)) => es1 == es2,
132            (TypeVariant::Struct(rbs1), TypeVariant::Struct(rbs2)) => {
133                // Ignore any type parameters. The original intent was that
134                // we would additionally check that the `field_types` fields
135                // were equal function pointers here. However, according to
136                // Miri's behavior at least, that check returns `false`
137                // more than we would like it to. So we settle for being
138                // a bit more accepting.
139                core::ptr::eq(rbs1.generic, rbs2.generic)
140            }
141            (TypeVariant::List(element1), TypeVariant::List(element2)) => {
142                element1.loose_equals(element2)
143            }
144            (TypeVariant::AnyPointer, TypeVariant::AnyPointer) => true,
145            (TypeVariant::Capability, TypeVariant::Capability) => true,
146            _ => false,
147        }
148    }
149}
150
151#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
152/// A `Type` unfolded one level. Suitable for pattern matching. Can be trivially
153/// converted to `Type` via the `From`/`Into` traits.
154pub enum TypeVariant {
155    Void,
156    Bool,
157    Int8,
158    Int16,
159    Int32,
160    Int64,
161    UInt8,
162    UInt16,
163    UInt32,
164    UInt64,
165    Float32,
166    Float64,
167    Text,
168    Data,
169    Struct(RawBrandedStructSchema),
170    AnyPointer,
171    Capability,
172    Enum(RawEnumSchema),
173    List(Type),
174}
175
176impl From<TypeVariant> for Type {
177    fn from(tv: TypeVariant) -> Type {
178        match tv {
179            TypeVariant::Void => Type::new_base(BaseType::Void),
180            TypeVariant::Bool => Type::new_base(BaseType::Bool),
181            TypeVariant::Int8 => Type::new_base(BaseType::Int8),
182            TypeVariant::Int16 => Type::new_base(BaseType::Int16),
183            TypeVariant::Int32 => Type::new_base(BaseType::Int32),
184            TypeVariant::Int64 => Type::new_base(BaseType::Int64),
185            TypeVariant::UInt8 => Type::new_base(BaseType::UInt8),
186            TypeVariant::UInt16 => Type::new_base(BaseType::UInt16),
187            TypeVariant::UInt32 => Type::new_base(BaseType::UInt32),
188            TypeVariant::UInt64 => Type::new_base(BaseType::UInt64),
189            TypeVariant::Float32 => Type::new_base(BaseType::Float32),
190            TypeVariant::Float64 => Type::new_base(BaseType::Float64),
191            TypeVariant::Text => Type::new_base(BaseType::Text),
192            TypeVariant::Data => Type::new_base(BaseType::Data),
193            TypeVariant::Struct(rbs) => Type::new_base(BaseType::Struct(rbs)),
194            TypeVariant::AnyPointer => Type::new_base(BaseType::AnyPointer),
195            TypeVariant::Capability => Type::new_base(BaseType::Capability),
196            TypeVariant::Enum(es) => Type::new_base(BaseType::Enum(es)),
197            TypeVariant::List(list) => Type::list_of(list),
198        }
199    }
200}
201
202/// A Cap'n Proto type, excluding `List`.
203#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
204enum BaseType {
205    Void,
206    Bool,
207    Int8,
208    Int16,
209    Int32,
210    Int64,
211    UInt8,
212    UInt16,
213    UInt32,
214    UInt64,
215    Float32,
216    Float64,
217    Text,
218    Data,
219    Struct(RawBrandedStructSchema),
220    AnyPointer,
221    Capability,
222    Enum(RawEnumSchema),
223}
224
225macro_rules! primitive_introspect(
226    ($t:ty, $v:ident) => (
227        impl Introspect for $t {
228            fn introspect() -> Type { Type::new_base(BaseType::$v) }
229        }
230    )
231);
232
233primitive_introspect!((), Void);
234primitive_introspect!(bool, Bool);
235primitive_introspect!(i8, Int8);
236primitive_introspect!(i16, Int16);
237primitive_introspect!(i32, Int32);
238primitive_introspect!(i64, Int64);
239primitive_introspect!(u8, UInt8);
240primitive_introspect!(u16, UInt16);
241primitive_introspect!(u32, UInt32);
242primitive_introspect!(u64, UInt64);
243primitive_introspect!(f32, Float32);
244primitive_introspect!(f64, Float64);
245
246/// Type information that gets included in the generated code for every
247/// user-defined Cap'n Proto struct.
248#[derive(Copy, Clone)]
249pub struct RawStructSchema {
250    /// The Node (as defined in schema.capnp), as a single segment message.
251    pub(crate) arena: &'static crate::private::arena::GeneratedCodeArena,
252
253    /// Indices (not ordinals) of fields that don't have a discriminant value.
254    pub(crate) nonunion_members: &'static [u16],
255
256    /// Map from discriminant value to field index.
257    pub(crate) members_by_discriminant: &'static [u16],
258
259    /// Indices of fields, sorted by their respective names.
260    pub(crate) members_by_name: &'static [u16],
261
262    /// Map from field index to the ids of the `type` newtypes the field was
263    /// declared with, nearest first. Empty (the default) when the struct has
264    /// no newtype fields, or was generated by an older code generator.
265    pub(crate) field_newtypes: &'static [&'static [u64]],
266
267    /// For a group that is a use site of a group or union `type` newtype:
268    /// the ids of the newtypes it was declared with, nearest first. Empty
269    /// for every other struct or group.
270    pub(crate) newtype_ids: &'static [u64],
271
272    /// For such a use site: where each of the newtype's members lives in
273    /// the parent's data, in the order the newtype declares them. This is
274    /// the table the generated `as_any()` hands to `AnyReader::new`.
275    pub(crate) any_offsets: &'static [u32],
276
277    /// For a use site of a union newtype: where its discriminant lives.
278    pub(crate) any_discriminant_offset: u32,
279}
280
281impl RawStructSchema {
282    /// Constructs a new `RawStructSchema`.
283    pub const fn new(
284        arena: &'static crate::private::arena::GeneratedCodeArena,
285        nonunion_members: &'static [u16],
286        members_by_discriminant: &'static [u16],
287        members_by_name: &'static [u16],
288    ) -> Self {
289        Self {
290            arena,
291            nonunion_members,
292            members_by_discriminant,
293            members_by_name,
294            field_newtypes: &[],
295            newtype_ids: &[],
296            any_offsets: &[],
297            any_discriminant_offset: 0,
298        }
299    }
300
301    /// Sets the per-field newtype ids; see [`crate::schema::Field::get_newtype_ids`].
302    pub const fn with_field_newtypes(mut self, field_newtypes: &'static [&'static [u64]]) -> Self {
303        self.field_newtypes = field_newtypes;
304        self
305    }
306
307    /// Marks this group as a use site of a group or union newtype, so the
308    /// dynamic API can `downcast` it to the newtype's `AnyReader` /
309    /// `AnyBuilder`. See [`crate::traits::AnyReader`].
310    pub const fn with_any_layout(
311        mut self,
312        newtype_ids: &'static [u64],
313        offsets: &'static [u32],
314        discriminant_offset: u32,
315    ) -> Self {
316        self.newtype_ids = newtype_ids;
317        self.any_offsets = offsets;
318        self.any_discriminant_offset = discriminant_offset;
319        self
320    }
321}
322
323/// A RawStructSchema with branding information, i.e. resolution of type parameters.
324/// To use one of this, you will usually want to convert it to a `schema::StructSchema`,
325/// which can be done via `into()`.
326#[derive(Copy, Clone)]
327pub struct RawBrandedStructSchema {
328    /// The unbranded base schema.
329    pub generic: &'static RawStructSchema,
330
331    /// Map from field index (not ordinal) to Type.
332    pub field_types: fn(u16) -> Type,
333
334    /// Map from (maybe field index, annotation index) to the Type
335    /// of the value held by that annotation.
336    pub annotation_types: fn(Option<u16>, u32) -> Type,
337
338    /// Used to compare schemas at runtime - the TypeId of the Owned struct that
339    /// this schema describes, including its branding.
340    pub type_id: ::core::any::TypeId,
341}
342
343impl ::core::cmp::PartialEq for RawBrandedStructSchema {
344    fn eq(&self, other: &Self) -> bool {
345        self.type_id == other.type_id
346    }
347}
348impl ::core::cmp::Eq for RawBrandedStructSchema {}
349impl ::core::hash::Hash for RawBrandedStructSchema {
350    fn hash<H: ::core::hash::Hasher>(&self, state: &mut H) {
351        self.type_id.hash(state);
352    }
353}
354
355impl core::fmt::Debug for RawBrandedStructSchema {
356    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::result::Result<(), core::fmt::Error> {
357        write!(
358            f,
359            "RawBrandedStructSchema({:?}, {:?})",
360            self.generic as *const _, self.field_types as *const fn(u16) -> Type
361        )
362    }
363}
364
365impl From<StructSchema> for RawBrandedStructSchema {
366    fn from(value: StructSchema) -> Self {
367        value.raw
368    }
369}
370
371/// Type information that gets included in the generated code for every
372/// user-defined Cap'n Proto enum.
373///
374/// To use one of these, you will usually want to convert it to a `schema::EnumSchema`,
375/// which can be done via `into()`.
376#[derive(Clone, Copy)]
377pub struct RawEnumSchema {
378    /// The Node (as defined in schema.capnp), as a single segment message.
379    pub(crate) arena: &'static crate::private::arena::GeneratedCodeArena,
380
381    /// Map from (maybe enumerant index, annotation index) to the Type
382    /// of the value held by that annotation.
383    pub(crate) annotation_types: fn(Option<u16>, u32) -> Type,
384}
385
386impl core::cmp::PartialEq for RawEnumSchema {
387    fn eq(&self, other: &Self) -> bool {
388        ::core::ptr::eq(self.arena, other.arena)
389    }
390}
391
392impl core::cmp::Eq for RawEnumSchema {}
393impl core::hash::Hash for RawEnumSchema {
394    fn hash<H: ::core::hash::Hasher>(&self, state: &mut H) {
395        (self.arena as *const crate::private::arena::GeneratedCodeArena).hash(state);
396    }
397}
398
399impl core::fmt::Debug for RawEnumSchema {
400    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::result::Result<(), core::fmt::Error> {
401        write!(f, "RawEnumSchema({:?})", self.arena as *const _)
402    }
403}
404
405impl RawEnumSchema {
406    /// Constructs a new `RawEnumSchema`.
407    pub const fn new(
408        arena: &'static crate::private::arena::GeneratedCodeArena,
409        annotation_types: fn(Option<u16>, u32) -> Type,
410    ) -> Self {
411        Self {
412            arena,
413            annotation_types,
414        }
415    }
416}
417
418impl From<EnumSchema> for RawEnumSchema {
419    fn from(value: EnumSchema) -> Self {
420        value.raw
421    }
422}
423
424/**
425Function intended to be called by generated `get_field_types()` methods.
426Defined here so that we can use inline format args syntax, which did
427not exist before Rust edition 2021. Not intended to be called directly by
428end users.
429 */
430pub fn panic_invalid_field_index(index: u16) -> ! {
431    panic!("invalid field index {index}")
432}
433
434/**
435Function intended to be called by generated `get_annotation_types()` methods.
436Defined here so that we can use inline format args syntax, which did
437not exist before Rust edition 2021. Not intended to be called directly by
438end users.
439 */
440pub fn panic_invalid_annotation_indices(child_index: Option<u16>, index: u32) -> ! {
441    panic!("invalid annotation indices ({child_index:?}, {index})")
442}