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