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::uuid::Uuid;
41pub use spacetimedb_sats::SpacetimeType;
42pub use spacetimedb_sats::__make_register_reftype;
43pub use spacetimedb_sats::{self as sats, bsatn, buffer, de, ser};
44pub use spacetimedb_sats::{AlgebraicType, ProductType, ProductTypeElement, SumType};
45pub use spacetimedb_sats::{AlgebraicValue, ProductValue};
46
47pub const MODULE_ABI_MAJOR_VERSION: u16 = 10;
48
49#[derive(PartialEq, Eq, PartialOrd, Ord, Copy, Clone, Debug)]
51pub struct VersionTuple {
52 pub major: u16,
54 pub minor: u16,
57}
58
59impl VersionTuple {
60 pub const fn new(major: u16, minor: u16) -> Self {
61 Self { major, minor }
62 }
63
64 #[inline]
65 pub const fn eq(self, other: Self) -> bool {
66 self.major == other.major && self.minor == other.minor
67 }
68
69 #[inline]
71 pub const fn supports(self, module_version: VersionTuple) -> bool {
72 self.major == module_version.major && self.minor >= module_version.minor
73 }
74
75 #[inline]
76 pub const fn from_u32(v: u32) -> Self {
77 let major = (v >> 16) as u16;
78 let minor = (v & 0xFF) as u16;
79 Self { major, minor }
80 }
81
82 #[inline]
83 pub const fn to_u32(self) -> u32 {
84 (self.major as u32) << 16 | self.minor as u32
85 }
86}
87
88impl std::fmt::Display for VersionTuple {
89 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
90 let Self { major, minor } = *self;
91 write!(f, "{major}.{minor}")
92 }
93}
94
95extern crate self as spacetimedb_lib;
96
97#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, SpacetimeType)]
99#[sats(crate = crate)]
100pub struct TableDesc {
101 pub schema: RawTableDefV8,
102 pub data: sats::AlgebraicTypeRef,
104}
105
106impl TableDesc {
107 pub fn into_table_def(table: WithTypespace<'_, TableDesc>) -> anyhow::Result<RawTableDefV8> {
108 let schema = table
109 .map(|t| &t.data)
110 .resolve_refs()
111 .context("recursive types not yet supported")?;
112 let schema = schema.into_product().ok().context("table not a product type?")?;
113 let table = table.ty();
114 anyhow::ensure!(
115 table.schema.columns.len() == schema.elements.len(),
116 "mismatched number of columns"
117 );
118
119 Ok(table.schema.clone())
120 }
121}
122
123#[derive(Debug, Clone, SpacetimeType)]
124#[cfg_attr(feature = "test", derive(PartialEq, Eq, PartialOrd, Ord))]
125#[sats(crate = crate)]
126pub struct ReducerDef {
127 pub name: Box<str>,
128 pub args: Vec<ProductTypeElement>,
129}
130
131#[derive(Debug, Clone, Default, SpacetimeType)]
133#[cfg_attr(feature = "test", derive(PartialEq, Eq, PartialOrd, Ord))]
134#[sats(crate = crate)]
135pub struct RawModuleDefV8 {
136 pub typespace: sats::Typespace,
137 pub tables: Vec<TableDesc>,
138 pub reducers: Vec<ReducerDef>,
139 pub misc_exports: Vec<MiscModuleExport>,
140}
141
142impl RawModuleDefV8 {
143 pub fn builder() -> ModuleDefBuilder {
144 ModuleDefBuilder::default()
145 }
146
147 pub fn with_builder(f: impl FnOnce(&mut ModuleDefBuilder)) -> Self {
148 let mut builder = Self::builder();
149 f(&mut builder);
150 builder.finish()
151 }
152}
153
154#[derive(Debug, Clone, SpacetimeType)]
158#[cfg_attr(feature = "test", derive(PartialEq, Eq, PartialOrd, Ord))]
159#[sats(crate = crate)]
160#[non_exhaustive]
161pub enum RawModuleDef {
162 V8BackCompat(RawModuleDefV8),
163 V9(db::raw_def::v9::RawModuleDefV9),
164 V10(db::raw_def::v10::RawModuleDefV10),
165 }
168
169#[derive(Default)]
172pub struct ModuleDefBuilder {
173 module: RawModuleDefV8,
175 type_map: BTreeMap<TypeId, sats::AlgebraicTypeRef>,
177}
178
179impl ModuleDefBuilder {
180 pub fn add_type<T: SpacetimeType>(&mut self) -> AlgebraicType {
181 TypespaceBuilder::add_type::<T>(self)
182 }
183
184 #[cfg(feature = "test")]
187 pub fn add_type_for_tests(&mut self, name: &str, ty: AlgebraicType) -> spacetimedb_sats::AlgebraicTypeRef {
188 let slot_ref = self.module.typespace.add(ty);
189 self.module.misc_exports.push(MiscModuleExport::TypeAlias(TypeAlias {
190 name: name.to_owned(),
191 ty: slot_ref,
192 }));
193 slot_ref
194 }
195
196 #[cfg(feature = "test")]
201 pub fn add_table_for_tests(&mut self, schema: RawTableDefV8) -> spacetimedb_sats::AlgebraicTypeRef {
202 let ty: ProductType = schema
203 .columns
204 .iter()
205 .map(|c| ProductTypeElement {
206 name: Some(c.col_name.clone()),
207 algebraic_type: c.col_type.clone(),
208 })
209 .collect();
210 let data = self.module.typespace.add(ty.into());
211 self.add_type_alias(TypeAlias {
212 name: schema.table_name.clone().into(),
213 ty: data,
214 });
215 self.add_table(TableDesc { schema, data });
216 data
217 }
218
219 pub fn add_table(&mut self, table: TableDesc) {
220 self.module.tables.push(table)
221 }
222
223 pub fn add_reducer(&mut self, reducer: ReducerDef) {
224 self.module.reducers.push(reducer)
225 }
226
227 #[cfg(feature = "test")]
228 pub fn add_reducer_for_tests(&mut self, name: impl Into<Box<str>>, args: ProductType) {
229 self.add_reducer(ReducerDef {
230 name: name.into(),
231 args: args.elements.to_vec(),
232 });
233 }
234
235 pub fn add_misc_export(&mut self, misc_export: MiscModuleExport) {
236 self.module.misc_exports.push(misc_export)
237 }
238
239 pub fn add_type_alias(&mut self, type_alias: TypeAlias) {
240 self.add_misc_export(MiscModuleExport::TypeAlias(type_alias))
241 }
242
243 pub fn typespace(&self) -> &sats::Typespace {
244 &self.module.typespace
245 }
246
247 pub fn finish(self) -> RawModuleDefV8 {
248 self.module
249 }
250}
251
252impl TypespaceBuilder for ModuleDefBuilder {
253 fn add(
254 &mut self,
255 typeid: TypeId,
256 name: Option<&'static str>,
257 make_ty: impl FnOnce(&mut Self) -> AlgebraicType,
258 ) -> AlgebraicType {
259 let r = match self.type_map.entry(typeid) {
260 btree_map::Entry::Occupied(o) => *o.get(),
261 btree_map::Entry::Vacant(v) => {
262 let slot_ref = self.module.typespace.add(AlgebraicType::unit());
264 v.insert(slot_ref);
266
267 if let Some(name) = name {
269 self.module.misc_exports.push(MiscModuleExport::TypeAlias(TypeAlias {
270 name: name.to_owned(),
271 ty: slot_ref,
272 }));
273 }
274
275 let ty = make_ty(self);
277 self.module.typespace[slot_ref] = ty;
278 slot_ref
279 }
280 };
281 AlgebraicType::Ref(r)
282 }
283}
284
285#[derive(Debug, Clone, SpacetimeType)]
287#[cfg_attr(feature = "test", derive(PartialEq, Eq, PartialOrd, Ord))]
288#[sats(crate = crate)]
289pub enum MiscModuleExport {
290 TypeAlias(TypeAlias),
291}
292
293#[derive(Debug, Clone, SpacetimeType)]
294#[cfg_attr(feature = "test", derive(PartialEq, Eq, PartialOrd, Ord))]
295#[sats(crate = crate)]
296pub struct TypeAlias {
297 pub name: String,
298 pub ty: sats::AlgebraicTypeRef,
299}
300
301pub fn from_hex_pad<R: hex::FromHex<Error = hex::FromHexError>, T: AsRef<[u8]>>(
307 hex: T,
308) -> Result<R, hex::FromHexError> {
309 let hex = hex.as_ref();
310 let hex = if hex.starts_with(b"0x") {
311 &hex[2..]
312 } else if hex.starts_with(b"X'") {
313 &hex[2..hex.len()]
314 } else {
315 hex
316 };
317 hex::FromHex::from_hex(hex)
318}
319
320pub fn resolved_type_via_v9<T: SpacetimeType>() -> AlgebraicType {
326 let mut builder = RawModuleDefV9Builder::new();
327 let ty = T::make_type(&mut builder);
328 let module = builder.finish();
329
330 WithTypespace::new(&module.typespace, &ty)
331 .resolve_refs()
332 .expect("recursive types not supported")
333}