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