Skip to main content

capnp/
schema.rs

1//! Convenience wrappers of the datatypes defined in schema.capnp.
2
3use crate::dynamic_value;
4use crate::introspect::{self, RawBrandedStructSchema, RawEnumSchema};
5use crate::private::layout;
6use crate::schema_capnp::{annotation, enumerant, field, node};
7use crate::struct_list;
8use crate::traits::{IndexMove, ListIter, ShortListIter};
9use crate::Result;
10
11/// A struct node, with generics applied.
12#[derive(Clone, Copy)]
13pub struct StructSchema {
14    pub(crate) raw: RawBrandedStructSchema,
15    pub(crate) proto: node::Reader<'static>,
16}
17
18impl StructSchema {
19    pub fn new(raw: RawBrandedStructSchema) -> Self {
20        let proto = crate::any_pointer::Reader::new(
21            layout::PointerReader::get_root_from_arena(raw.generic.arena).unwrap(),
22        )
23        .get_as()
24        .unwrap();
25        Self { raw, proto }
26    }
27
28    pub fn get_proto(&self) -> node::Reader<'static> {
29        self.proto
30    }
31
32    /// For a group declared with a group or union `type` newtype, the ids
33    /// of that newtype and the newtypes it aliases, nearest first; the same
34    /// as the parent field's [`Field::get_newtype_ids`]. Empty for any
35    /// other struct or group.
36    ///
37    /// A group with `ids.contains(&vec3::TYPE_ID)` can be downcast to
38    /// `vec3::AnyReader` (or `AnyBuilder`).
39    pub fn get_newtype_ids(&self) -> &'static [u64] {
40        self.raw.generic.newtype_ids
41    }
42
43    pub fn get_fields(self) -> crate::Result<FieldList> {
44        if let node::Struct(s) = self.proto.which()? {
45            Ok(FieldList {
46                fields: s.get_fields()?,
47                parent: self,
48            })
49        } else {
50            panic!()
51        }
52    }
53
54    pub fn get_field_by_discriminant(self, discriminant: u16) -> Result<Option<Field>> {
55        match self
56            .raw
57            .generic
58            .members_by_discriminant
59            .get(discriminant as usize)
60        {
61            None => Ok(None),
62            Some(&idx) => Ok(Some(self.get_fields()?.get(idx))),
63        }
64    }
65
66    /// Looks up a field by name using binary search. Returns `None` if no matching field is found.
67    pub fn find_field_by_name(&self, name: &str) -> Result<Option<Field>> {
68        let fields = self.get_fields()?;
69        let mut lower: usize = 0;
70        let mut upper: usize = self.raw.generic.members_by_name.len();
71
72        while lower < upper {
73            let mid: usize = (lower + upper) / 2;
74            let candidate_index = self.raw.generic.members_by_name[mid];
75            let candidate_name = fields.get(candidate_index).get_proto().get_name()?;
76
77            use core::cmp::Ordering;
78            match (&name).partial_cmp(&candidate_name) {
79                Some(Ordering::Equal) => return Ok(Some(fields.get(candidate_index))),
80                Some(Ordering::Greater) => lower = mid + 1,
81                Some(Ordering::Less) => upper = mid,
82                None => unreachable!(),
83            }
84        }
85        Ok(None)
86    }
87
88    /// Like `find_field_by_name()`, but returns an error if the field is not found.
89    pub fn get_field_by_name(&self, name: &str) -> Result<Field> {
90        if let Some(field) = self.find_field_by_name(name)? {
91            Ok(field)
92        } else {
93            let mut error = crate::Error::from_kind(crate::ErrorKind::FieldNotFound);
94            write!(error, "{name}");
95            Err(error)
96        }
97    }
98
99    pub fn get_union_fields(self) -> Result<FieldSubset> {
100        if let node::Struct(s) = self.proto.which()? {
101            Ok(FieldSubset {
102                fields: s.get_fields()?,
103                indices: self.raw.generic.members_by_discriminant,
104                parent: self,
105            })
106        } else {
107            panic!()
108        }
109    }
110
111    pub fn get_non_union_fields(self) -> Result<FieldSubset> {
112        if let node::Struct(s) = self.proto.which()? {
113            Ok(FieldSubset {
114                fields: s.get_fields()?,
115                indices: self.raw.generic.nonunion_members,
116                parent: self,
117            })
118        } else {
119            panic!()
120        }
121    }
122
123    pub fn get_annotations(self) -> Result<AnnotationList> {
124        Ok(AnnotationList {
125            annotations: self.proto.get_annotations()?,
126            child_index: None,
127            get_annotation_type: self.raw.annotation_types,
128        })
129    }
130}
131
132impl From<RawBrandedStructSchema> for StructSchema {
133    fn from(rs: RawBrandedStructSchema) -> StructSchema {
134        StructSchema::new(rs)
135    }
136}
137
138impl ::core::cmp::PartialEq for StructSchema {
139    fn eq(&self, other: &Self) -> bool {
140        self.raw == other.raw
141    }
142}
143
144impl ::core::cmp::Eq for StructSchema {}
145
146impl ::core::hash::Hash for StructSchema {
147    fn hash<H: ::core::hash::Hasher>(&self, state: &mut H) {
148        self.raw.hash(state);
149    }
150}
151
152impl ::core::fmt::Debug for StructSchema {
153    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
154        // Two schemas with the same display name are unequal if their brandings
155        // differ, so also include the type id.
156        match self.proto.get_display_name().map(|n| n.to_str()) {
157            Ok(Ok(name)) => write!(f, "StructSchema({name}, {:?})", self.raw.type_id),
158            _ => write!(f, "StructSchema({:?})", self.raw),
159        }
160    }
161}
162
163/// A field of a struct, with generics applied.
164#[derive(Clone, Copy)]
165pub struct Field {
166    proto: field::Reader<'static>,
167    index: u16,
168    ty: introspect::Type,
169    pub(crate) parent: StructSchema,
170}
171
172impl Field {
173    pub fn get_proto(self) -> field::Reader<'static> {
174        self.proto
175    }
176
177    pub fn get_type(&self) -> introspect::Type {
178        self.ty
179    }
180
181    pub fn get_index(&self) -> u16 {
182        self.index
183    }
184
185    pub fn get_annotations(self) -> Result<AnnotationList> {
186        Ok(AnnotationList {
187            annotations: self.proto.get_annotations()?,
188            child_index: Some(self.index),
189            get_annotation_type: self.parent.raw.annotation_types,
190        })
191    }
192
193    /// The ids of the `type` newtypes this field was declared with, nearest
194    /// first, or empty if it wasn't declared with one.
195    ///
196    /// With `type Point = Vec3;` and `type ChainedPoint = Point;`, a field
197    /// declared `corner :ChainedPoint` gives the ids of `ChainedPoint`,
198    /// `Point` and `Vec3`, in that order. Each newtype's generated module
199    /// has its id as `TYPE_ID`, so `ids.contains(&vec3::TYPE_ID)` asks "is
200    /// this field a `Vec3`, under whatever alias?".
201    ///
202    /// The field's type (`get_type`) is unchanged by this: a group newtype
203    /// is still a struct, and a scalar newtype its underlying type. For a
204    /// list, this describes the list field itself, not its elements.
205    pub fn get_newtype_ids(&self) -> &'static [u64] {
206        match self
207            .parent
208            .raw
209            .generic
210            .field_newtypes
211            .get(self.index as usize)
212        {
213            Some(ids) => ids,
214            None => &[],
215        }
216    }
217}
218
219impl ::core::cmp::PartialEq for Field {
220    fn eq(&self, other: &Self) -> bool {
221        self.parent == other.parent && self.index == other.index
222    }
223}
224impl ::core::cmp::Eq for Field {}
225impl ::core::hash::Hash for Field {
226    fn hash<H: ::core::hash::Hasher>(&self, state: &mut H) {
227        self.parent.hash(state);
228        self.index.hash(state);
229    }
230}
231
232impl ::core::fmt::Debug for Field {
233    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
234        match self.proto.get_name().map(|n| n.to_str()) {
235            Ok(Ok(name)) => write!(f, "Field({name}, {:?})", self.parent),
236            _ => write!(f, "Field(index {}, {:?})", self.index, self.parent),
237        }
238    }
239}
240
241/// A list of fields of a struct, with generics applied.
242#[derive(Clone, Copy)]
243pub struct FieldList {
244    pub(crate) fields: crate::struct_list::Reader<'static, field::Owned>,
245    pub(crate) parent: StructSchema,
246}
247
248impl FieldList {
249    pub fn len(&self) -> u16 {
250        self.fields.len().try_into().unwrap()
251    }
252
253    pub fn is_empty(&self) -> bool {
254        self.len() == 0
255    }
256
257    pub fn get(self, index: u16) -> Field {
258        Field {
259            proto: self.fields.get(index as u32),
260            index,
261            ty: (self.parent.raw.field_types)(index),
262            parent: self.parent,
263        }
264    }
265
266    pub fn iter(self) -> ShortListIter<Self, Field> {
267        ShortListIter::new(self, self.len())
268    }
269}
270
271impl IndexMove<u16, Field> for FieldList {
272    fn index_move(&self, index: u16) -> Field {
273        self.get(index)
274    }
275}
276
277impl ::core::iter::IntoIterator for FieldList {
278    type Item = Field;
279    type IntoIter = ShortListIter<FieldList, Self::Item>;
280
281    fn into_iter(self) -> Self::IntoIter {
282        self.iter()
283    }
284}
285
286/// A list of a subset of fields of a struct, with generics applied.
287#[derive(Clone, Copy)]
288pub struct FieldSubset {
289    fields: struct_list::Reader<'static, field::Owned>,
290    indices: &'static [u16],
291    parent: StructSchema,
292}
293
294impl FieldSubset {
295    pub fn len(&self) -> u16 {
296        self.indices.len().try_into().unwrap()
297    }
298
299    pub fn is_empty(&self) -> bool {
300        self.len() == 0
301    }
302
303    pub fn get(self, index: u16) -> Field {
304        let index = self.indices[index as usize];
305        Field {
306            proto: self.fields.get(index as u32),
307            index,
308            ty: (self.parent.raw.field_types)(index),
309            parent: self.parent,
310        }
311    }
312
313    pub fn iter(self) -> ShortListIter<Self, Field> {
314        ShortListIter::new(self, self.len())
315    }
316}
317
318impl IndexMove<u16, Field> for FieldSubset {
319    fn index_move(&self, index: u16) -> Field {
320        self.get(index)
321    }
322}
323
324impl ::core::iter::IntoIterator for FieldSubset {
325    type Item = Field;
326    type IntoIter = ShortListIter<FieldSubset, Self::Item>;
327
328    fn into_iter(self) -> Self::IntoIter {
329        self.iter()
330    }
331}
332
333/// An enum, with generics applied. (Generics may affect types of annotations.)
334#[derive(Clone, Copy)]
335pub struct EnumSchema {
336    pub(crate) raw: RawEnumSchema,
337    pub(crate) proto: node::Reader<'static>,
338}
339
340impl EnumSchema {
341    pub fn new(raw: RawEnumSchema) -> Self {
342        let proto = crate::any_pointer::Reader::new(
343            layout::PointerReader::get_root_from_arena(raw.arena).unwrap(),
344        )
345        .get_as()
346        .unwrap();
347        Self { raw, proto }
348    }
349
350    pub fn get_proto(self) -> node::Reader<'static> {
351        self.proto
352    }
353
354    pub fn get_enumerants(self) -> crate::Result<EnumerantList> {
355        if let node::Enum(s) = self.proto.which()? {
356            Ok(EnumerantList {
357                enumerants: s.get_enumerants()?,
358                parent: self,
359            })
360        } else {
361            panic!()
362        }
363    }
364
365    pub fn get_annotations(self) -> Result<AnnotationList> {
366        Ok(AnnotationList {
367            annotations: self.proto.get_annotations()?,
368            child_index: None,
369            get_annotation_type: self.raw.annotation_types,
370        })
371    }
372}
373
374impl From<RawEnumSchema> for EnumSchema {
375    fn from(re: RawEnumSchema) -> EnumSchema {
376        EnumSchema::new(re)
377    }
378}
379
380impl ::core::cmp::PartialEq for EnumSchema {
381    fn eq(&self, other: &Self) -> bool {
382        self.raw == other.raw
383    }
384}
385
386impl ::core::cmp::Eq for EnumSchema {}
387
388impl ::core::hash::Hash for EnumSchema {
389    fn hash<H: ::core::hash::Hasher>(&self, state: &mut H) {
390        self.raw.hash(state);
391    }
392}
393
394impl ::core::fmt::Debug for EnumSchema {
395    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
396        match self.proto.get_display_name().map(|n| n.to_str()) {
397            Ok(Ok(name)) => write!(f, "EnumSchema({name})"),
398            _ => write!(f, "EnumSchema({:?})", self.raw),
399        }
400    }
401}
402
403/// An enumerant, with generics applied. (Generics may affect types of annotations.)
404#[derive(Clone, Copy)]
405pub struct Enumerant {
406    ordinal: u16,
407    parent: EnumSchema,
408    proto: enumerant::Reader<'static>,
409}
410
411impl Enumerant {
412    pub fn get_containing_enum(self) -> EnumSchema {
413        self.parent
414    }
415
416    pub fn get_ordinal(self) -> u16 {
417        self.ordinal
418    }
419
420    pub fn get_proto(self) -> enumerant::Reader<'static> {
421        self.proto
422    }
423
424    pub fn get_annotations(self) -> Result<AnnotationList> {
425        Ok(AnnotationList {
426            annotations: self.proto.get_annotations()?,
427            child_index: Some(self.ordinal),
428            get_annotation_type: self.parent.raw.annotation_types,
429        })
430    }
431}
432
433impl ::core::cmp::PartialEq for Enumerant {
434    fn eq(&self, other: &Self) -> bool {
435        self.parent == other.parent && self.ordinal == other.ordinal
436    }
437}
438impl ::core::cmp::Eq for Enumerant {}
439impl ::core::hash::Hash for Enumerant {
440    fn hash<H: ::core::hash::Hasher>(&self, state: &mut H) {
441        self.parent.hash(state);
442        self.ordinal.hash(state);
443    }
444}
445
446impl ::core::fmt::Debug for Enumerant {
447    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
448        match self.proto.get_name().map(|n| n.to_str()) {
449            Ok(Ok(name)) => write!(f, "Enumerant({name}, {:?})", self.parent),
450            _ => write!(f, "Enumerant(ordinal {}, {:?})", self.ordinal, self.parent),
451        }
452    }
453}
454
455/// A list of enumerants.
456#[derive(Clone, Copy)]
457pub struct EnumerantList {
458    enumerants: struct_list::Reader<'static, enumerant::Owned>,
459    parent: EnumSchema,
460}
461
462impl EnumerantList {
463    pub fn len(&self) -> u16 {
464        self.enumerants.len().try_into().unwrap()
465    }
466
467    pub fn is_empty(&self) -> bool {
468        self.len() == 0
469    }
470
471    pub fn get(self, ordinal: u16) -> Enumerant {
472        Enumerant {
473            proto: self.enumerants.get(ordinal as u32),
474            ordinal,
475            parent: self.parent,
476        }
477    }
478
479    pub fn iter(self) -> ShortListIter<Self, Enumerant> {
480        ShortListIter::new(self, self.len())
481    }
482}
483
484impl IndexMove<u16, Enumerant> for EnumerantList {
485    fn index_move(&self, index: u16) -> Enumerant {
486        self.get(index)
487    }
488}
489
490impl ::core::iter::IntoIterator for EnumerantList {
491    type Item = Enumerant;
492    type IntoIter = ShortListIter<Self, Self::Item>;
493
494    fn into_iter(self) -> Self::IntoIter {
495        self.iter()
496    }
497}
498
499/// An annotation.
500#[derive(Clone, Copy)]
501pub struct Annotation {
502    proto: annotation::Reader<'static>,
503    ty: introspect::Type,
504}
505
506impl Annotation {
507    /// Gets the value held in this annotation.
508    pub fn get_value(self) -> Result<dynamic_value::Reader<'static>> {
509        dynamic_value::Reader::new(self.proto.get_value()?, self.ty)
510    }
511
512    /// Gets the ID of the annotation node.
513    pub fn get_id(&self) -> u64 {
514        self.proto.get_id()
515    }
516
517    /// Gets the type of the value held in this annotation.
518    pub fn get_type(&self) -> introspect::Type {
519        self.ty
520    }
521}
522
523/// A list of annotations.
524#[derive(Clone, Copy)]
525pub struct AnnotationList {
526    annotations: struct_list::Reader<'static, annotation::Owned>,
527    child_index: Option<u16>,
528    get_annotation_type: fn(Option<u16>, u32) -> introspect::Type,
529}
530
531impl AnnotationList {
532    pub fn len(&self) -> u32 {
533        self.annotations.len()
534    }
535
536    pub fn is_empty(&self) -> bool {
537        self.len() == 0
538    }
539
540    pub fn get(self, index: u32) -> Annotation {
541        let proto = self.annotations.get(index);
542        let ty = (self.get_annotation_type)(self.child_index, index);
543        Annotation { proto, ty }
544    }
545
546    /// Returns the first annotation in the list that matches `id`.
547    /// Otherwise returns `None`.
548    pub fn find(self, id: u64) -> Option<Annotation> {
549        self.iter().find(|&annotation| annotation.get_id() == id)
550    }
551
552    pub fn iter(self) -> ListIter<Self, Annotation> {
553        ListIter::new(self, self.len())
554    }
555}
556
557impl IndexMove<u32, Annotation> for AnnotationList {
558    fn index_move(&self, index: u32) -> Annotation {
559        self.get(index)
560    }
561}
562
563impl ::core::iter::IntoIterator for AnnotationList {
564    type Item = Annotation;
565    type IntoIter = ListIter<Self, Self::Item>;
566
567    fn into_iter(self) -> Self::IntoIter {
568        self.iter()
569    }
570}
571
572#[cfg(test)]
573mod tests {
574    use crate::introspect::Introspect;
575
576    #[cfg(feature = "std")]
577    #[test]
578    fn fields_can_be_hashed() {
579        let crate::introspect::TypeVariant::Struct(struct_schema) =
580            crate::schema_capnp::node::Owned::introspect().which()
581        else {
582            panic!("Expected a struct schema");
583        };
584
585        let struct_schema = crate::schema::StructSchema::new(struct_schema);
586
587        let display_name = struct_schema.get_field_by_name("displayName").unwrap();
588        let id = struct_schema.get_field_by_name("id").unwrap();
589
590        let mut map = std::collections::HashMap::new();
591        map.insert(display_name, 1);
592        map.insert(id, 2);
593
594        assert_eq!(map.get(&display_name), Some(&1));
595        assert_eq!(map.get(&id), Some(&2));
596        assert_eq!(
597            map.get(&struct_schema.get_field_by_name("displayName").unwrap()),
598            Some(&1)
599        );
600        assert_eq!(
601            map.get(&struct_schema.get_field_by_name("id").unwrap()),
602            Some(&2)
603        );
604    }
605
606    #[test]
607    fn fields_can_be_compared() {
608        let crate::introspect::TypeVariant::Struct(struct_schema) =
609            crate::schema_capnp::node::Owned::introspect().which()
610        else {
611            panic!("Expected a struct schema");
612        };
613
614        let struct_schema = crate::schema::StructSchema::new(struct_schema);
615
616        let display_name = struct_schema.get_field_by_name("displayName").unwrap();
617        let id = struct_schema.get_field_by_name("id").unwrap();
618
619        assert_eq!(display_name, display_name);
620        assert_eq!(
621            display_name,
622            struct_schema.get_field_by_name("displayName").unwrap()
623        );
624        assert_eq!(id, id);
625        assert_eq!(id, struct_schema.get_field_by_name("id").unwrap());
626
627        assert_ne!(display_name, id);
628    }
629
630    #[cfg(feature = "std")]
631    #[test]
632    fn schemas_can_be_hashed() {
633        let node_schema = {
634            let crate::introspect::TypeVariant::Struct(schema) =
635                crate::schema_capnp::node::Owned::introspect().which()
636            else {
637                panic!("Expected a struct schema");
638            };
639
640            crate::schema::StructSchema::new(schema)
641        };
642        let cgr_schema = {
643            let crate::introspect::TypeVariant::Struct(schema) =
644                crate::schema_capnp::code_generator_request::Owned::introspect().which()
645            else {
646                panic!("Expected a struct schema");
647            };
648            crate::schema::StructSchema::new(schema)
649        };
650
651        let mut map = std::collections::HashMap::new();
652        map.insert(node_schema, 1);
653        map.insert(cgr_schema, 2);
654
655        assert_eq!(map.get(&node_schema), Some(&1));
656        assert_eq!(map.get(&cgr_schema), Some(&2));
657    }
658
659    #[test]
660    fn schemas_can_be_compared() {
661        let node_schema = {
662            let crate::introspect::TypeVariant::Struct(schema) =
663                crate::schema_capnp::node::Owned::introspect().which()
664            else {
665                panic!("Expected a struct schema");
666            };
667
668            crate::schema::StructSchema::new(schema)
669        };
670        let cgr_schema = {
671            let crate::introspect::TypeVariant::Struct(schema) =
672                crate::schema_capnp::code_generator_request::Owned::introspect().which()
673            else {
674                panic!("Expected a struct schema");
675            };
676            crate::schema::StructSchema::new(schema)
677        };
678
679        assert_eq!(node_schema, node_schema);
680        assert_eq!(cgr_schema, cgr_schema);
681        assert_ne!(node_schema, cgr_schema);
682    }
683
684    #[test]
685    fn enum_schemas_can_be_compared() {
686        let crate::introspect::TypeVariant::Enum(raw) =
687            crate::schema_capnp::ElementSize::introspect().which()
688        else {
689            panic!("Expected an enum schema");
690        };
691        let schema = crate::schema::EnumSchema::new(raw);
692
693        assert_eq!(schema, crate::schema::EnumSchema::new(raw));
694
695        let enumerants = schema.get_enumerants().unwrap();
696        assert_eq!(enumerants.get(0), enumerants.get(0));
697        assert_ne!(enumerants.get(0), enumerants.get(1));
698    }
699
700    #[cfg(feature = "std")]
701    #[test]
702    fn enumerants_can_be_hashed() {
703        let crate::introspect::TypeVariant::Enum(raw) =
704            crate::schema_capnp::ElementSize::introspect().which()
705        else {
706            panic!("Expected an enum schema");
707        };
708        let schema = crate::schema::EnumSchema::new(raw);
709        let enumerants = schema.get_enumerants().unwrap();
710
711        let mut map = std::collections::HashMap::new();
712        map.insert(enumerants.get(0), 0);
713        map.insert(enumerants.get(1), 1);
714
715        assert_eq!(map.get(&enumerants.get(0)), Some(&0));
716        assert_eq!(map.get(&enumerants.get(1)), Some(&1));
717    }
718
719    #[test]
720    fn type_variants_can_be_compared() {
721        use crate::introspect::TypeVariant;
722
723        assert_eq!(u32::introspect().which(), TypeVariant::UInt32);
724        assert_ne!(u32::introspect().which(), TypeVariant::Int32);
725        assert_eq!(
726            crate::schema_capnp::node::Owned::introspect().which(),
727            crate::schema_capnp::node::Owned::introspect().which()
728        );
729        assert_ne!(
730            crate::schema_capnp::node::Owned::introspect().which(),
731            crate::schema_capnp::code_generator_request::Owned::introspect().which()
732        );
733    }
734}