spacetimedb_schema_2/
def.rs

1//! Canonicalized module definitions.
2//!
3//! This module contains a set of types that represent the canonical form of SpacetimeDB module definitions.
4//! These types are immutable to prevent accidental introduction of errors.
5//! The internal data structures of this module are not considered public API or ABI and may change
6//! at any time.
7//!
8//! Different module ABI versions correspond to different submodules of `spacetimedb_lib::db::raw_def`.
9//! All of these ABI versions can be converted to the standard form in this module via `TryFrom`.
10//! We provide streams of errors in case the conversion fails, to provide as much information
11//! to the user as possible about why their module is invalid.
12//!
13//! The `ModuleDef` type is the main type in this module. It contains all the information about a module, including its tables, reducers, typespace, and type metadata.
14//!
15//! After validation, a `ModuleDef` can be converted to the `*Schema` types in `crate::schema` for use in the database.
16//! (Eventually, we may unify these types...)
17
18use std::fmt::{self, Debug, Write};
19use std::hash::Hash;
20
21use crate::error::{IdentifierError, ValidationErrors};
22use crate::identifier::Identifier;
23use crate::schema::{Schema, TableSchema};
24use crate::type_for_generate::{AlgebraicTypeUse, ProductTypeDef, TypespaceForGenerate};
25use deserialize::ReducerArgsDeserializeSeed;
26use hashbrown::Equivalent;
27use itertools::Itertools;
28use spacetimedb_data_structures::error_stream::{CollectAllErrors, CombineErrors, ErrorStream};
29use spacetimedb_data_structures::map::HashMap;
30use spacetimedb_lib::db::raw_def;
31use spacetimedb_lib::db::raw_def::v9::{
32    Lifecycle, RawConstraintDataV9, RawConstraintDefV9, RawIdentifier, RawIndexAlgorithm, RawIndexDefV9,
33    RawModuleDefV9, RawReducerDefV9, RawRowLevelSecurityDefV9, RawScheduleDefV9, RawScopedTypeNameV9, RawSequenceDefV9,
34    RawSql, RawTableDefV9, RawTypeDefV9, RawUniqueConstraintDataV9, TableAccess, TableType,
35};
36use spacetimedb_lib::{ProductType, RawModuleDef};
37use spacetimedb_primitives::{ColId, ColList, ColSet, TableId};
38use spacetimedb_sats::AlgebraicType;
39use spacetimedb_sats::{AlgebraicTypeRef, Typespace};
40
41pub mod deserialize;
42pub mod validate;
43
44/// A map from `Identifier`s to values of type `T`.
45pub type IdentifierMap<T> = HashMap<Identifier, T>;
46
47// We may eventually want to reorganize this module to look more
48// like the system tables, with numeric IDs used for lookups
49// in addition to `Identifier`s.
50//
51// If that path is taken, it might be possible to have this type
52// entirely subsume the various `Schema` types, which would be cool.
53
54/// A validated, canonicalized, immutable module definition.
55///
56/// Cannot be created directly. Instead, create/deserialize a [spacetimedb_lib::RawModuleDef] and call [ModuleDef::try_from].
57///
58/// ```rust
59/// use spacetimedb_lib::RawModuleDef;
60/// use spacetimedb_schema::def::{ModuleDef, TableDef, IndexDef, TypeDef, ModuleDefLookup, ScopedTypeName};
61/// use spacetimedb_schema::identifier::Identifier;
62///
63/// fn read_raw_module_def_from_file() -> RawModuleDef {
64///     // ...
65/// #   RawModuleDef::V9(Default::default())
66/// }
67///
68/// let raw_module_def = read_raw_module_def_from_file();
69/// let module_def = ModuleDef::try_from(raw_module_def).expect("valid module def");
70///
71/// let table_name = Identifier::new("my_table".into()).expect("valid identifier");
72/// let index_name = Identifier::new("my_index".into()).expect("valid identifier");
73/// let scoped_type_name = ScopedTypeName::try_new([], "MyType").expect("valid scoped type name");
74///
75/// let table: Option<&TableDef> = module_def.lookup(&table_name);
76/// let index: Option<&IndexDef> = module_def.lookup(&index_name);
77/// let type_def: Option<&TypeDef> = module_def.lookup(&scoped_type_name);
78/// // etc.
79/// ```
80#[derive(Debug, Clone)]
81#[non_exhaustive]
82pub struct ModuleDef {
83    /// The tables of the module definition.
84    tables: IdentifierMap<TableDef>,
85
86    /// The reducers of the module definition.
87    reducers: IdentifierMap<ReducerDef>,
88
89    /// The type definitions of the module definition.
90    types: HashMap<ScopedTypeName, TypeDef>,
91
92    /// The typespace of the module definition.
93    typespace: Typespace,
94
95    /// The typespace, restructured to be useful for client codegen.
96    typespace_for_generate: TypespaceForGenerate,
97
98    /// The global namespace of the module:
99    /// tables, indexes, constraints, schedules, and sequences live in the global namespace.
100    /// Concretely, though, they're stored in the `TableDef` data structures.
101    /// This map allows looking up which `TableDef` stores the `Def` you're looking for.
102    stored_in_table_def: IdentifierMap<Identifier>,
103
104    /// A map from type defs to their names.
105    refmap: HashMap<AlgebraicTypeRef, ScopedTypeName>,
106
107    /// The row-level security policies.
108    ///
109    /// **Note**: Are only validated syntax-wise.
110    row_level_security_raw: HashMap<RawSql, RawRowLevelSecurityDefV9>,
111}
112
113impl ModuleDef {
114    /// The tables of the module definition.
115    pub fn tables(&self) -> impl Iterator<Item = &TableDef> {
116        self.tables.values()
117    }
118
119    /// The indexes of the module definition.
120    pub fn indexes(&self) -> impl Iterator<Item = &IndexDef> {
121        self.tables().flat_map(|table| table.indexes.values())
122    }
123
124    /// The constraints of the module definition.
125    pub fn constraints(&self) -> impl Iterator<Item = &ConstraintDef> {
126        self.tables().flat_map(|table| table.constraints.values())
127    }
128
129    /// The sequences of the module definition.
130    pub fn sequences(&self) -> impl Iterator<Item = &SequenceDef> {
131        self.tables().flat_map(|table| table.sequences.values())
132    }
133
134    /// The schedules of the module definition.
135    pub fn schedules(&self) -> impl Iterator<Item = &ScheduleDef> {
136        self.tables().filter_map(|table| table.schedule.as_ref())
137    }
138
139    /// The reducers of the module definition.
140    pub fn reducers(&self) -> impl Iterator<Item = &ReducerDef> {
141        self.reducers.values()
142    }
143
144    /// The type definitions of the module definition.
145    pub fn types(&self) -> impl Iterator<Item = &TypeDef> {
146        self.types.values()
147    }
148
149    /// The row-level security policies of the module definition.
150    pub fn row_level_security(&self) -> impl Iterator<Item = &RawRowLevelSecurityDefV9> {
151        self.row_level_security_raw.values()
152    }
153
154    /// The `Typespace` used by the module.
155    ///
156    /// `AlgebraicTypeRef`s in the table, reducer, and type alias declarations refer to this typespace.
157    ///
158    /// The typespace must satisfy `Typespace::is_valid_for_client_code_generation`. That is, all types stored in the typespace must either:
159    /// 1. satisfy `AlgebraicType::is_valid_for_client_type_definition`
160    /// 2. and/or `AlgebraicType::is_valid_for_client_type_use`.
161    ///
162    /// Types satisfying condition 1 correspond to generated classes in client code.
163    /// (Types satisfying condition 2 are an artifact of the module bindings, and do not affect the semantics of the module definition.)
164    ///
165    /// Types satisfying condition 1 are required to have corresponding `RawTypeDefV9` declarations in the module.
166    pub fn typespace(&self) -> &Typespace {
167        &self.typespace
168    }
169
170    /// The typespace of the module from a different perspective, one useful for client code generation.
171    pub fn typespace_for_generate(&self) -> &TypespaceForGenerate {
172        &self.typespace_for_generate
173    }
174
175    /// The `TableDef` an entity in the global namespace is stored in, if any.
176    ///
177    /// Generally, you will want to use the `lookup` method on the entity type instead.
178    pub fn stored_in_table_def(&self, name: &Identifier) -> Option<&TableDef> {
179        self.stored_in_table_def
180            .get(name)
181            .and_then(|table_name| self.tables.get(table_name))
182    }
183
184    /// Lookup a definition by its key in `self`.
185    pub fn lookup<T: ModuleDefLookup>(&self, key: T::Key<'_>) -> Option<&T> {
186        T::lookup(self, key)
187    }
188
189    /// Lookup a definition by its key in `self`, panicking if not found.
190    /// Only use this method if you are sure the key exists in the module definition.
191    pub fn lookup_expect<T: ModuleDefLookup>(&self, key: T::Key<'_>) -> &T {
192        T::lookup(self, key).expect("expected ModuleDef to contain key, but it does not")
193    }
194
195    /// Convenience method to look up a table, possibly by a string.
196    pub fn table<K: ?Sized + Hash + Equivalent<Identifier>>(&self, name: &K) -> Option<&TableDef> {
197        // If the string IS a valid identifier, we can just look it up.
198        self.tables.get(name)
199    }
200
201    /// Convenience method to look up a reducer, possibly by a string.
202    pub fn reducer<K: ?Sized + Hash + Equivalent<Identifier>>(&self, name: &K) -> Option<&ReducerDef> {
203        // If the string IS a valid identifier, we can just look it up.
204        self.reducers.get(name)
205    }
206
207    /// Get a `DeserializeSeed` that can pull data from a `Deserializer` and format it into a `ProductType`
208    /// at the parameter type of the reducer named `name`.
209    pub fn reducer_arg_deserialize_seed<K: ?Sized + Hash + Equivalent<Identifier>>(
210        &self,
211        name: &K,
212    ) -> Option<ReducerArgsDeserializeSeed> {
213        let reducer = self.reducer(name)?;
214        Some(ReducerArgsDeserializeSeed(self.typespace.with_type(reducer)))
215    }
216
217    /// Look up the name corresponding to an `AlgebraicTypeRef`.
218    pub fn type_def_from_ref(&self, r: AlgebraicTypeRef) -> Option<(&ScopedTypeName, &TypeDef)> {
219        let name = self.refmap.get(&r)?;
220        let def = self
221            .types
222            .get(name)
223            .expect("if it was in refmap, it should be in types");
224
225        Some((name, def))
226    }
227
228    /// Convenience method to look up a table and convert it to a `TableSchema`.
229    /// All indexes, constraints, etc inside the table will have ID 0!
230    pub fn table_schema<K: ?Sized + Hash + Equivalent<Identifier>>(
231        &self,
232        name: &K,
233        table_id: TableId,
234    ) -> Option<TableSchema> {
235        // If the string IS a valid identifier, we can just look it up.
236        let table_def = self.tables.get(name)?;
237        Some(TableSchema::from_module_def(self, table_def, (), table_id))
238    }
239
240    /// Generate indexes for the module definition.
241    /// We guarantee that all `unique` constraints have an index generated for them.
242    /// This will be removed once another enforcement mechanism is implemented.
243    /// This is a noop if there are already usable indexes present.
244    fn generate_indexes(&mut self) {
245        for table in self.tables.values_mut() {
246            for constraint in table.constraints.values() {
247                let ConstraintData::Unique(UniqueConstraintData { columns }) = &constraint.data;
248
249                // if we have a constraint for the index, we're fine.
250                if table.indexes.values().any(|index| {
251                    let IndexDef {
252                        algorithm: IndexAlgorithm::BTree(BTreeAlgorithm { columns: index_columns }),
253                        ..
254                    } = index;
255
256                    index_columns == &**columns
257                }) {
258                    continue;
259                }
260
261                // This replicates the logic from `RawIndexDefV8::for_column`.
262                let constraint_name = &constraint
263                    .name
264                    .trim_start_matches(&format!("ct_{}_", table.name))
265                    .trim_end_matches("_unique");
266                let mut index_name = Identifier::new(format!("idx_{}_{}_unique", table.name, constraint_name).into())
267                    .expect("validated identifier parts");
268
269                // incredibly janky loop to avoid name collisions.
270                // hey, somebody could be being malicious.
271                while self.stored_in_table_def.contains_key(&index_name) {
272                    index_name =
273                        Identifier::new(format!("{}_1", index_name).into()).expect("validated identifier parts");
274                }
275
276                table.indexes.insert(
277                    index_name.clone(),
278                    IndexDef {
279                        name: index_name.clone(),
280                        algorithm: IndexAlgorithm::BTree(BTreeAlgorithm {
281                            columns: columns.clone().into(),
282                        }),
283                        accessor_name: None, // this is a generated index.
284                    },
285                );
286                self.stored_in_table_def.insert(index_name, table.name.clone());
287            }
288        }
289    }
290
291    /// Lookup a definition by its key in `self`, panicking if it is not found.
292    pub fn expect_lookup<T: ModuleDefLookup>(&self, key: T::Key<'_>) -> &T {
293        if let Some(result) = T::lookup(self, key) {
294            result
295        } else {
296            panic!("expected ModuleDef to contain {:?}, but it does not", key);
297        }
298    }
299
300    /// Expect that this module definition contains a definition.
301    pub fn expect_contains<Def: ModuleDefLookup>(&self, def: &Def) {
302        if let Some(my_def) = self.lookup(def.key()) {
303            assert_eq!(
304                def as *const Def, my_def as *const Def,
305                "expected ModuleDef to contain {:?}, but it contained {:?}",
306                def, my_def
307            );
308        } else {
309            panic!("expected ModuleDef to contain {:?}, but it does not", def.key());
310        }
311    }
312}
313
314impl TryFrom<RawModuleDef> for ModuleDef {
315    type Error = ValidationErrors;
316
317    fn try_from(raw: RawModuleDef) -> Result<Self, Self::Error> {
318        match raw {
319            RawModuleDef::V8BackCompat(v8_mod) => Self::try_from(v8_mod),
320            RawModuleDef::V9(v9_mod) => Self::try_from(v9_mod),
321            _ => unimplemented!(),
322        }
323    }
324}
325impl TryFrom<raw_def::v8::RawModuleDefV8> for ModuleDef {
326    type Error = ValidationErrors;
327
328    fn try_from(v8_mod: raw_def::v8::RawModuleDefV8) -> Result<Self, Self::Error> {
329        // it is not necessary to generate indexes for a v8 mod, since the validation
330        // handles index generation.
331        validate::v8::validate(v8_mod)
332    }
333}
334impl TryFrom<raw_def::v9::RawModuleDefV9> for ModuleDef {
335    type Error = ValidationErrors;
336
337    fn try_from(v9_mod: raw_def::v9::RawModuleDefV9) -> Result<Self, Self::Error> {
338        let mut result = validate::v9::validate(v9_mod)?;
339        result.generate_indexes();
340        Ok(result)
341    }
342}
343impl From<ModuleDef> for RawModuleDefV9 {
344    fn from(val: ModuleDef) -> Self {
345        let ModuleDef {
346            tables,
347            reducers,
348            types,
349            typespace,
350            stored_in_table_def: _,
351            typespace_for_generate: _,
352            refmap: _,
353            row_level_security_raw,
354        } = val;
355
356        RawModuleDefV9 {
357            tables: to_raw(tables, |table: &RawTableDefV9| &table.name),
358            reducers: to_raw(reducers, |reducer: &RawReducerDefV9| &reducer.name),
359            types: to_raw(types, |type_: &RawTypeDefV9| &type_.name),
360            misc_exports: vec![],
361            typespace,
362            row_level_security: row_level_security_raw.into_iter().map(|(_, def)| def).collect(),
363        }
364    }
365}
366
367/// Implemented by definitions stored in a `ModuleDef`.
368/// Allows looking definitions up in a `ModuleDef`, and across
369/// `ModuleDef`s during migrations.
370pub trait ModuleDefLookup: Sized + Debug + 'static {
371    /// A reference to a definition of this type within a module def. This reference should be portable across migrations.
372    type Key<'a>: Debug + Copy;
373
374    /// Get a reference to this definition.
375    fn key(&self) -> Self::Key<'_>;
376
377    /// Look up this entity in the module def.
378    fn lookup<'a>(module_def: &'a ModuleDef, key: Self::Key<'_>) -> Option<&'a Self>;
379}
380
381/// A data structure representing the validated definition of a database table.
382///
383/// Cannot be created directly. Construct a [`ModuleDef`] by validating a [`RawModuleDef`] instead,
384/// and then access the tables from there.
385///
386/// This struct holds information about the table, including its name, columns, indexes,
387/// constraints, sequences, type, and access rights.
388///
389/// Validation rules:
390/// - The table name must be a valid identifier.
391/// - The table's columns must be sorted according to [crate::db::ordering::canonical_ordering].
392/// - The table's indexes, constraints, and sequences must be sorted by their keys.
393/// - The table's column types may refer only to types in the containing DatabaseDef's typespace.
394/// - The table's column names must be unique.
395#[derive(Debug, Clone, Eq, PartialEq)]
396#[non_exhaustive]
397pub struct TableDef {
398    /// The name of the table.
399    /// Unique within a module, acts as the table's identifier.
400    /// Must be a valid [crate::db::identifier::Identifier].
401    pub name: Identifier,
402
403    /// A reference to a `ProductType` containing the columns of this table.
404    /// This is the single source of truth for the table's columns.
405    /// All elements of the `ProductType` must have names.
406    ///
407    /// Like all types in the module, this must have the [default element ordering](crate::db::default_element_ordering), UNLESS a custom ordering is declared via `ModuleDef.misc_exports` for this type.
408    pub product_type_ref: AlgebraicTypeRef,
409
410    /// The primary key of the table, if present. Must refer to a valid column.
411    ///
412    /// Currently, there must be a unique constraint and an index corresponding to the primary key.
413    /// Eventually, we may remove the requirement for an index.
414    ///
415    /// The database engine does not actually care about this, but client code generation does.
416    pub primary_key: Option<ColId>,
417
418    /// The columns of this table. This stores the information in
419    /// `product_type_ref` in a more convenient-to-access format.
420    pub columns: Vec<ColumnDef>,
421
422    /// The indices on the table, indexed by name.
423    pub indexes: IdentifierMap<IndexDef>,
424
425    /// The unique constraints on the table, indexed by name.
426    pub constraints: IdentifierMap<ConstraintDef>,
427
428    /// The sequences for the table, indexed by name.
429    pub sequences: IdentifierMap<SequenceDef>,
430
431    /// The schedule for the table, if present.
432    pub schedule: Option<ScheduleDef>,
433
434    /// Whether this is a system- or user-created table.
435    pub table_type: TableType,
436
437    /// Whether this table is public or private.
438    pub table_access: TableAccess,
439}
440
441impl TableDef {
442    /// Get a column of the `TableDef`.
443    pub fn get_column(&self, id: ColId) -> Option<&ColumnDef> {
444        self.columns.get(id.idx())
445    }
446    /// Get a column by the column's name.
447    pub fn get_column_by_name(&self, name: &Identifier) -> Option<&ColumnDef> {
448        self.columns.iter().find(|c| &c.name == name)
449    }
450}
451
452impl From<TableDef> for RawTableDefV9 {
453    fn from(val: TableDef) -> Self {
454        let TableDef {
455            name,
456            product_type_ref,
457            primary_key,
458            columns: _, // will be reconstructed from the product type.
459            indexes,
460            constraints,
461            sequences,
462            schedule,
463            table_type,
464            table_access,
465        } = val;
466
467        RawTableDefV9 {
468            name: name.into(),
469            product_type_ref,
470            primary_key: ColList::from_iter(primary_key),
471            indexes: to_raw(indexes, |index: &RawIndexDefV9| &index.name),
472            constraints: to_raw(constraints, |constraint: &RawConstraintDefV9| &constraint.name),
473            sequences: to_raw(sequences, |sequence: &RawSequenceDefV9| &sequence.name),
474            schedule: schedule.map(Into::into),
475            table_type,
476            table_access,
477        }
478    }
479}
480
481/// A sequence definition for a database table column.
482#[derive(Debug, Clone, Eq, PartialEq)]
483pub struct SequenceDef {
484    /// The name of the sequence. Must be unique within the containing `ModuleDef`.
485    pub name: Identifier,
486
487    /// The position of the column associated with this sequence.
488    /// This refers to a column in the same `RawTableDef` that contains this `RawSequenceDef`.
489    /// The column must have integral type.
490    /// This must be the unique `RawSequenceDef` for this column.
491    pub column: ColId,
492
493    /// The value to start assigning to this column.
494    /// Will be incremented by 1 for each new row.
495    /// If not present, an arbitrary start point may be selected.
496    pub start: Option<i128>,
497
498    /// The minimum allowed value in this column.
499    /// If not present, no minimum.
500    pub min_value: Option<i128>,
501
502    /// The maximum allowed value in this column.
503    /// If not present, no maximum.
504    pub max_value: Option<i128>,
505
506    /// The increment to use when updating the sequence.
507    pub increment: i128,
508}
509
510impl From<SequenceDef> for RawSequenceDefV9 {
511    fn from(val: SequenceDef) -> Self {
512        RawSequenceDefV9 {
513            name: val.name.into(),
514            column: val.column,
515            start: val.start,
516            min_value: val.min_value,
517            max_value: val.max_value,
518            increment: val.increment,
519        }
520    }
521}
522
523/// A struct representing the validated definition of a database index.
524///
525/// Cannot be created directly. Construct a [`ModuleDef`] by validating a [`RawModuleDef`] instead,
526/// and then access the index from there.
527#[derive(Debug, Clone, Eq, PartialEq)]
528#[non_exhaustive]
529pub struct IndexDef {
530    /// The name of the index. Must be unique within the containing `ModuleDef`.
531    pub name: Identifier,
532
533    /// Accessor name for the index used in client codegen.
534    ///
535    /// This is set the user and should not be assumed to follow
536    /// any particular format.
537    ///
538    /// May be set to `None` if this is an auto-generated index for which the user
539    /// has not supplied a name. In this case, no client code generation for this index
540    /// will be performed.
541    ///
542    /// This name is not visible in the system tables, it is only used for client codegen.
543    pub accessor_name: Option<Identifier>,
544
545    /// The algorithm parameters for the index.
546    pub algorithm: IndexAlgorithm,
547}
548
549impl IndexDef {
550    /// Whether this index was generated by the system.
551    pub fn generated(&self) -> bool {
552        self.accessor_name.is_none()
553    }
554}
555
556impl From<IndexDef> for RawIndexDefV9 {
557    fn from(val: IndexDef) -> Self {
558        RawIndexDefV9 {
559            name: val.name.into(),
560            algorithm: match val.algorithm {
561                IndexAlgorithm::BTree(BTreeAlgorithm { columns }) => RawIndexAlgorithm::BTree { columns },
562            },
563            accessor_name: val.accessor_name.map(Into::into),
564        }
565    }
566}
567
568/// Data specifying a supported index algorithm.
569#[non_exhaustive]
570#[derive(Debug, Clone, Eq, PartialEq)]
571pub enum IndexAlgorithm {
572    /// Implemented using a rust `std::collections::BTreeMap`.
573    BTree(BTreeAlgorithm),
574}
575
576impl IndexAlgorithm {
577    /// Get the columns of the index.
578    pub fn columns(&self) -> &ColList {
579        match self {
580            IndexAlgorithm::BTree(btree) => &btree.columns,
581        }
582    }
583}
584
585impl From<IndexAlgorithm> for RawIndexAlgorithm {
586    fn from(val: IndexAlgorithm) -> Self {
587        match val {
588            IndexAlgorithm::BTree(BTreeAlgorithm { columns }) => RawIndexAlgorithm::BTree { columns },
589        }
590    }
591}
592
593/// Data specifying a BTree index.
594#[derive(Debug, Clone, Eq, PartialEq)]
595pub struct BTreeAlgorithm {
596    /// The columns to index.
597    pub columns: ColList,
598}
599
600impl From<BTreeAlgorithm> for IndexAlgorithm {
601    fn from(val: BTreeAlgorithm) -> Self {
602        IndexAlgorithm::BTree(val)
603    }
604}
605
606/// A struct representing the validated definition of a database column.
607///
608/// Cannot be created directly. Construct a [`ModuleDef`] by validating a [`RawModuleDef`] instead,
609/// and then access the column from there.
610#[derive(Debug, Clone, Eq, PartialEq)]
611#[non_exhaustive]
612pub struct ColumnDef {
613    /// The name of the column.
614    /// Unique within the containing `TableDef`, but
615    /// NOT within the containing `ModuleDef`.
616    pub name: Identifier,
617
618    /// The ID of this column.
619    pub col_id: ColId,
620
621    /// The type of this column. May refer to the containing `ModuleDef`'s `Typespace`.
622    /// Must satisfy `AlgebraicType::is_valid_for_client_type_use`.
623    ///
624    /// Will always correspond to the corresponding element of this table's
625    /// `product_type_ref`, that is, the element at index `col_id.idx()`
626    /// with name `Some(name.as_str())`.
627    pub ty: AlgebraicType,
628
629    /// The type of the column, formatted for client code generation.
630    pub ty_for_generate: AlgebraicTypeUse,
631
632    /// The table this `ColumnDef` is stored in.
633    pub table_name: Identifier,
634}
635
636/// A constraint definition attached to a table.
637#[derive(Debug, Clone, Eq, PartialEq)]
638pub struct ConstraintDef {
639    /// The name of the constraint. Unique within the containing `ModuleDef`.
640    pub name: Identifier,
641
642    /// The data for the constraint.
643    pub data: ConstraintData,
644}
645
646impl From<ConstraintDef> for RawConstraintDefV9 {
647    fn from(val: ConstraintDef) -> Self {
648        RawConstraintDefV9 {
649            name: val.name.into(),
650            data: val.data.into(),
651        }
652    }
653}
654
655/// Data for a constraint attached to a table.
656#[derive(Debug, Clone, Eq, PartialEq)]
657#[non_exhaustive]
658pub enum ConstraintData {
659    Unique(UniqueConstraintData),
660}
661
662impl ConstraintData {
663    /// If this is a unique constraint, returns the columns that must be unique.
664    /// Otherwise, returns `None`.
665    pub fn unique_columns(&self) -> Option<&ColSet> {
666        match &self {
667            ConstraintData::Unique(UniqueConstraintData { columns }) => Some(columns),
668        }
669    }
670}
671
672impl From<ConstraintData> for RawConstraintDataV9 {
673    fn from(val: ConstraintData) -> Self {
674        match val {
675            ConstraintData::Unique(unique) => RawConstraintDataV9::Unique(unique.into()),
676        }
677    }
678}
679
680/// Requires that the projection of the table onto these columns is an bijection.
681///
682/// That is, there must be a one-to-one relationship between a row and the `columns` of that row.
683#[derive(Debug, Clone, Eq, PartialEq)]
684pub struct UniqueConstraintData {
685    /// The columns on the containing `TableDef`
686    pub columns: ColSet,
687}
688
689impl From<UniqueConstraintData> for RawUniqueConstraintDataV9 {
690    fn from(val: UniqueConstraintData) -> Self {
691        RawUniqueConstraintDataV9 {
692            columns: val.columns.into(),
693        }
694    }
695}
696
697impl From<UniqueConstraintData> for ConstraintData {
698    fn from(val: UniqueConstraintData) -> Self {
699        ConstraintData::Unique(val)
700    }
701}
702
703/// Data for the `RLS` policy on a table.
704#[derive(Debug, Clone, Eq, PartialEq)]
705pub struct RowLevelSecurityDef {
706    /// The `sql` expression to use for row-level security.
707    pub sql: RawSql,
708}
709
710impl From<RowLevelSecurityDef> for RawRowLevelSecurityDefV9 {
711    fn from(val: RowLevelSecurityDef) -> Self {
712        RawRowLevelSecurityDefV9 { sql: val.sql }
713    }
714}
715
716/// Marks a table as a timer table for a scheduled reducer.
717#[derive(Debug, Clone, Eq, PartialEq)]
718#[non_exhaustive]
719pub struct ScheduleDef {
720    /// The name of the schedule. Must be unique within the containing `ModuleDef`.
721    pub name: Identifier,
722
723    /// The name of the column that stores the desired invocation time.
724    ///
725    /// Must be named `scheduled_at` and be of type `ScheduleAt`.
726    pub at_column: ColId,
727
728    /// The name of the column that stores the invocation ID.
729    ///
730    /// Must be named `scheduled_id` and be of type `u64`.
731    pub id_column: ColId,
732
733    /// The name of the reducer to call. Not yet an `Identifier` because
734    /// reducer names are not currently validated.
735    pub reducer_name: Identifier,
736}
737
738impl From<ScheduleDef> for RawScheduleDefV9 {
739    fn from(val: ScheduleDef) -> Self {
740        RawScheduleDefV9 {
741            name: val.name.into(),
742            reducer_name: val.reducer_name.into(),
743            scheduled_at_column: val.at_column,
744        }
745    }
746}
747
748/// A type exported by the module.
749#[derive(Debug, Clone, Eq, PartialEq)]
750#[non_exhaustive]
751pub struct TypeDef {
752    /// The (scoped) name of the type.
753    pub name: ScopedTypeName,
754
755    /// The type to which the alias refers.
756    /// Look in `ModuleDef.typespace` for the actual type,
757    /// or in `ModuleDef.typespace_for_generate` for the client codegen version.
758    pub ty: AlgebraicTypeRef,
759
760    /// Whether this type has a custom ordering.
761    pub custom_ordering: bool,
762}
763impl From<TypeDef> for RawTypeDefV9 {
764    fn from(val: TypeDef) -> Self {
765        RawTypeDefV9 {
766            name: val.name.into(),
767            ty: val.ty,
768            custom_ordering: val.custom_ordering,
769        }
770    }
771}
772
773/// A scoped type name, in the form `scope0::scope1::...::scopeN::name`.
774///
775/// These are the names that will be used *in client code generation*, NOT the names used for types
776/// in the module source code.
777#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
778pub struct ScopedTypeName {
779    /// The scope for this type.
780    ///
781    /// Empty unless a sats `name` attribute is used, e.g.
782    /// `#[sats(name = "namespace.name")]` in Rust.
783    scope: Box<[Identifier]>,
784
785    /// The name of the type.
786    ///
787    /// Eventually, we may add more information to this, such as generic arguments.
788    name: Identifier,
789}
790impl ScopedTypeName {
791    /// Create a new `ScopedTypeName` from a scope and a name.
792    pub fn new(scope: Box<[Identifier]>, name: Identifier) -> Self {
793        ScopedTypeName { scope, name }
794    }
795
796    /// Try to create a new `ScopedTypeName` from a scope and a name.
797    /// Errors if the scope or name are invalid.
798    pub fn try_new(
799        scope: impl IntoIterator<Item = RawIdentifier>,
800        name: impl Into<RawIdentifier>,
801    ) -> Result<Self, ErrorStream<IdentifierError>> {
802        let scope = scope
803            .into_iter()
804            .map(|chunk| Identifier::new(chunk).map_err(ErrorStream::from))
805            .collect_all_errors();
806        let name = Identifier::new(name.into()).map_err(ErrorStream::from);
807        let (scope, name) = (scope, name).combine_errors()?;
808        Ok(ScopedTypeName { scope, name })
809    }
810
811    /// Create a new `ScopedTypeName` with an empty scope.
812    pub fn from_name(name: Identifier) -> Self {
813        ScopedTypeName {
814            scope: Box::new([]),
815            name,
816        }
817    }
818
819    /// Retrieve the name of this type.
820    pub fn name(&self) -> &Identifier {
821        &self.name
822    }
823
824    /// Retrieve the name of this type, if the scope is empty.
825    pub fn as_identifier(&self) -> Option<&Identifier> {
826        self.scope.is_empty().then_some(&self.name)
827    }
828
829    /// Iterate over the segments of this name.
830    pub fn name_segments(&self) -> impl Iterator<Item = &Identifier> {
831        self.scope.iter().chain(std::iter::once(&self.name))
832    }
833}
834impl fmt::Debug for ScopedTypeName {
835    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
836        // we can wrap this in a pair of double quotes, since we know
837        // none of its elements contain quotes.
838        f.write_char('"')?;
839        for scope in &*self.scope {
840            write!(f, "{}::", scope)?;
841        }
842        write!(f, "{}\"", self.name)
843    }
844}
845impl fmt::Display for ScopedTypeName {
846    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
847        for scope in &*self.scope {
848            write!(f, "{}::", scope)?;
849        }
850        fmt::Display::fmt(&self.name, f)
851    }
852}
853impl TryFrom<RawScopedTypeNameV9> for ScopedTypeName {
854    type Error = ErrorStream<IdentifierError>;
855
856    fn try_from(value: RawScopedTypeNameV9) -> Result<Self, Self::Error> {
857        Self::try_new(value.scope.into_vec(), value.name)
858    }
859}
860impl From<ScopedTypeName> for RawScopedTypeNameV9 {
861    fn from(val: ScopedTypeName) -> Self {
862        RawScopedTypeNameV9 {
863            scope: val.scope.into_vec().into_iter().map_into().collect(),
864            name: val.name.into(),
865        }
866    }
867}
868
869/// A reducer exported by the module.
870#[derive(Debug, Clone, Eq, PartialEq)]
871#[non_exhaustive]
872pub struct ReducerDef {
873    /// The name of the reducer. This must be unique within the module.
874    pub name: Identifier,
875
876    /// The parameters of the reducer.
877    ///
878    /// This `ProductType` need not be registered in the module's `Typespace`.
879    pub params: ProductType,
880
881    /// The parameters of the reducer, formatted for client codegen.
882    ///
883    /// This `ProductType` need not be registered in the module's `TypespaceForGenerate`.
884    pub params_for_generate: ProductTypeDef,
885
886    /// The special role of this reducer in the module lifecycle, if any.
887    pub lifecycle: Option<Lifecycle>,
888}
889
890impl From<ReducerDef> for RawReducerDefV9 {
891    fn from(val: ReducerDef) -> Self {
892        RawReducerDefV9 {
893            name: val.name.into(),
894            params: val.params,
895            lifecycle: val.lifecycle,
896        }
897    }
898}
899
900impl ModuleDefLookup for TableDef {
901    type Key<'a> = &'a Identifier;
902
903    fn key(&self) -> Self::Key<'_> {
904        &self.name
905    }
906
907    fn lookup<'a>(module_def: &'a ModuleDef, key: Self::Key<'_>) -> Option<&'a Self> {
908        module_def.tables.get(key)
909    }
910}
911
912impl ModuleDefLookup for SequenceDef {
913    type Key<'a> = &'a Identifier;
914
915    fn key(&self) -> Self::Key<'_> {
916        &self.name
917    }
918
919    fn lookup<'a>(module_def: &'a ModuleDef, key: Self::Key<'_>) -> Option<&'a Self> {
920        module_def.stored_in_table_def(key)?.sequences.get(key)
921    }
922}
923
924impl ModuleDefLookup for IndexDef {
925    type Key<'a> = &'a Identifier;
926
927    fn key(&self) -> Self::Key<'_> {
928        &self.name
929    }
930
931    fn lookup<'a>(module_def: &'a ModuleDef, key: Self::Key<'_>) -> Option<&'a Self> {
932        module_def.stored_in_table_def(key)?.indexes.get(key)
933    }
934}
935
936impl ModuleDefLookup for ColumnDef {
937    // (table_name, column_name).
938    // We don't use `ColId` here because we want this to be portable
939    // across migrations.
940    type Key<'a> = (&'a Identifier, &'a Identifier);
941
942    fn key(&self) -> Self::Key<'_> {
943        (&self.table_name, &self.name)
944    }
945
946    fn lookup<'a>(module_def: &'a ModuleDef, (table_name, name): Self::Key<'_>) -> Option<&'a Self> {
947        module_def
948            .tables
949            .get(table_name)
950            .and_then(|table| table.get_column_by_name(name))
951    }
952}
953
954impl ModuleDefLookup for ConstraintDef {
955    type Key<'a> = &'a Identifier;
956
957    fn key(&self) -> Self::Key<'_> {
958        &self.name
959    }
960
961    fn lookup<'a>(module_def: &'a ModuleDef, key: Self::Key<'_>) -> Option<&'a Self> {
962        module_def.stored_in_table_def(key)?.constraints.get(key)
963    }
964}
965
966impl ModuleDefLookup for RawRowLevelSecurityDefV9 {
967    type Key<'a> = &'a RawSql;
968
969    fn key(&self) -> Self::Key<'_> {
970        &self.sql
971    }
972
973    fn lookup<'a>(module_def: &'a ModuleDef, key: Self::Key<'_>) -> Option<&'a Self> {
974        module_def.row_level_security_raw.get(key)
975    }
976}
977
978impl ModuleDefLookup for ScheduleDef {
979    type Key<'a> = &'a Identifier;
980
981    fn key(&self) -> Self::Key<'_> {
982        &self.name
983    }
984
985    fn lookup<'a>(module_def: &'a ModuleDef, key: Self::Key<'_>) -> Option<&'a Self> {
986        let schedule = module_def.stored_in_table_def(key)?.schedule.as_ref()?;
987        if &schedule.name == key {
988            Some(schedule)
989        } else {
990            None
991        }
992    }
993}
994
995impl ModuleDefLookup for TypeDef {
996    type Key<'a> = &'a ScopedTypeName;
997
998    fn key(&self) -> Self::Key<'_> {
999        &self.name
1000    }
1001
1002    fn lookup<'a>(module_def: &'a ModuleDef, key: Self::Key<'_>) -> Option<&'a Self> {
1003        module_def.types.get(key)
1004    }
1005}
1006
1007impl ModuleDefLookup for ReducerDef {
1008    type Key<'a> = &'a Identifier;
1009
1010    fn key(&self) -> Self::Key<'_> {
1011        &self.name
1012    }
1013
1014    fn lookup<'a>(module_def: &'a ModuleDef, key: Self::Key<'_>) -> Option<&'a Self> {
1015        module_def.reducers.get(key)
1016    }
1017}
1018
1019fn to_raw<Def, RawDef, Name, RawName>(data: HashMap<Name, Def>, f: impl Fn(&RawDef) -> &RawName) -> Vec<RawDef>
1020where
1021    Def: ModuleDefLookup + Into<RawDef>,
1022    RawName: Eq + Ord + 'static,
1023{
1024    let mut result: Vec<RawDef> = data.into_iter().map(|(_, def)| def.into()).collect();
1025    result.sort_by(|a, b| f(a).cmp(f(b)));
1026    result
1027}