ossa_typeable/
lib.rs

1#[cfg(feature = "im")]
2mod im;
3pub mod internal;
4#[cfg(feature = "serde")]
5mod serde;
6
7use internal::helper_string_non_ascii;
8use lazy_static::lazy_static;
9pub use ossa_typeable_derive::Typeable;
10use sha2::{Digest, Sha256};
11use std::fmt;
12
13use crate::internal::{
14    helper_type_args_count, helper_type_constructor, helper_type_ident, helper_usize,
15};
16
17/// A unique identifier for a type. It is typically derived from the sha256 hash of the type's declaration.
18#[derive(Copy, Clone, Debug, PartialEq, Eq)]
19pub struct TypeId([u8; 32]);
20
21impl TypeId {
22    pub fn new(h: [u8; 32]) -> TypeId {
23        TypeId(h)
24    }
25
26    pub fn identifier(&self) -> [u8; 32] {
27        self.0
28    }
29}
30
31impl fmt::Display for TypeId {
32    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
33        write!(f, "0x")?;
34        for b in self.0 {
35            write!(f, "{:02X}", b)?;
36        }
37        Ok(())
38    }
39}
40
41impl AsRef<[u8]> for TypeId {
42    fn as_ref(&self) -> &[u8] {
43        &self.0
44    }
45}
46
47/// A trait that specifies unique identifiers for types in a deterministic way that can be shared
48/// on multiple machines over the network.
49///
50/// Requirements:
51/// - 1-1 mapping between a (fully instantiated) type and its identifier, assuming no hash collisions.
52/// - A type identifier should be deterministic so that it is the same on different machines (and compilation configurations).
53/// - Changes to a type's semantics should result in a different type identifier. By default, type
54/// identifiers are derived from type declarations, so changes to semantics like function
55/// implementations should cause the type identifier to change. In practice, this may require tag
56/// updates (ex. changing a tag from "v1" to "v2" when a type's implementation changes).
57///
58/// The following is the grammar of how the type is hashed to create its identifier. Primitive
59/// types may have custom encodings that do not match this grammar.
60///
61/// typeable :=
62///   type_name type_arg_count tag type_body
63///
64/// type_name := string
65/// type_arg_count := u8
66/// type_body :=
67///     // Struct
68///     '0' fields
69///     // Enum
70///   | '1' variant_count variant*
71///
72/// fields :=
73///     // Named fields {}
74///     '0' field_count field
75///     // Unnamed fields ()
76///   | '1' field_count type_ident*
77///
78/// field := field_name type_ident
79/// field_name := string
80///
81/// tag := string | _
82///
83/// variant := variant_name fields
84///
85/// field_count := u8
86/// char_count := u8
87/// variant_count := u8
88///
89/// string := char_count alphanumeric
90/// alphanumeric := [a-zA-Z0-9_]*
91///
92/// type_ident := [u8; 32]
93///
94pub trait Typeable {
95    /// A unique identifier for a given type.
96    fn type_ident() -> TypeId; // JP: This should be a const, but rust doesn't like that.
97}
98
99macro_rules! derive_typeable_primitive {
100    ( $type_name: ident ) => {
101        derive_typeable_primitive!($type_name, $type_name);
102    };
103    ( $type_name: ident, $mod_name: ident ) => {
104        mod $mod_name {
105            use super::*;
106
107            lazy_static! {
108                static ref DERIVED_TYPE_ID: TypeId = {
109                    let mut h = Sha256::new();
110                    helper_type_constructor(&mut h, stringify!($type_name));
111                    TypeId(h.finalize().into())
112                };
113            }
114
115            impl Typeable for $type_name {
116                fn type_ident() -> TypeId {
117                    *DERIVED_TYPE_ID
118                }
119            }
120        }
121    };
122}
123
124derive_typeable_primitive!(bool);
125derive_typeable_primitive!(char);
126derive_typeable_primitive!(u8);
127derive_typeable_primitive!(u16);
128derive_typeable_primitive!(u32);
129derive_typeable_primitive!(u64);
130derive_typeable_primitive!(u128);
131derive_typeable_primitive!(i8);
132derive_typeable_primitive!(i16);
133derive_typeable_primitive!(i32);
134derive_typeable_primitive!(i64);
135derive_typeable_primitive!(i128);
136derive_typeable_primitive!(f32);
137derive_typeable_primitive!(f64);
138derive_typeable_primitive!(String, string);
139
140impl<T: Typeable, const N: usize> Typeable for [T; N] {
141    fn type_ident() -> TypeId {
142        let mut h = Sha256::new();
143        helper_string_non_ascii(&mut h, "[]");
144        helper_type_args_count(&mut h, 1);
145        helper_usize(&mut h, N);
146        helper_type_ident::<T>(&mut h);
147        TypeId(h.finalize().into())
148    }
149}
150
151impl<T: Typeable> Typeable for Vec<T> {
152    fn type_ident() -> TypeId {
153        let mut h = Sha256::new();
154        helper_type_constructor(&mut h, "Vec");
155        helper_type_args_count(&mut h, 1);
156        helper_type_ident::<T>(&mut h);
157        TypeId(h.finalize().into())
158    }
159}
160
161impl<T: Typeable> Typeable for Option<T> {
162    fn type_ident() -> TypeId {
163        let mut h = Sha256::new();
164        helper_type_constructor(&mut h, "Option");
165        helper_type_args_count(&mut h, 1);
166        helper_type_ident::<T>(&mut h);
167        TypeId(h.finalize().into())
168    }
169}