1mod error;
16mod field;
17mod hash;
18mod schema;
19
20use std::num::NonZeroU16;
21
22#[cfg(feature = "serde")]
23use serde::{de::Error as DeError, Deserialize, Deserializer, Serialize, Serializer};
24
25pub use error::{SchemaError, SchemaResult};
26pub use field::{ChangePolicy, FieldCodec, FieldDef, FixedPoint};
27pub use hash::schema_hash;
28pub use schema::{ComponentDef, Schema, SchemaBuilder};
29
30#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
32pub struct ComponentId(NonZeroU16);
33
34impl ComponentId {
35 #[must_use]
37 pub const fn new(value: u16) -> Option<Self> {
38 match NonZeroU16::new(value) {
39 Some(value) => Some(Self(value)),
40 None => None,
41 }
42 }
43
44 #[must_use]
46 pub const fn get(self) -> u16 {
47 self.0.get()
48 }
49}
50
51#[cfg(feature = "serde")]
52impl Serialize for ComponentId {
53 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
54 serializer.serialize_u16(self.get())
55 }
56}
57
58#[cfg(feature = "serde")]
59impl<'de> Deserialize<'de> for ComponentId {
60 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
61 let value = u16::deserialize(deserializer)?;
62 ComponentId::new(value).ok_or_else(|| D::Error::custom("component id must be non-zero"))
63 }
64}
65
66#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
68pub struct FieldId(NonZeroU16);
69
70impl FieldId {
71 #[must_use]
73 pub const fn new(value: u16) -> Option<Self> {
74 match NonZeroU16::new(value) {
75 Some(value) => Some(Self(value)),
76 None => None,
77 }
78 }
79
80 #[must_use]
82 pub const fn get(self) -> u16 {
83 self.0.get()
84 }
85}
86
87#[cfg(feature = "serde")]
88impl Serialize for FieldId {
89 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
90 serializer.serialize_u16(self.get())
91 }
92}
93
94#[cfg(feature = "serde")]
95impl<'de> Deserialize<'de> for FieldId {
96 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
97 let value = u16::deserialize(deserializer)?;
98 FieldId::new(value).ok_or_else(|| D::Error::custom("field id must be non-zero"))
99 }
100}
101
102#[cfg(test)]
103mod tests {
104 use super::*;
105 use std::mem::size_of;
106
107 #[test]
108 fn public_api_exports() {
109 let _ = FieldCodec::bool();
111 let _ = ChangePolicy::Always;
112 let _ = FieldDef::new(FieldId::new(1).unwrap(), FieldCodec::bool());
113 let _ = Schema::builder();
114 let _ = schema_hash(&Schema::new(Vec::new()).unwrap());
115
116 let _: ComponentId = ComponentId::new(1).unwrap();
118 let _: FieldId = FieldId::new(1).unwrap();
119 }
120
121 #[test]
122 fn field_codec_basic_usage() {
123 let codec = FieldCodec::bool();
124 assert!(matches!(codec, FieldCodec::Bool));
125 }
126
127 #[test]
128 fn schema_hash_basic() {
129 let schema = Schema::new(Vec::new()).unwrap();
130 assert_ne!(schema_hash(&schema), 0);
131 }
132
133 #[test]
134 fn component_id_and_field_id_sizes() {
135 assert_eq!(size_of::<ComponentId>(), 2);
137 assert_eq!(size_of::<FieldId>(), 2);
138 }
139
140 #[test]
141 fn component_id_zero_is_invalid() {
142 assert!(ComponentId::new(0).is_none());
143 }
144
145 #[test]
146 fn field_id_zero_is_invalid() {
147 assert!(FieldId::new(0).is_none());
148 }
149}