Skip to main content

near_schema_checker_core/
lib.rs

1use std::any::TypeId;
2
3pub type TypeName = &'static str;
4pub type Discriminant = u64;
5pub type FieldName = &'static str;
6pub type VariantName = &'static str;
7pub type Variant = Option<&'static [(FieldName, FieldTypeInfo)]>;
8
9/// Type name and its decomposition into type ids.
10/// Decomposition is defined recursively, starting from type id of the type
11/// itself, followed by decompositions of its generic parameters, respectively.
12/// For example, for `Vec<Vec<u8>>` it will be `[TypeId::of::<Vec<Vec<u8>>>(),
13/// TypeId::of::<Vec<u8>>(), TypeId::of::<u8>()]`.
14// TODO (#11755): consider better candidates for decomposition. For example,
15// `Vec<u8>` is not expected to implement `ProtocolSchema`, so its type id
16// won't help to identify changes in the outer struct.
17pub type FieldTypeInfo = (TypeName, &'static [TypeId]);
18
19#[derive(Debug, Copy, Clone)]
20pub enum ProtocolSchemaInfo {
21    Struct {
22        name: FieldName,
23        type_id: TypeId,
24        fields: &'static [(FieldName, FieldTypeInfo)],
25    },
26    Enum {
27        name: FieldName,
28        type_id: TypeId,
29        variants: &'static [(Discriminant, VariantName, Variant)],
30    },
31}
32
33impl ProtocolSchemaInfo {
34    pub fn type_id(&self) -> TypeId {
35        match self {
36            ProtocolSchemaInfo::Struct { type_id, .. } => *type_id,
37            ProtocolSchemaInfo::Enum { type_id, .. } => *type_id,
38        }
39    }
40
41    pub fn type_name(&self) -> TypeName {
42        match self {
43            ProtocolSchemaInfo::Struct { name, .. } => name,
44            ProtocolSchemaInfo::Enum { name, .. } => name,
45        }
46    }
47}
48
49#[cfg(feature = "protocol_schema")]
50inventory::collect!(ProtocolSchemaInfo);
51
52pub trait ProtocolSchema {
53    /// Workaround to be called directly for the specific type, if this is not
54    /// included by collecting tool for some reason.
55    /// Perhaps a call to this function, which is overridden for each type,
56    /// ensures that linker doesn't optimize the type out, even if all
57    /// implementations are no-op.
58    /// TODO (#11755): understand cases where it may be needed and find a
59    /// proper solution for them.
60    fn ensure_registration() {}
61}
62
63/// Implementation for primitive types.
64macro_rules! primitive_impl {
65    ($($t:ty),*) => {
66        $(
67            impl ProtocolSchema for $t {}
68
69            #[cfg(feature = "protocol_schema")]
70            inventory::submit! {
71                ProtocolSchemaInfo::Struct {
72                    name: stringify!($t),
73                    type_id: TypeId::of::<$t>(),
74                    fields: &[],
75                }
76            }
77        )*
78    }
79}
80
81primitive_impl!(bool, u8, u16, u32, u64, u128, i8, i16, i32, i64, i128, String);