1use std::{
4 alloc::Layout,
5 sync::atomic::{AtomicU32, Ordering::SeqCst},
6};
7
8use append_only_vec::AppendOnlyVec;
9use bones_utils::Deref;
10
11use crate::prelude::*;
12
13#[derive(Debug, Hash, PartialEq, Eq, Copy, Clone)]
15pub struct SchemaId {
16 id: u32,
17}
18
19#[doc(hidden)]
23#[derive(Deref, Clone, Debug)]
42pub struct Schema {
43 id: SchemaId,
44 #[deref]
45 data: SchemaData,
46 layout: Layout,
47 field_offsets: &'static [(Option<String>, usize)],
48}
49
50impl PartialEq for Schema {
51 fn eq(&self, other: &Self) -> bool {
52 self.id == other.id
53 }
54}
55impl Eq for Schema {}
56
57impl Schema {
58 #[inline]
60 pub fn id(&self) -> SchemaId {
61 self.id
62 }
63
64 #[inline]
66 pub fn schema(&self) -> &SchemaData {
67 &self.data
68 }
69
70 #[inline]
72 pub fn layout(&self) -> Layout {
73 self.layout
74 }
75
76 #[inline]
79 pub fn field_offsets(&self) -> &'static [(Option<String>, usize)] {
80 self.field_offsets
81 }
82
83 pub fn ensure_match(&self, other: &Self) -> Result<(), SchemaMismatchError> {
86 if self == other {
87 Ok(())
88 } else {
89 Err(SchemaMismatchError)
90 }
91 }
92}
93
94pub struct SchemaRegistry {
97 next_id: AtomicU32,
98 pub schemas: AppendOnlyVec<Schema>,
100}
101
102impl SchemaRegistry {
103 #[track_caller]
105 pub fn register(&self, schema_data: SchemaData) -> &Schema {
106 let id = SchemaId {
108 id: self.next_id.fetch_add(1, SeqCst),
109 };
110 assert_ne!(id.id, u32::MAX, "Exhausted all {} schema IDs", u32::MAX);
111
112 let SchemaLayoutInfo {
114 layout,
115 field_offsets,
116 } = schema_data.kind.compute_layout_info();
117
118 let field_offsets: Box<_> = field_offsets
120 .into_iter()
121 .map(|(name, offset)| (name.map(|n| n.to_string()), offset))
122 .collect();
123 let field_offsets = Box::leak(field_offsets);
124
125 let schema = Schema {
127 id,
128 data: schema_data,
129 layout,
130 field_offsets,
131 };
132
133 let idx = self.schemas.push(schema);
135
136 &self.schemas[idx]
137 }
138}
139
140pub static SCHEMA_REGISTRY: SchemaRegistry = SchemaRegistry {
142 next_id: AtomicU32::new(0),
143 schemas: AppendOnlyVec::new(),
144};
145
146#[cfg(test)]
147mod test {
148 use bones_utils::default;
149
150 use super::*;
151
152 #[test]
153 fn registry_smoke() {
154 let mut schemas = Vec::new();
155 for i in 0..100 {
156 let data = SchemaData {
157 name: format!("data{i}").into(),
158 full_name: format!("data{i}").into(),
159 kind: SchemaKind::Primitive(Primitive::U8),
160 type_data: default(),
161 type_id: None,
162 clone_fn: None,
163 drop_fn: None,
164 default_fn: None,
165 hash_fn: None,
166 eq_fn: None,
167 };
168
169 let schema = SCHEMA_REGISTRY.register(data.clone());
170 schemas.push(schema);
171 }
172
173 for (i, schema) in schemas.iter().enumerate() {
174 assert_eq!(schema.data.name, format!("data{i}"));
175 }
176 }
177}