1use crate::db::raw_def::v9::RawModuleDefV9Builder;
2use crate::db::raw_def::RawTableDefV8;
3use anyhow::Context;
4use sats::typespace::TypespaceBuilder;
5use spacetimedb_sats::WithTypespace;
6use std::any::TypeId;
7use std::collections::{btree_map, BTreeMap};
8
9pub mod connection_id;
10pub mod db;
11mod direct_index_key;
12pub mod error;
13mod filterable_value;
14pub mod http;
15pub mod identity;
16pub mod metrics;
17pub mod operator;
18pub mod query;
19pub mod scheduler;
20pub mod st_var;
21pub mod version;
22
23pub mod type_def {
24 pub use spacetimedb_sats::{AlgebraicType, ProductType, ProductTypeElement, SumType};
25}
26pub mod type_value {
27 pub use spacetimedb_sats::{AlgebraicValue, ProductValue};
28}
29
30pub use connection_id::ConnectionId;
31pub use direct_index_key::{assert_column_type_valid_for_direct_index, DirectIndexKey};
32#[doc(hidden)]
33pub use filterable_value::Private;
34pub use filterable_value::{FilterableValue, IndexScanRangeBoundsTerminator, TermBound};
35pub use identity::Identity;
36pub use scheduler::ScheduleAt;
37pub use spacetimedb_sats::hash::{self, hash_bytes, Hash};
38pub use spacetimedb_sats::time_duration::TimeDuration;
39pub use spacetimedb_sats::timestamp::Timestamp;
40pub use spacetimedb_sats::SpacetimeType;
41pub use spacetimedb_sats::__make_register_reftype;
42pub use spacetimedb_sats::{self as sats, bsatn, buffer, de, ser};
43pub use spacetimedb_sats::{AlgebraicType, ProductType, ProductTypeElement, SumType};
44pub use spacetimedb_sats::{AlgebraicValue, ProductValue};
45
46pub const MODULE_ABI_MAJOR_VERSION: u16 = 10;
47
48#[derive(PartialEq, Eq, PartialOrd, Ord, Copy, Clone, Debug)]
50pub struct VersionTuple {
51 pub major: u16,
53 pub minor: u16,
56}
57
58impl VersionTuple {
59 pub const fn new(major: u16, minor: u16) -> Self {
60 Self { major, minor }
61 }
62
63 #[inline]
64 pub const fn eq(self, other: Self) -> bool {
65 self.major == other.major && self.minor == other.minor
66 }
67
68 #[inline]
70 pub const fn supports(self, module_version: VersionTuple) -> bool {
71 self.major == module_version.major && self.minor >= module_version.minor
72 }
73
74 #[inline]
75 pub const fn from_u32(v: u32) -> Self {
76 let major = (v >> 16) as u16;
77 let minor = (v & 0xFF) as u16;
78 Self { major, minor }
79 }
80
81 #[inline]
82 pub const fn to_u32(self) -> u32 {
83 (self.major as u32) << 16 | self.minor as u32
84 }
85}
86
87impl std::fmt::Display for VersionTuple {
88 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
89 let Self { major, minor } = *self;
90 write!(f, "{major}.{minor}")
91 }
92}
93
94extern crate self as spacetimedb_lib;
95
96#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, SpacetimeType)]
98#[sats(crate = crate)]
99pub struct TableDesc {
100 pub schema: RawTableDefV8,
101 pub data: sats::AlgebraicTypeRef,
103}
104
105impl TableDesc {
106 pub fn into_table_def(table: WithTypespace<'_, TableDesc>) -> anyhow::Result<RawTableDefV8> {
107 let schema = table
108 .map(|t| &t.data)
109 .resolve_refs()
110 .context("recursive types not yet supported")?;
111 let schema = schema.into_product().ok().context("table not a product type?")?;
112 let table = table.ty();
113 anyhow::ensure!(
114 table.schema.columns.len() == schema.elements.len(),
115 "mismatched number of columns"
116 );
117
118 Ok(table.schema.clone())
119 }
120}
121
122#[derive(Debug, Clone, SpacetimeType)]
123#[cfg_attr(feature = "test", derive(PartialEq, Eq, PartialOrd, Ord))]
124#[sats(crate = crate)]
125pub struct ReducerDef {
126 pub name: Box<str>,
127 pub args: Vec<ProductTypeElement>,
128}
129
130#[derive(Debug, Clone, Default, SpacetimeType)]
132#[cfg_attr(feature = "test", derive(PartialEq, Eq, PartialOrd, Ord))]
133#[sats(crate = crate)]
134pub struct RawModuleDefV8 {
135 pub typespace: sats::Typespace,
136 pub tables: Vec<TableDesc>,
137 pub reducers: Vec<ReducerDef>,
138 pub misc_exports: Vec<MiscModuleExport>,
139}
140
141impl RawModuleDefV8 {
142 pub fn builder() -> ModuleDefBuilder {
143 ModuleDefBuilder::default()
144 }
145
146 pub fn with_builder(f: impl FnOnce(&mut ModuleDefBuilder)) -> Self {
147 let mut builder = Self::builder();
148 f(&mut builder);
149 builder.finish()
150 }
151}
152
153#[derive(Debug, Clone, SpacetimeType)]
157#[cfg_attr(feature = "test", derive(PartialEq, Eq, PartialOrd, Ord))]
158#[sats(crate = crate)]
159#[non_exhaustive]
160pub enum RawModuleDef {
161 V8BackCompat(RawModuleDefV8),
162 V9(db::raw_def::v9::RawModuleDefV9),
163 }
166
167#[derive(Default)]
170pub struct ModuleDefBuilder {
171 module: RawModuleDefV8,
173 type_map: BTreeMap<TypeId, sats::AlgebraicTypeRef>,
175}
176
177impl ModuleDefBuilder {
178 pub fn add_type<T: SpacetimeType>(&mut self) -> AlgebraicType {
179 TypespaceBuilder::add_type::<T>(self)
180 }
181
182 #[cfg(feature = "test")]
185 pub fn add_type_for_tests(&mut self, name: &str, ty: AlgebraicType) -> spacetimedb_sats::AlgebraicTypeRef {
186 let slot_ref = self.module.typespace.add(ty);
187 self.module.misc_exports.push(MiscModuleExport::TypeAlias(TypeAlias {
188 name: name.to_owned(),
189 ty: slot_ref,
190 }));
191 slot_ref
192 }
193
194 #[cfg(feature = "test")]
199 pub fn add_table_for_tests(&mut self, schema: RawTableDefV8) -> spacetimedb_sats::AlgebraicTypeRef {
200 let ty: ProductType = schema
201 .columns
202 .iter()
203 .map(|c| ProductTypeElement {
204 name: Some(c.col_name.clone()),
205 algebraic_type: c.col_type.clone(),
206 })
207 .collect();
208 let data = self.module.typespace.add(ty.into());
209 self.add_type_alias(TypeAlias {
210 name: schema.table_name.clone().into(),
211 ty: data,
212 });
213 self.add_table(TableDesc { schema, data });
214 data
215 }
216
217 pub fn add_table(&mut self, table: TableDesc) {
218 self.module.tables.push(table)
219 }
220
221 pub fn add_reducer(&mut self, reducer: ReducerDef) {
222 self.module.reducers.push(reducer)
223 }
224
225 #[cfg(feature = "test")]
226 pub fn add_reducer_for_tests(&mut self, name: impl Into<Box<str>>, args: ProductType) {
227 self.add_reducer(ReducerDef {
228 name: name.into(),
229 args: args.elements.to_vec(),
230 });
231 }
232
233 pub fn add_misc_export(&mut self, misc_export: MiscModuleExport) {
234 self.module.misc_exports.push(misc_export)
235 }
236
237 pub fn add_type_alias(&mut self, type_alias: TypeAlias) {
238 self.add_misc_export(MiscModuleExport::TypeAlias(type_alias))
239 }
240
241 pub fn typespace(&self) -> &sats::Typespace {
242 &self.module.typespace
243 }
244
245 pub fn finish(self) -> RawModuleDefV8 {
246 self.module
247 }
248}
249
250impl TypespaceBuilder for ModuleDefBuilder {
251 fn add(
252 &mut self,
253 typeid: TypeId,
254 name: Option<&'static str>,
255 make_ty: impl FnOnce(&mut Self) -> AlgebraicType,
256 ) -> AlgebraicType {
257 let r = match self.type_map.entry(typeid) {
258 btree_map::Entry::Occupied(o) => *o.get(),
259 btree_map::Entry::Vacant(v) => {
260 let slot_ref = self.module.typespace.add(AlgebraicType::unit());
262 v.insert(slot_ref);
264
265 if let Some(name) = name {
267 self.module.misc_exports.push(MiscModuleExport::TypeAlias(TypeAlias {
268 name: name.to_owned(),
269 ty: slot_ref,
270 }));
271 }
272
273 let ty = make_ty(self);
275 self.module.typespace[slot_ref] = ty;
276 slot_ref
277 }
278 };
279 AlgebraicType::Ref(r)
280 }
281}
282
283#[derive(Debug, Clone, SpacetimeType)]
285#[cfg_attr(feature = "test", derive(PartialEq, Eq, PartialOrd, Ord))]
286#[sats(crate = crate)]
287pub enum MiscModuleExport {
288 TypeAlias(TypeAlias),
289}
290
291#[derive(Debug, Clone, SpacetimeType)]
292#[cfg_attr(feature = "test", derive(PartialEq, Eq, PartialOrd, Ord))]
293#[sats(crate = crate)]
294pub struct TypeAlias {
295 pub name: String,
296 pub ty: sats::AlgebraicTypeRef,
297}
298
299pub fn from_hex_pad<R: hex::FromHex<Error = hex::FromHexError>, T: AsRef<[u8]>>(
305 hex: T,
306) -> Result<R, hex::FromHexError> {
307 let hex = hex.as_ref();
308 let hex = if hex.starts_with(b"0x") {
309 &hex[2..]
310 } else if hex.starts_with(b"X'") {
311 &hex[2..hex.len()]
312 } else {
313 hex
314 };
315 hex::FromHex::from_hex(hex)
316}
317
318pub fn resolved_type_via_v9<T: SpacetimeType>() -> AlgebraicType {
324 let mut builder = RawModuleDefV9Builder::new();
325 let ty = T::make_type(&mut builder);
326 let module = builder.finish();
327
328 WithTypespace::new(&module.typespace, &ty)
329 .resolve_refs()
330 .expect("recursive types not supported")
331}