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 }
167
168#[derive(Default)]
171pub struct ModuleDefBuilder {
172 module: RawModuleDefV8,
174 type_map: BTreeMap<TypeId, sats::AlgebraicTypeRef>,
176}
177
178impl ModuleDefBuilder {
179 pub fn add_type<T: SpacetimeType>(&mut self) -> AlgebraicType {
180 TypespaceBuilder::add_type::<T>(self)
181 }
182
183 #[cfg(feature = "test")]
186 pub fn add_type_for_tests(&mut self, name: &str, ty: AlgebraicType) -> spacetimedb_sats::AlgebraicTypeRef {
187 let slot_ref = self.module.typespace.add(ty);
188 self.module.misc_exports.push(MiscModuleExport::TypeAlias(TypeAlias {
189 name: name.to_owned(),
190 ty: slot_ref,
191 }));
192 slot_ref
193 }
194
195 #[cfg(feature = "test")]
200 pub fn add_table_for_tests(&mut self, schema: RawTableDefV8) -> spacetimedb_sats::AlgebraicTypeRef {
201 let ty: ProductType = schema
202 .columns
203 .iter()
204 .map(|c| ProductTypeElement {
205 name: Some(c.col_name.clone()),
206 algebraic_type: c.col_type.clone(),
207 })
208 .collect();
209 let data = self.module.typespace.add(ty.into());
210 self.add_type_alias(TypeAlias {
211 name: schema.table_name.clone().into(),
212 ty: data,
213 });
214 self.add_table(TableDesc { schema, data });
215 data
216 }
217
218 pub fn add_table(&mut self, table: TableDesc) {
219 self.module.tables.push(table)
220 }
221
222 pub fn add_reducer(&mut self, reducer: ReducerDef) {
223 self.module.reducers.push(reducer)
224 }
225
226 #[cfg(feature = "test")]
227 pub fn add_reducer_for_tests(&mut self, name: impl Into<Box<str>>, args: ProductType) {
228 self.add_reducer(ReducerDef {
229 name: name.into(),
230 args: args.elements.to_vec(),
231 });
232 }
233
234 pub fn add_misc_export(&mut self, misc_export: MiscModuleExport) {
235 self.module.misc_exports.push(misc_export)
236 }
237
238 pub fn add_type_alias(&mut self, type_alias: TypeAlias) {
239 self.add_misc_export(MiscModuleExport::TypeAlias(type_alias))
240 }
241
242 pub fn typespace(&self) -> &sats::Typespace {
243 &self.module.typespace
244 }
245
246 pub fn finish(self) -> RawModuleDefV8 {
247 self.module
248 }
249}
250
251impl TypespaceBuilder for ModuleDefBuilder {
252 fn add(
253 &mut self,
254 typeid: TypeId,
255 name: Option<&'static str>,
256 make_ty: impl FnOnce(&mut Self) -> AlgebraicType,
257 ) -> AlgebraicType {
258 let r = match self.type_map.entry(typeid) {
259 btree_map::Entry::Occupied(o) => *o.get(),
260 btree_map::Entry::Vacant(v) => {
261 let slot_ref = self.module.typespace.add(AlgebraicType::unit());
263 v.insert(slot_ref);
265
266 if let Some(name) = name {
268 self.module.misc_exports.push(MiscModuleExport::TypeAlias(TypeAlias {
269 name: name.to_owned(),
270 ty: slot_ref,
271 }));
272 }
273
274 let ty = make_ty(self);
276 self.module.typespace[slot_ref] = ty;
277 slot_ref
278 }
279 };
280 AlgebraicType::Ref(r)
281 }
282}
283
284#[derive(Debug, Clone, SpacetimeType)]
286#[cfg_attr(feature = "test", derive(PartialEq, Eq, PartialOrd, Ord))]
287#[sats(crate = crate)]
288pub enum MiscModuleExport {
289 TypeAlias(TypeAlias),
290}
291
292#[derive(Debug, Clone, SpacetimeType)]
293#[cfg_attr(feature = "test", derive(PartialEq, Eq, PartialOrd, Ord))]
294#[sats(crate = crate)]
295pub struct TypeAlias {
296 pub name: String,
297 pub ty: sats::AlgebraicTypeRef,
298}
299
300pub fn from_hex_pad<R: hex::FromHex<Error = hex::FromHexError>, T: AsRef<[u8]>>(
306 hex: T,
307) -> Result<R, hex::FromHexError> {
308 let hex = hex.as_ref();
309 let hex = if hex.starts_with(b"0x") {
310 &hex[2..]
311 } else if hex.starts_with(b"X'") {
312 &hex[2..hex.len()]
313 } else {
314 hex
315 };
316 hex::FromHex::from_hex(hex)
317}
318
319pub fn resolved_type_via_v9<T: SpacetimeType>() -> AlgebraicType {
325 let mut builder = RawModuleDefV9Builder::new();
326 let ty = T::make_type(&mut builder);
327 let module = builder.finish();
328
329 WithTypespace::new(&module.typespace, &ty)
330 .resolve_refs()
331 .expect("recursive types not supported")
332}