-
Notifications
You must be signed in to change notification settings - Fork 1k
Expand file tree
/
Copy pathutil.rs
More file actions
130 lines (114 loc) · 4.7 KB
/
util.rs
File metadata and controls
130 lines (114 loc) · 4.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
//! 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::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, PrimitiveType},
};
/// 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);
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)
}