near_schema_checker_core/
lib.rs

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