mirror of
https://github.com/clockworklabs/SpacetimeDB.git
synced 2026-05-17 05:07:35 -04:00
8b0e5b1ee3
# Description of Changes Closes https://github.com/clockworklabs/SpacetimeDBPrivate/issues/1987 # API and ABI breaking changes This does not affect any APIs but it does affect the code generated # Expected complexity level and risk 1 # Testing <!-- Describe any testing you've done, and any testing you'd like your reviewers to do, so that you're confident that all the changes work as expected! --> - [ ] Snap tests
135 lines
4.8 KiB
Rust
135 lines
4.8 KiB
Rust
//! Various utility functions that the generate modules have in common.
|
|
|
|
use std::{
|
|
fmt::{Display, Formatter, Result},
|
|
ops::Deref,
|
|
};
|
|
|
|
use super::code_indenter::Indenter;
|
|
use convert_case::{Case, Casing};
|
|
use itertools::Itertools;
|
|
use spacetimedb_lib::sats::layout::PrimitiveType;
|
|
use spacetimedb_lib::version;
|
|
use spacetimedb_lib::{db::raw_def::v9::Lifecycle, sats::AlgebraicTypeRef};
|
|
use spacetimedb_primitives::ColList;
|
|
use spacetimedb_schema::schema::TableSchema;
|
|
use spacetimedb_schema::type_for_generate::ProductTypeDef;
|
|
use spacetimedb_schema::{
|
|
def::{IndexDef, TableDef, TypeDef},
|
|
type_for_generate::TypespaceForGenerate,
|
|
};
|
|
use spacetimedb_schema::{
|
|
def::{ModuleDef, ReducerDef},
|
|
identifier::Identifier,
|
|
type_for_generate::AlgebraicTypeUse,
|
|
};
|
|
|
|
/// Turns a closure `f: Fn(&mut Formatter) -> Result` into `fmt::Display`.
|
|
pub(super) fn fmt_fn(f: impl Fn(&mut Formatter) -> Result) -> impl Display {
|
|
struct FDisplay<F>(F);
|
|
impl<F: Fn(&mut Formatter) -> Result> Display for FDisplay<F> {
|
|
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
|
|
(self.0)(f)
|
|
}
|
|
}
|
|
FDisplay(f)
|
|
}
|
|
|
|
pub(super) fn collect_case<'a>(case: Case, segs: impl Iterator<Item = &'a Identifier>) -> String {
|
|
segs.map(|s| s.deref().to_case(case)).join(case.delim())
|
|
}
|
|
|
|
pub(super) fn print_lines(output: &mut Indenter, lines: &[&str]) {
|
|
for line in lines {
|
|
writeln!(output, "{line}");
|
|
}
|
|
}
|
|
|
|
pub const AUTO_GENERATED_PREFIX: &str = "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB";
|
|
|
|
const AUTO_GENERATED_FILE_COMMENT: &[&str] = &[
|
|
"// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE",
|
|
"// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.",
|
|
"",
|
|
];
|
|
|
|
pub(super) fn print_auto_generated_file_comment(output: &mut Indenter) {
|
|
print_lines(output, AUTO_GENERATED_FILE_COMMENT);
|
|
}
|
|
|
|
pub(super) fn print_auto_generated_version_comment(output: &mut Indenter) {
|
|
writeln!(
|
|
output,
|
|
"// This was generated using spacetimedb cli version {} (commit {}).",
|
|
version::spacetimedb_lib_version(),
|
|
version::GIT_HASH
|
|
);
|
|
writeln!(output);
|
|
}
|
|
|
|
pub(super) fn type_ref_name(module: &ModuleDef, typeref: AlgebraicTypeRef) -> String {
|
|
let (name, _def) = module.type_def_from_ref(typeref).unwrap();
|
|
collect_case(Case::Pascal, name.name_segments())
|
|
}
|
|
|
|
pub(super) fn is_type_filterable(typespace: &TypespaceForGenerate, ty: &AlgebraicTypeUse) -> bool {
|
|
match ty {
|
|
AlgebraicTypeUse::Primitive(prim) => !matches!(prim, PrimitiveType::F32 | PrimitiveType::F64),
|
|
AlgebraicTypeUse::String | AlgebraicTypeUse::Identity | AlgebraicTypeUse::ConnectionId => true,
|
|
// Sum types with all unit variants:
|
|
AlgebraicTypeUse::Never => true,
|
|
AlgebraicTypeUse::Option(inner) => matches!(&**inner, AlgebraicTypeUse::Unit),
|
|
AlgebraicTypeUse::Ref(r) => typespace[r].is_plain_enum(),
|
|
_ => false,
|
|
}
|
|
}
|
|
|
|
pub(super) fn is_reducer_invokable(reducer: &ReducerDef) -> bool {
|
|
reducer.lifecycle.is_none()
|
|
}
|
|
|
|
/// Iterate over all the [`ReducerDef`]s defined by the module, in alphabetical order by name.
|
|
///
|
|
/// The init reducer is skipped because it should never be visible to the clients.
|
|
/// Sorting is not necessary for reducers because they are already stored in an IndexMap.
|
|
pub(super) fn iter_reducers(module: &ModuleDef) -> impl Iterator<Item = &ReducerDef> {
|
|
module
|
|
.reducers()
|
|
.filter(|reducer| reducer.lifecycle != Some(Lifecycle::Init))
|
|
}
|
|
|
|
/// Iterate over all the [`TableDef`]s defined by the module, in alphabetical order by name.
|
|
///
|
|
/// Sorting is necessary to have deterministic reproducible codegen.
|
|
pub(super) fn iter_tables(module: &ModuleDef) -> impl Iterator<Item = &TableDef> {
|
|
module.tables().sorted_by_key(|table| &table.name)
|
|
}
|
|
|
|
pub(super) fn iter_unique_cols<'a>(
|
|
typespace: &'a TypespaceForGenerate,
|
|
schema: &'a TableSchema,
|
|
product_def: &'a ProductTypeDef,
|
|
) -> impl Iterator<Item = &'a (Identifier, AlgebraicTypeUse)> + 'a {
|
|
let constraints = schema.backcompat_column_constraints();
|
|
schema.columns().iter().filter_map(move |field| {
|
|
constraints[&ColList::from(field.col_pos)]
|
|
.has_unique()
|
|
.then(|| {
|
|
let res @ (_, ref ty) = &product_def.elements[field.col_pos.idx()];
|
|
is_type_filterable(typespace, ty).then_some(res)
|
|
})
|
|
.flatten()
|
|
})
|
|
}
|
|
|
|
pub(super) fn iter_indexes(table: &TableDef) -> impl Iterator<Item = &IndexDef> {
|
|
table.indexes.values().sorted_by_key(|index| &index.name)
|
|
}
|
|
|
|
/// Iterate over all the [`TypeDef`]s defined by the module, in alphabetical order by name.
|
|
///
|
|
/// Sorting is necessary to have deterministic reproducible codegen.
|
|
pub fn iter_types(module: &ModuleDef) -> impl Iterator<Item = &TypeDef> {
|
|
module.types().sorted_by_key(|table| &table.name)
|
|
}
|