1use super::code_indenter::{CodeIndenter, Indenter};
2use super::util::{collect_case, iter_reducers, print_lines, type_ref_name};
3use super::Lang;
4use crate::util::{iter_tables, iter_types, iter_unique_cols, print_auto_generated_file_comment};
5use convert_case::{Case, Casing};
6use spacetimedb_lib::sats::layout::PrimitiveType;
7use spacetimedb_lib::sats::AlgebraicTypeRef;
8use spacetimedb_schema::def::{ModuleDef, ReducerDef, ScopedTypeName, TableDef, TypeDef};
9use spacetimedb_schema::identifier::Identifier;
10use spacetimedb_schema::schema::{Schema, TableSchema};
11use spacetimedb_schema::type_for_generate::{AlgebraicTypeDef, AlgebraicTypeUse};
12use std::collections::BTreeSet;
13use std::fmt::{self, Write};
14use std::ops::Deref;
15
16type Imports = BTreeSet<AlgebraicTypeRef>;
18
19const INDENT: &str = " ";
20
21pub struct Rust;
22
23impl Lang for Rust {
24 fn table_filename(
25 &self,
26 _module: &spacetimedb_schema::def::ModuleDef,
27 table: &spacetimedb_schema::def::TableDef,
28 ) -> String {
29 table_module_name(&table.name) + ".rs"
30 }
31
32 fn type_filename(&self, type_name: &ScopedTypeName) -> String {
33 type_module_name(type_name) + ".rs"
34 }
35
36 fn reducer_filename(&self, reducer_name: &Identifier) -> String {
37 reducer_module_name(reducer_name) + ".rs"
38 }
39
40 fn generate_type(&self, module: &ModuleDef, typ: &TypeDef) -> String {
41 let type_name = collect_case(Case::Pascal, typ.name.name_segments());
42
43 let mut output = CodeIndenter::new(String::new(), INDENT);
44 let out = &mut output;
45
46 print_file_header(out);
47 out.newline();
48
49 match &module.typespace_for_generate()[typ.ty] {
50 AlgebraicTypeDef::Product(product) => {
51 gen_and_print_imports(module, out, &product.elements, &[typ.ty]);
52 out.newline();
53 define_struct_for_product(module, out, &type_name, &product.elements, "pub");
54 }
55 AlgebraicTypeDef::Sum(sum) => {
56 gen_and_print_imports(module, out, &sum.variants, &[typ.ty]);
57 out.newline();
58 define_enum_for_sum(module, out, &type_name, &sum.variants, false);
59 }
60 AlgebraicTypeDef::PlainEnum(plain_enum) => {
61 let variants = plain_enum
62 .variants
63 .iter()
64 .cloned()
65 .map(|var| (var, AlgebraicTypeUse::Unit))
66 .collect::<Vec<_>>();
67 define_enum_for_sum(module, out, &type_name, &variants, true);
68 }
69 }
70 out.newline();
71
72 writeln!(
73 out,
74 "
75impl __sdk::InModule for {type_name} {{
76 type Module = super::RemoteModule;
77}}
78",
79 );
80
81 output.into_inner()
82 }
83 fn generate_table(&self, module: &ModuleDef, table: &TableDef) -> String {
84 let schema = TableSchema::from_module_def(module, table, (), 0.into())
85 .validated()
86 .expect("Failed to generate table due to validation errors");
87
88 let type_ref = table.product_type_ref;
89
90 let mut output = CodeIndenter::new(String::new(), INDENT);
91 let out = &mut output;
92
93 print_file_header(out);
94
95 let row_type = type_ref_name(module, type_ref);
96 let row_type_module = type_ref_module_name(module, type_ref);
97
98 writeln!(out, "use super::{row_type_module}::{row_type};");
99
100 let product_def = module.typespace_for_generate()[type_ref].as_product().unwrap();
101
102 gen_and_print_imports(
106 module,
107 out,
108 &product_def.elements,
109 &[], );
111
112 let table_name = table.name.deref();
113 let table_name_pascalcase = table.name.deref().to_case(Case::Pascal);
114 let table_handle = table_name_pascalcase.clone() + "TableHandle";
115 let insert_callback_id = table_name_pascalcase.clone() + "InsertCallbackId";
116 let delete_callback_id = table_name_pascalcase.clone() + "DeleteCallbackId";
117 let accessor_trait = table_access_trait_name(&table.name);
118 let accessor_method = table_method_name(&table.name);
119
120 write!(
121 out,
122 "
123/// Table handle for the table `{table_name}`.
124///
125/// Obtain a handle from the [`{accessor_trait}::{accessor_method}`] method on [`super::RemoteTables`],
126/// like `ctx.db.{accessor_method}()`.
127///
128/// Users are encouraged not to explicitly reference this type,
129/// but to directly chain method calls,
130/// like `ctx.db.{accessor_method}().on_insert(...)`.
131pub struct {table_handle}<'ctx> {{
132 imp: __sdk::TableHandle<{row_type}>,
133 ctx: std::marker::PhantomData<&'ctx super::RemoteTables>,
134}}
135
136#[allow(non_camel_case_types)]
137/// Extension trait for access to the table `{table_name}`.
138///
139/// Implemented for [`super::RemoteTables`].
140pub trait {accessor_trait} {{
141 #[allow(non_snake_case)]
142 /// Obtain a [`{table_handle}`], which mediates access to the table `{table_name}`.
143 fn {accessor_method}(&self) -> {table_handle}<'_>;
144}}
145
146impl {accessor_trait} for super::RemoteTables {{
147 fn {accessor_method}(&self) -> {table_handle}<'_> {{
148 {table_handle} {{
149 imp: self.imp.get_table::<{row_type}>({table_name:?}),
150 ctx: std::marker::PhantomData,
151 }}
152 }}
153}}
154
155pub struct {insert_callback_id}(__sdk::CallbackId);
156pub struct {delete_callback_id}(__sdk::CallbackId);
157
158impl<'ctx> __sdk::Table for {table_handle}<'ctx> {{
159 type Row = {row_type};
160 type EventContext = super::EventContext;
161
162 fn count(&self) -> u64 {{ self.imp.count() }}
163 fn iter(&self) -> impl Iterator<Item = {row_type}> + '_ {{ self.imp.iter() }}
164
165 type InsertCallbackId = {insert_callback_id};
166
167 fn on_insert(
168 &self,
169 callback: impl FnMut(&Self::EventContext, &Self::Row) + Send + 'static,
170 ) -> {insert_callback_id} {{
171 {insert_callback_id}(self.imp.on_insert(Box::new(callback)))
172 }}
173
174 fn remove_on_insert(&self, callback: {insert_callback_id}) {{
175 self.imp.remove_on_insert(callback.0)
176 }}
177
178 type DeleteCallbackId = {delete_callback_id};
179
180 fn on_delete(
181 &self,
182 callback: impl FnMut(&Self::EventContext, &Self::Row) + Send + 'static,
183 ) -> {delete_callback_id} {{
184 {delete_callback_id}(self.imp.on_delete(Box::new(callback)))
185 }}
186
187 fn remove_on_delete(&self, callback: {delete_callback_id}) {{
188 self.imp.remove_on_delete(callback.0)
189 }}
190}}
191"
192 );
193
194 out.delimited_block(
195 "
196#[doc(hidden)]
197pub(super) fn register_table(client_cache: &mut __sdk::ClientCache<super::RemoteModule>) {
198",
199 |out| {
200 writeln!(out, "let _table = client_cache.get_or_make_table::<{row_type}>({table_name:?});");
201 for (unique_field_ident, unique_field_type_use) in iter_unique_cols(module.typespace_for_generate(), &schema, product_def) {
202 let unique_field_name = unique_field_ident.deref().to_case(Case::Snake);
203 let unique_field_type = type_name(module, unique_field_type_use);
204 writeln!(
205 out,
206 "_table.add_unique_constraint::<{unique_field_type}>({unique_field_name:?}, |row| &row.{unique_field_name});",
207 );
208 }
209 },
210 "}",
211 );
212
213 if schema.pk().is_some() {
214 let update_callback_id = table_name_pascalcase.clone() + "UpdateCallbackId";
215 write!(
216 out,
217 "
218pub struct {update_callback_id}(__sdk::CallbackId);
219
220impl<'ctx> __sdk::TableWithPrimaryKey for {table_handle}<'ctx> {{
221 type UpdateCallbackId = {update_callback_id};
222
223 fn on_update(
224 &self,
225 callback: impl FnMut(&Self::EventContext, &Self::Row, &Self::Row) + Send + 'static,
226 ) -> {update_callback_id} {{
227 {update_callback_id}(self.imp.on_update(Box::new(callback)))
228 }}
229
230 fn remove_on_update(&self, callback: {update_callback_id}) {{
231 self.imp.remove_on_update(callback.0)
232 }}
233}}
234"
235 );
236 }
237
238 out.newline();
239
240 write!(
241 out,
242 "
243#[doc(hidden)]
244pub(super) fn parse_table_update(
245 raw_updates: __ws::TableUpdate<__ws::BsatnFormat>,
246) -> __sdk::Result<__sdk::TableUpdate<{row_type}>> {{
247 __sdk::TableUpdate::parse_table_update(raw_updates).map_err(|e| {{
248 __sdk::InternalError::failed_parse(
249 \"TableUpdate<{row_type}>\",
250 \"TableUpdate\",
251 ).with_cause(e).into()
252 }})
253}}
254"
255 );
256
257 for (unique_field_ident, unique_field_type_use) in
258 iter_unique_cols(module.typespace_for_generate(), &schema, product_def)
259 {
260 let unique_field_name = unique_field_ident.deref().to_case(Case::Snake);
261 let unique_field_name_pascalcase = unique_field_name.to_case(Case::Pascal);
262
263 let unique_constraint = table_name_pascalcase.clone() + &unique_field_name_pascalcase + "Unique";
264 let unique_field_type = type_name(module, unique_field_type_use);
265
266 write!(
267 out,
268 "
269 /// Access to the `{unique_field_name}` unique index on the table `{table_name}`,
270 /// which allows point queries on the field of the same name
271 /// via the [`{unique_constraint}::find`] method.
272 ///
273 /// Users are encouraged not to explicitly reference this type,
274 /// but to directly chain method calls,
275 /// like `ctx.db.{accessor_method}().{unique_field_name}().find(...)`.
276 pub struct {unique_constraint}<'ctx> {{
277 imp: __sdk::UniqueConstraintHandle<{row_type}, {unique_field_type}>,
278 phantom: std::marker::PhantomData<&'ctx super::RemoteTables>,
279 }}
280
281 impl<'ctx> {table_handle}<'ctx> {{
282 /// Get a handle on the `{unique_field_name}` unique index on the table `{table_name}`.
283 pub fn {unique_field_name}(&self) -> {unique_constraint}<'ctx> {{
284 {unique_constraint} {{
285 imp: self.imp.get_unique_constraint::<{unique_field_type}>({unique_field_name:?}),
286 phantom: std::marker::PhantomData,
287 }}
288 }}
289 }}
290
291 impl<'ctx> {unique_constraint}<'ctx> {{
292 /// Find the subscribed row whose `{unique_field_name}` column value is equal to `col_val`,
293 /// if such a row is present in the client cache.
294 pub fn find(&self, col_val: &{unique_field_type}) -> Option<{row_type}> {{
295 self.imp.find(col_val)
296 }}
297 }}
298 "
299 );
300 }
301
302 output.into_inner()
305 }
306 fn generate_reducer(&self, module: &ModuleDef, reducer: &ReducerDef) -> String {
307 let mut output = CodeIndenter::new(String::new(), INDENT);
308 let out = &mut output;
309
310 print_file_header(out);
311
312 out.newline();
313
314 gen_and_print_imports(
315 module,
316 out,
317 &reducer.params_for_generate.elements,
318 &[],
320 );
321
322 out.newline();
323
324 let reducer_name = reducer.name.deref();
325 let func_name = reducer_function_name(reducer);
326 let set_reducer_flags_trait = reducer_flags_trait_name(reducer);
327 let args_type = reducer_args_type_name(&reducer.name);
328 let enum_variant_name = reducer_variant_name(&reducer.name);
329
330 define_struct_for_product(
341 module,
342 out,
343 &args_type,
344 &reducer.params_for_generate.elements,
345 "pub(super)",
346 );
347
348 out.newline();
349
350 let callback_id = reducer_callback_id_name(&reducer.name);
351
352 let mut arglist = String::new();
355 write_arglist_no_delimiters(module, &mut arglist, &reducer.params_for_generate.elements, None).unwrap();
356
357 let mut arg_types_ref_list = String::new();
360 let mut arg_names_list = String::new();
363 for (arg_ident, arg_ty) in &reducer.params_for_generate.elements[..] {
364 arg_types_ref_list += "&";
365 write_type(module, &mut arg_types_ref_list, arg_ty).unwrap();
366 arg_types_ref_list += ", ";
367
368 let arg_name = arg_ident.deref().to_case(Case::Snake);
369 arg_names_list += &arg_name;
370 arg_names_list += ", ";
371 }
372
373 write!(out, "impl From<{args_type}> for super::Reducer ");
374 out.delimited_block(
375 "{",
376 |out| {
377 write!(out, "fn from(args: {args_type}) -> Self ");
378 out.delimited_block(
379 "{",
380 |out| {
381 write!(out, "Self::{enum_variant_name}");
382 if !reducer.params_for_generate.elements.is_empty() {
383 out.delimited_block(
387 " {",
388 |out| {
389 for (arg_ident, _ty) in &reducer.params_for_generate.elements[..] {
390 let arg_name = arg_ident.deref().to_case(Case::Snake);
391 writeln!(out, "{arg_name}: args.{arg_name},");
392 }
393 },
394 "}",
395 );
396 }
397 out.newline();
398 },
399 "}\n",
400 );
401 },
402 "}\n",
403 );
404
405 writeln!(
408 out,
409 "
410impl __sdk::InModule for {args_type} {{
411 type Module = super::RemoteModule;
412}}
413
414pub struct {callback_id}(__sdk::CallbackId);
415
416#[allow(non_camel_case_types)]
417/// Extension trait for access to the reducer `{reducer_name}`.
418///
419/// Implemented for [`super::RemoteReducers`].
420pub trait {func_name} {{
421 /// Request that the remote module invoke the reducer `{reducer_name}` to run as soon as possible.
422 ///
423 /// This method returns immediately, and errors only if we are unable to send the request.
424 /// The reducer will run asynchronously in the future,
425 /// and its status can be observed by listening for [`Self::on_{func_name}`] callbacks.
426 fn {func_name}(&self, {arglist}) -> __sdk::Result<()>;
427 /// Register a callback to run whenever we are notified of an invocation of the reducer `{reducer_name}`.
428 ///
429 /// Callbacks should inspect the [`__sdk::ReducerEvent`] contained in the [`super::ReducerEventContext`]
430 /// to determine the reducer's status.
431 ///
432 /// The returned [`{callback_id}`] can be passed to [`Self::remove_on_{func_name}`]
433 /// to cancel the callback.
434 fn on_{func_name}(&self, callback: impl FnMut(&super::ReducerEventContext, {arg_types_ref_list}) + Send + 'static) -> {callback_id};
435 /// Cancel a callback previously registered by [`Self::on_{func_name}`],
436 /// causing it not to run in the future.
437 fn remove_on_{func_name}(&self, callback: {callback_id});
438}}
439
440impl {func_name} for super::RemoteReducers {{
441 fn {func_name}(&self, {arglist}) -> __sdk::Result<()> {{
442 self.imp.call_reducer({reducer_name:?}, {args_type} {{ {arg_names_list} }})
443 }}
444 fn on_{func_name}(
445 &self,
446 mut callback: impl FnMut(&super::ReducerEventContext, {arg_types_ref_list}) + Send + 'static,
447 ) -> {callback_id} {{
448 {callback_id}(self.imp.on_reducer(
449 {reducer_name:?},
450 Box::new(move |ctx: &super::ReducerEventContext| {{
451 let super::ReducerEventContext {{
452 event: __sdk::ReducerEvent {{
453 reducer: super::Reducer::{enum_variant_name} {{
454 {arg_names_list}
455 }},
456 ..
457 }},
458 ..
459 }} = ctx else {{ unreachable!() }};
460 callback(ctx, {arg_names_list})
461 }}),
462 ))
463 }}
464 fn remove_on_{func_name}(&self, callback: {callback_id}) {{
465 self.imp.remove_on_reducer({reducer_name:?}, callback.0)
466 }}
467}}
468
469#[allow(non_camel_case_types)]
470#[doc(hidden)]
471/// Extension trait for setting the call-flags for the reducer `{reducer_name}`.
472///
473/// Implemented for [`super::SetReducerFlags`].
474///
475/// This type is currently unstable and may be removed without a major version bump.
476pub trait {set_reducer_flags_trait} {{
477 /// Set the call-reducer flags for the reducer `{reducer_name}` to `flags`.
478 ///
479 /// This type is currently unstable and may be removed without a major version bump.
480 fn {func_name}(&self, flags: __ws::CallReducerFlags);
481}}
482
483impl {set_reducer_flags_trait} for super::SetReducerFlags {{
484 fn {func_name}(&self, flags: __ws::CallReducerFlags) {{
485 self.imp.set_call_reducer_flags({reducer_name:?}, flags);
486 }}
487}}
488"
489 );
490
491 output.into_inner()
492 }
493
494 fn generate_globals(&self, module: &ModuleDef) -> Vec<(String, String)> {
495 let mut output = CodeIndenter::new(String::new(), INDENT);
496 let out = &mut output;
497
498 print_file_header(out);
499
500 out.newline();
501
502 print_module_decls(module, out);
504
505 out.newline();
506
507 print_module_reexports(module, out);
509
510 out.newline();
511
512 print_reducer_enum_defn(module, out);
514
515 out.newline();
516
517 print_db_update_defn(module, out);
519
520 out.newline();
521
522 print_applied_diff_defn(module, out);
524
525 out.newline();
526
527 print_const_db_context_types(out);
530
531 out.newline();
532
533 print_impl_spacetime_module(module, out);
536
537 vec![("mod.rs".to_string(), (output.into_inner()))]
538 }
539}
540
541pub fn write_type<W: Write>(module: &ModuleDef, out: &mut W, ty: &AlgebraicTypeUse) -> fmt::Result {
542 match ty {
543 AlgebraicTypeUse::Unit => write!(out, "()")?,
544 AlgebraicTypeUse::Never => write!(out, "std::convert::Infallible")?,
545 AlgebraicTypeUse::Identity => write!(out, "__sdk::Identity")?,
546 AlgebraicTypeUse::ConnectionId => write!(out, "__sdk::ConnectionId")?,
547 AlgebraicTypeUse::Timestamp => write!(out, "__sdk::Timestamp")?,
548 AlgebraicTypeUse::TimeDuration => write!(out, "__sdk::TimeDuration")?,
549 AlgebraicTypeUse::ScheduleAt => write!(out, "__sdk::ScheduleAt")?,
550 AlgebraicTypeUse::Option(inner_ty) => {
551 write!(out, "Option::<")?;
552 write_type(module, out, inner_ty)?;
553 write!(out, ">")?;
554 }
555 AlgebraicTypeUse::Primitive(prim) => match prim {
556 PrimitiveType::Bool => write!(out, "bool")?,
557 PrimitiveType::I8 => write!(out, "i8")?,
558 PrimitiveType::U8 => write!(out, "u8")?,
559 PrimitiveType::I16 => write!(out, "i16")?,
560 PrimitiveType::U16 => write!(out, "u16")?,
561 PrimitiveType::I32 => write!(out, "i32")?,
562 PrimitiveType::U32 => write!(out, "u32")?,
563 PrimitiveType::I64 => write!(out, "i64")?,
564 PrimitiveType::U64 => write!(out, "u64")?,
565 PrimitiveType::I128 => write!(out, "i128")?,
566 PrimitiveType::U128 => write!(out, "u128")?,
567 PrimitiveType::I256 => write!(out, "__sats::i256")?,
568 PrimitiveType::U256 => write!(out, "__sats::u256")?,
569 PrimitiveType::F32 => write!(out, "f32")?,
570 PrimitiveType::F64 => write!(out, "f64")?,
571 },
572 AlgebraicTypeUse::String => write!(out, "String")?,
573 AlgebraicTypeUse::Array(elem_ty) => {
574 write!(out, "Vec::<")?;
575 write_type(module, out, elem_ty)?;
576 write!(out, ">")?;
577 }
578 AlgebraicTypeUse::Ref(r) => {
579 write!(out, "{}", type_ref_name(module, *r))?;
580 }
581 }
582 Ok(())
583}
584
585pub fn type_name(module: &ModuleDef, ty: &AlgebraicTypeUse) -> String {
586 let mut s = String::new();
587 write_type(module, &mut s, ty).unwrap();
588 s
589}
590
591const ALLOW_LINTS: &str = "#![allow(unused, clippy::all)]";
592
593const SPACETIMEDB_IMPORTS: &[&str] = &[
594 "use spacetimedb_sdk::__codegen::{",
595 "\tself as __sdk,",
596 "\t__lib,",
597 "\t__sats,",
598 "\t__ws,",
599 "};",
600];
601
602fn print_spacetimedb_imports(output: &mut Indenter) {
603 print_lines(output, SPACETIMEDB_IMPORTS);
604}
605
606fn print_file_header(output: &mut Indenter) {
607 print_auto_generated_file_comment(output);
608 writeln!(output, "{ALLOW_LINTS}");
609 print_spacetimedb_imports(output);
610}
611
612const ENUM_DERIVES: &[&str] = &[
622 "#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]",
623 "#[sats(crate = __lib)]",
624];
625
626fn print_enum_derives(output: &mut Indenter) {
627 print_lines(output, ENUM_DERIVES);
628}
629
630const PLAIN_ENUM_EXTRA_DERIVES: &[&str] = &["#[derive(Copy, Eq, Hash)]"];
631
632fn print_plain_enum_extra_derives(output: &mut Indenter) {
633 print_lines(output, PLAIN_ENUM_EXTRA_DERIVES);
634}
635
636pub fn define_enum_for_sum(
638 module: &ModuleDef,
639 out: &mut Indenter,
640 name: &str,
641 variants: &[(Identifier, AlgebraicTypeUse)],
642 is_plain: bool,
643) {
644 print_enum_derives(out);
645 if is_plain {
646 print_plain_enum_extra_derives(out);
647 }
648 write!(out, "pub enum {name} ");
649
650 out.delimited_block(
651 "{",
652 |out| {
653 for (ident, ty) in variants {
654 write_enum_variant(module, out, ident, ty);
655 out.newline();
656 }
657 },
658 "}\n",
659 );
660
661 out.newline()
662}
663
664fn write_enum_variant(module: &ModuleDef, out: &mut Indenter, ident: &Identifier, ty: &AlgebraicTypeUse) {
665 let name = ident.deref().to_case(Case::Pascal);
666 write!(out, "{name}");
667
668 if !matches!(ty, AlgebraicTypeUse::Unit) {
674 write!(out, "(");
677 write_type(module, out, ty).unwrap();
678 write!(out, ")");
679 }
680 writeln!(out, ",");
681}
682
683fn write_struct_type_fields_in_braces(
684 module: &ModuleDef,
685 out: &mut Indenter,
686 elements: &[(Identifier, AlgebraicTypeUse)],
687
688 pub_qualifier: bool,
691) {
692 out.delimited_block(
693 "{",
694 |out| write_arglist_no_delimiters(module, out, elements, pub_qualifier.then_some("pub")).unwrap(),
695 "}",
696 );
697}
698
699fn write_arglist_no_delimiters(
700 module: &ModuleDef,
701 out: &mut impl Write,
702 elements: &[(Identifier, AlgebraicTypeUse)],
703
704 prefix: Option<&str>,
706) -> anyhow::Result<()> {
707 for (ident, ty) in elements {
708 if let Some(prefix) = prefix {
709 write!(out, "{prefix} ")?;
710 }
711
712 let name = ident.deref().to_case(Case::Snake);
713
714 write!(out, "{name}: ")?;
715 write_type(module, out, ty)?;
716 writeln!(out, ",")?;
717 }
718
719 Ok(())
720}
721
722const STRUCT_DERIVES: &[&str] = &[
732 "#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]",
733 "#[sats(crate = __lib)]",
734];
735
736fn print_struct_derives(output: &mut Indenter) {
737 print_lines(output, STRUCT_DERIVES);
738}
739
740fn define_struct_for_product(
741 module: &ModuleDef,
742 out: &mut Indenter,
743 name: &str,
744 elements: &[(Identifier, AlgebraicTypeUse)],
745 vis: &str,
746) {
747 print_struct_derives(out);
748
749 write!(out, "{vis} struct {name} ");
750
751 write_struct_type_fields_in_braces(
753 module, out, elements, true, );
755
756 out.newline();
757}
758
759fn type_ref_module_name(module: &ModuleDef, type_ref: AlgebraicTypeRef) -> String {
760 let (name, _) = module.type_def_from_ref(type_ref).unwrap();
761 type_module_name(name)
762}
763
764fn type_module_name(type_name: &ScopedTypeName) -> String {
765 collect_case(Case::Snake, type_name.name_segments()) + "_type"
766}
767
768fn table_module_name(table_name: &Identifier) -> String {
769 table_name.deref().to_case(Case::Snake) + "_table"
770}
771
772fn table_method_name(table_name: &Identifier) -> String {
773 table_name.deref().to_case(Case::Snake)
774}
775
776fn table_access_trait_name(table_name: &Identifier) -> String {
777 table_name.deref().to_case(Case::Pascal) + "TableAccess"
778}
779
780fn reducer_args_type_name(reducer_name: &Identifier) -> String {
781 reducer_name.deref().to_case(Case::Pascal) + "Args"
782}
783
784fn reducer_variant_name(reducer_name: &Identifier) -> String {
785 reducer_name.deref().to_case(Case::Pascal)
786}
787
788fn reducer_callback_id_name(reducer_name: &Identifier) -> String {
789 reducer_name.deref().to_case(Case::Pascal) + "CallbackId"
790}
791
792fn reducer_module_name(reducer_name: &Identifier) -> String {
793 reducer_name.deref().to_case(Case::Snake) + "_reducer"
794}
795
796fn reducer_function_name(reducer: &ReducerDef) -> String {
797 reducer.name.deref().to_case(Case::Snake)
798}
799
800fn reducer_flags_trait_name(reducer: &ReducerDef) -> String {
801 format!("set_flags_for_{}", reducer_function_name(reducer))
802}
803
804fn iter_module_names(module: &ModuleDef) -> impl Iterator<Item = String> + '_ {
806 itertools::chain!(
807 iter_types(module).map(|ty| type_module_name(&ty.name)),
808 iter_reducers(module).map(|r| reducer_module_name(&r.name)),
809 iter_tables(module).map(|tbl| table_module_name(&tbl.name)),
810 )
811}
812
813fn print_module_decls(module: &ModuleDef, out: &mut Indenter) {
815 for module_name in iter_module_names(module) {
816 writeln!(out, "pub mod {module_name};");
817 }
818}
819
820fn print_module_reexports(module: &ModuleDef, out: &mut Indenter) {
822 for ty in iter_types(module) {
823 let mod_name = type_module_name(&ty.name);
824 let type_name = collect_case(Case::Pascal, ty.name.name_segments());
825 writeln!(out, "pub use {mod_name}::{type_name};")
826 }
827 for table in iter_tables(module) {
828 let mod_name = table_module_name(&table.name);
829 writeln!(out, "pub use {mod_name}::*;");
835 }
836 for reducer in iter_reducers(module) {
837 let mod_name = reducer_module_name(&reducer.name);
838 let reducer_trait_name = reducer_function_name(reducer);
839 let flags_trait_name = reducer_flags_trait_name(reducer);
840 let callback_id_name = reducer_callback_id_name(&reducer.name);
841 writeln!(
842 out,
843 "pub use {mod_name}::{{{reducer_trait_name}, {flags_trait_name}, {callback_id_name}}};"
844 );
845 }
846}
847
848fn print_reducer_enum_defn(module: &ModuleDef, out: &mut Indenter) {
849 writeln!(out, "#[derive(Clone, PartialEq, Debug)]");
852 writeln!(
853 out,
854 "
855/// One of the reducers defined by this module.
856///
857/// Contained within a [`__sdk::ReducerEvent`] in [`EventContext`]s for reducer events
858/// to indicate which reducer caused the event.
859",
860 );
861 out.delimited_block(
862 "pub enum Reducer {",
863 |out| {
864 for reducer in iter_reducers(module) {
865 write!(out, "{} ", reducer_variant_name(&reducer.name));
866 if !reducer.params_for_generate.elements.is_empty() {
867 write_struct_type_fields_in_braces(module, out, &reducer.params_for_generate.elements, false);
872 }
873 writeln!(out, ",");
874 }
875 },
876 "}\n",
877 );
878 out.newline();
879 writeln!(
880 out,
881 "
882impl __sdk::InModule for Reducer {{
883 type Module = RemoteModule;
884}}
885",
886 );
887
888 out.delimited_block(
889 "impl __sdk::Reducer for Reducer {",
890 |out| {
891 out.delimited_block(
892 "fn reducer_name(&self) -> &'static str {",
893 |out| {
894 out.delimited_block(
895 "match self {",
896 |out| {
897 for reducer in iter_reducers(module) {
898 write!(out, "Reducer::{}", reducer_variant_name(&reducer.name));
899 if !reducer.params_for_generate.elements.is_empty() {
900 write!(out, " {{ .. }}");
907 }
908 writeln!(out, " => {:?},", reducer.name.deref());
909 }
910 },
911 "}\n",
912 );
913 },
914 "}\n",
915 );
916 },
917 "}\n",
918 );
919
920 out.delimited_block(
921 "impl TryFrom<__ws::ReducerCallInfo<__ws::BsatnFormat>> for Reducer {",
922 |out| {
923 writeln!(out, "type Error = __sdk::Error;");
924 out.delimited_block(
940 "fn try_from(value: __ws::ReducerCallInfo<__ws::BsatnFormat>) -> __sdk::Result<Self> {",
941 |out| {
942 out.delimited_block(
943 "match &value.reducer_name[..] {",
944 |out| {
945 for reducer in iter_reducers(module) {
946 writeln!(
947 out,
948 "{:?} => Ok(__sdk::parse_reducer_args::<{}::{}>({:?}, &value.args)?.into()),",
949 reducer.name.deref(),
950 reducer_module_name(&reducer.name),
951 reducer_args_type_name(&reducer.name),
952 reducer.name.deref(),
953 );
954 }
955 writeln!(
956 out,
957 "unknown => Err(__sdk::InternalError::unknown_name(\"reducer\", unknown, \"ReducerCallInfo\").into()),",
958 );
959 },
960 "}\n",
961 )
962 },
963 "}\n",
964 );
965 },
966 "}\n",
967 )
968}
969
970fn print_db_update_defn(module: &ModuleDef, out: &mut Indenter) {
971 writeln!(out, "#[derive(Default)]");
972 writeln!(out, "#[allow(non_snake_case)]");
973 writeln!(out, "#[doc(hidden)]");
974 out.delimited_block(
975 "pub struct DbUpdate {",
976 |out| {
977 for table in iter_tables(module) {
978 writeln!(
979 out,
980 "{}: __sdk::TableUpdate<{}>,",
981 table_method_name(&table.name),
982 type_ref_name(module, table.product_type_ref),
983 );
984 }
985 },
986 "}\n",
987 );
988
989 out.newline();
990
991 out.delimited_block(
992 "
993impl TryFrom<__ws::DatabaseUpdate<__ws::BsatnFormat>> for DbUpdate {
994 type Error = __sdk::Error;
995 fn try_from(raw: __ws::DatabaseUpdate<__ws::BsatnFormat>) -> Result<Self, Self::Error> {
996 let mut db_update = DbUpdate::default();
997 for table_update in raw.tables {
998 match &table_update.table_name[..] {
999",
1000 |out| {
1001 for table in iter_tables(module) {
1002 writeln!(
1003 out,
1004 "{:?} => db_update.{}.append({}::parse_table_update(table_update)?),",
1005 table.name.deref(),
1006 table_method_name(&table.name),
1007 table_module_name(&table.name),
1008 );
1009 }
1010 },
1011 "
1012 unknown => {
1013 return Err(__sdk::InternalError::unknown_name(
1014 \"table\",
1015 unknown,
1016 \"DatabaseUpdate\",
1017 ).into());
1018 }
1019 }
1020 }
1021 Ok(db_update)
1022 }
1023}",
1024 );
1025
1026 out.newline();
1027
1028 writeln!(
1029 out,
1030 "
1031impl __sdk::InModule for DbUpdate {{
1032 type Module = RemoteModule;
1033}}
1034",
1035 );
1036
1037 out.delimited_block(
1038 "impl __sdk::DbUpdate for DbUpdate {",
1039 |out| {
1040 out.delimited_block(
1041 "fn apply_to_client_cache(&self, cache: &mut __sdk::ClientCache<RemoteModule>) -> AppliedDiff<'_> {
1042 let mut diff = AppliedDiff::default();
1043 ",
1044 |out| {
1045 for table in iter_tables(module) {
1046 let with_updates = table
1047 .primary_key
1048 .map(|col| {
1049 let pk_field = table.get_column(col).unwrap().name.deref().to_case(Case::Snake);
1050 format!(".with_updates_by_pk(|row| &row.{pk_field})")
1051 })
1052 .unwrap_or_default();
1053
1054 let field_name = table_method_name(&table.name);
1055 writeln!(
1056 out,
1057 "diff.{field_name} = cache.apply_diff_to_table::<{}>({:?}, &self.{field_name}){with_updates};",
1058 type_ref_name(module, table.product_type_ref),
1059 table.name.deref(),
1060 );
1061 }
1062 },
1063 "
1064 diff
1065 }\n",
1066 );
1067 },
1068 "}\n",
1069 );
1070}
1071
1072fn print_applied_diff_defn(module: &ModuleDef, out: &mut Indenter) {
1073 writeln!(out, "#[derive(Default)]");
1074 writeln!(out, "#[allow(non_snake_case)]");
1075 writeln!(out, "#[doc(hidden)]");
1076 out.delimited_block(
1077 "pub struct AppliedDiff<'r> {",
1078 |out| {
1079 for table in iter_tables(module) {
1080 writeln!(
1081 out,
1082 "{}: __sdk::TableAppliedDiff<'r, {}>,",
1083 table_method_name(&table.name),
1084 type_ref_name(module, table.product_type_ref),
1085 );
1086 }
1087 },
1088 "}\n",
1089 );
1090
1091 out.newline();
1092
1093 writeln!(
1094 out,
1095 "
1096impl __sdk::InModule for AppliedDiff<'_> {{
1097 type Module = RemoteModule;
1098}}
1099",
1100 );
1101
1102 out.delimited_block(
1103 "impl<'r> __sdk::AppliedDiff<'r> for AppliedDiff<'r> {",
1104 |out| {
1105 out.delimited_block(
1106 "fn invoke_row_callbacks(&self, event: &EventContext, callbacks: &mut __sdk::DbCallbacks<RemoteModule>) {",
1107 |out| {
1108 for table in iter_tables(module) {
1109 writeln!(
1110 out,
1111 "callbacks.invoke_table_row_callbacks::<{}>({:?}, &self.{}, event);",
1112 type_ref_name(module, table.product_type_ref),
1113 table.name.deref(),
1114 table_method_name(&table.name),
1115 );
1116 }
1117 },
1118 "}\n",
1119 );
1120 },
1121 "}\n",
1122 );
1123}
1124
1125fn print_impl_spacetime_module(module: &ModuleDef, out: &mut Indenter) {
1126 out.delimited_block(
1127 "impl __sdk::SpacetimeModule for RemoteModule {",
1128 |out| {
1129 writeln!(
1130 out,
1131 "
1132type DbConnection = DbConnection;
1133type EventContext = EventContext;
1134type ReducerEventContext = ReducerEventContext;
1135type SubscriptionEventContext = SubscriptionEventContext;
1136type ErrorContext = ErrorContext;
1137type Reducer = Reducer;
1138type DbView = RemoteTables;
1139type Reducers = RemoteReducers;
1140type SetReducerFlags = SetReducerFlags;
1141type DbUpdate = DbUpdate;
1142type AppliedDiff<'r> = AppliedDiff<'r>;
1143type SubscriptionHandle = SubscriptionHandle;
1144"
1145 );
1146 out.delimited_block(
1147 "fn register_tables(client_cache: &mut __sdk::ClientCache<Self>) {",
1148 |out| {
1149 for table in iter_tables(module) {
1150 writeln!(out, "{}::register_table(client_cache);", table_module_name(&table.name));
1151 }
1152 },
1153 "}\n",
1154 );
1155 },
1156 "}\n",
1157 );
1158}
1159
1160fn print_const_db_context_types(out: &mut Indenter) {
1161 writeln!(
1162 out,
1163 "
1164#[doc(hidden)]
1165pub struct RemoteModule;
1166
1167impl __sdk::InModule for RemoteModule {{
1168 type Module = Self;
1169}}
1170
1171/// The `reducers` field of [`EventContext`] and [`DbConnection`],
1172/// with methods provided by extension traits for each reducer defined by the module.
1173pub struct RemoteReducers {{
1174 imp: __sdk::DbContextImpl<RemoteModule>,
1175}}
1176
1177impl __sdk::InModule for RemoteReducers {{
1178 type Module = RemoteModule;
1179}}
1180
1181#[doc(hidden)]
1182/// The `set_reducer_flags` field of [`DbConnection`],
1183/// with methods provided by extension traits for each reducer defined by the module.
1184/// Each method sets the flags for the reducer with the same name.
1185///
1186/// This type is currently unstable and may be removed without a major version bump.
1187pub struct SetReducerFlags {{
1188 imp: __sdk::DbContextImpl<RemoteModule>,
1189}}
1190
1191impl __sdk::InModule for SetReducerFlags {{
1192 type Module = RemoteModule;
1193}}
1194
1195/// The `db` field of [`EventContext`] and [`DbConnection`],
1196/// with methods provided by extension traits for each table defined by the module.
1197pub struct RemoteTables {{
1198 imp: __sdk::DbContextImpl<RemoteModule>,
1199}}
1200
1201impl __sdk::InModule for RemoteTables {{
1202 type Module = RemoteModule;
1203}}
1204
1205/// A connection to a remote module, including a materialized view of a subset of the database.
1206///
1207/// Connect to a remote module by calling [`DbConnection::builder`]
1208/// and using the [`__sdk::DbConnectionBuilder`] builder-pattern constructor.
1209///
1210/// You must explicitly advance the connection by calling any one of:
1211///
1212/// - [`DbConnection::frame_tick`].
1213/// - [`DbConnection::run_threaded`].
1214/// - [`DbConnection::run_async`].
1215/// - [`DbConnection::advance_one_message`].
1216/// - [`DbConnection::advance_one_message_blocking`].
1217/// - [`DbConnection::advance_one_message_async`].
1218///
1219/// Which of these methods you should call depends on the specific needs of your application,
1220/// but you must call one of them, or else the connection will never progress.
1221pub struct DbConnection {{
1222 /// Access to tables defined by the module via extension traits implemented for [`RemoteTables`].
1223 pub db: RemoteTables,
1224 /// Access to reducers defined by the module via extension traits implemented for [`RemoteReducers`].
1225 pub reducers: RemoteReducers,
1226 #[doc(hidden)]
1227 /// Access to setting the call-flags of each reducer defined for each reducer defined by the module
1228 /// via extension traits implemented for [`SetReducerFlags`].
1229 ///
1230 /// This type is currently unstable and may be removed without a major version bump.
1231 pub set_reducer_flags: SetReducerFlags,
1232
1233 imp: __sdk::DbContextImpl<RemoteModule>,
1234}}
1235
1236impl __sdk::InModule for DbConnection {{
1237 type Module = RemoteModule;
1238}}
1239
1240impl __sdk::DbContext for DbConnection {{
1241 type DbView = RemoteTables;
1242 type Reducers = RemoteReducers;
1243 type SetReducerFlags = SetReducerFlags;
1244
1245 fn db(&self) -> &Self::DbView {{
1246 &self.db
1247 }}
1248 fn reducers(&self) -> &Self::Reducers {{
1249 &self.reducers
1250 }}
1251 fn set_reducer_flags(&self) -> &Self::SetReducerFlags {{
1252 &self.set_reducer_flags
1253 }}
1254
1255 fn is_active(&self) -> bool {{
1256 self.imp.is_active()
1257 }}
1258
1259 fn disconnect(&self) -> __sdk::Result<()> {{
1260 self.imp.disconnect()
1261 }}
1262
1263 type SubscriptionBuilder = __sdk::SubscriptionBuilder<RemoteModule>;
1264
1265 fn subscription_builder(&self) -> Self::SubscriptionBuilder {{
1266 __sdk::SubscriptionBuilder::new(&self.imp)
1267 }}
1268
1269 fn try_identity(&self) -> Option<__sdk::Identity> {{
1270 self.imp.try_identity()
1271 }}
1272 fn connection_id(&self) -> __sdk::ConnectionId {{
1273 self.imp.connection_id()
1274 }}
1275}}
1276
1277impl DbConnection {{
1278 /// Builder-pattern constructor for a connection to a remote module.
1279 ///
1280 /// See [`__sdk::DbConnectionBuilder`] for required and optional configuration for the new connection.
1281 pub fn builder() -> __sdk::DbConnectionBuilder<RemoteModule> {{
1282 __sdk::DbConnectionBuilder::new()
1283 }}
1284
1285 /// If any WebSocket messages are waiting, process one of them.
1286 ///
1287 /// Returns `true` if a message was processed, or `false` if the queue is empty.
1288 /// Callers should invoke this message in a loop until it returns `false`
1289 /// or for as much time is available to process messages.
1290 ///
1291 /// Returns an error if the connection is disconnected.
1292 /// If the disconnection in question was normal,
1293 /// i.e. the result of a call to [`__sdk::DbContext::disconnect`],
1294 /// the returned error will be downcastable to [`__sdk::DisconnectedError`].
1295 ///
1296 /// This is a low-level primitive exposed for power users who need significant control over scheduling.
1297 /// Most applications should call [`Self::frame_tick`] each frame
1298 /// to fully exhaust the queue whenever time is available.
1299 pub fn advance_one_message(&self) -> __sdk::Result<bool> {{
1300 self.imp.advance_one_message()
1301 }}
1302
1303 /// Process one WebSocket message, potentially blocking the current thread until one is received.
1304 ///
1305 /// Returns an error if the connection is disconnected.
1306 /// If the disconnection in question was normal,
1307 /// i.e. the result of a call to [`__sdk::DbContext::disconnect`],
1308 /// the returned error will be downcastable to [`__sdk::DisconnectedError`].
1309 ///
1310 /// This is a low-level primitive exposed for power users who need significant control over scheduling.
1311 /// Most applications should call [`Self::run_threaded`] to spawn a thread
1312 /// which advances the connection automatically.
1313 pub fn advance_one_message_blocking(&self) -> __sdk::Result<()> {{
1314 self.imp.advance_one_message_blocking()
1315 }}
1316
1317 /// Process one WebSocket message, `await`ing until one is received.
1318 ///
1319 /// Returns an error if the connection is disconnected.
1320 /// If the disconnection in question was normal,
1321 /// i.e. the result of a call to [`__sdk::DbContext::disconnect`],
1322 /// the returned error will be downcastable to [`__sdk::DisconnectedError`].
1323 ///
1324 /// This is a low-level primitive exposed for power users who need significant control over scheduling.
1325 /// Most applications should call [`Self::run_async`] to run an `async` loop
1326 /// which advances the connection when polled.
1327 pub async fn advance_one_message_async(&self) -> __sdk::Result<()> {{
1328 self.imp.advance_one_message_async().await
1329 }}
1330
1331 /// Process all WebSocket messages waiting in the queue,
1332 /// then return without `await`ing or blocking the current thread.
1333 pub fn frame_tick(&self) -> __sdk::Result<()> {{
1334 self.imp.frame_tick()
1335 }}
1336
1337 /// Spawn a thread which processes WebSocket messages as they are received.
1338 pub fn run_threaded(&self) -> std::thread::JoinHandle<()> {{
1339 self.imp.run_threaded()
1340 }}
1341
1342 /// Run an `async` loop which processes WebSocket messages when polled.
1343 pub async fn run_async(&self) -> __sdk::Result<()> {{
1344 self.imp.run_async().await
1345 }}
1346}}
1347
1348impl __sdk::DbConnection for DbConnection {{
1349 fn new(imp: __sdk::DbContextImpl<RemoteModule>) -> Self {{
1350 Self {{
1351 db: RemoteTables {{ imp: imp.clone() }},
1352 reducers: RemoteReducers {{ imp: imp.clone() }},
1353 set_reducer_flags: SetReducerFlags {{ imp: imp.clone() }},
1354 imp,
1355 }}
1356 }}
1357}}
1358
1359/// A handle on a subscribed query.
1360// TODO: Document this better after implementing the new subscription API.
1361#[derive(Clone)]
1362pub struct SubscriptionHandle {{
1363 imp: __sdk::SubscriptionHandleImpl<RemoteModule>,
1364}}
1365
1366impl __sdk::InModule for SubscriptionHandle {{
1367 type Module = RemoteModule;
1368}}
1369
1370impl __sdk::SubscriptionHandle for SubscriptionHandle {{
1371 fn new(imp: __sdk::SubscriptionHandleImpl<RemoteModule>) -> Self {{
1372 Self {{ imp }}
1373 }}
1374
1375 /// Returns true if this subscription has been terminated due to an unsubscribe call or an error.
1376 fn is_ended(&self) -> bool {{
1377 self.imp.is_ended()
1378 }}
1379
1380 /// Returns true if this subscription has been applied and has not yet been unsubscribed.
1381 fn is_active(&self) -> bool {{
1382 self.imp.is_active()
1383 }}
1384
1385 /// Unsubscribe from the query controlled by this `SubscriptionHandle`,
1386 /// then run `on_end` when its rows are removed from the client cache.
1387 fn unsubscribe_then(self, on_end: __sdk::OnEndedCallback<RemoteModule>) -> __sdk::Result<()> {{
1388 self.imp.unsubscribe_then(Some(on_end))
1389 }}
1390
1391 fn unsubscribe(self) -> __sdk::Result<()> {{
1392 self.imp.unsubscribe_then(None)
1393 }}
1394
1395}}
1396
1397/// Alias trait for a [`__sdk::DbContext`] connected to this module,
1398/// with that trait's associated types bounded to this module's concrete types.
1399///
1400/// Users can use this trait as a boundary on definitions which should accept
1401/// either a [`DbConnection`] or an [`EventContext`] and operate on either.
1402pub trait RemoteDbContext: __sdk::DbContext<
1403 DbView = RemoteTables,
1404 Reducers = RemoteReducers,
1405 SetReducerFlags = SetReducerFlags,
1406 SubscriptionBuilder = __sdk::SubscriptionBuilder<RemoteModule>,
1407> {{}}
1408impl<Ctx: __sdk::DbContext<
1409 DbView = RemoteTables,
1410 Reducers = RemoteReducers,
1411 SetReducerFlags = SetReducerFlags,
1412 SubscriptionBuilder = __sdk::SubscriptionBuilder<RemoteModule>,
1413>> RemoteDbContext for Ctx {{}}
1414",
1415 );
1416
1417 define_event_context(
1418 out,
1419 "EventContext",
1420 Some("__sdk::Event<Reducer>"),
1421 "[`__sdk::Table::on_insert`], [`__sdk::Table::on_delete`] and [`__sdk::TableWithPrimaryKey::on_update`] callbacks",
1422 Some("[`__sdk::Event`]"),
1423 );
1424
1425 define_event_context(
1426 out,
1427 "ReducerEventContext",
1428 Some("__sdk::ReducerEvent<Reducer>"),
1429 "on-reducer callbacks", Some("[`__sdk::ReducerEvent`]"),
1431 );
1432
1433 define_event_context(
1434 out,
1435 "SubscriptionEventContext",
1436 None, "[`__sdk::SubscriptionBuilder::on_applied`] and [`SubscriptionHandle::unsubscribe_then`] callbacks",
1438 None,
1439 );
1440
1441 define_event_context(
1442 out,
1443 "ErrorContext",
1444 Some("Option<__sdk::Error>"),
1445 "[`__sdk::DbConnectionBuilder::on_disconnect`], [`__sdk::DbConnectionBuilder::on_connect_error`] and [`__sdk::SubscriptionBuilder::on_error`] callbacks",
1446 Some("[`__sdk::Error`]"),
1447 );
1448}
1449
1450fn define_event_context(
1468 out: &mut Indenter,
1469 struct_and_trait_name: &str,
1470 event_type: Option<&str>,
1471 passed_to_callbacks_doc_link: &str,
1472 event_type_doc_link: Option<&str>,
1473) {
1474 if let (Some(event_type), Some(event_type_doc_link)) = (event_type, event_type_doc_link) {
1475 write!(
1476 out,
1477 "
1478/// An [`__sdk::DbContext`] augmented with a {event_type_doc_link},
1479/// passed to {passed_to_callbacks_doc_link}.
1480pub struct {struct_and_trait_name} {{
1481 /// Access to tables defined by the module via extension traits implemented for [`RemoteTables`].
1482 pub db: RemoteTables,
1483 /// Access to reducers defined by the module via extension traits implemented for [`RemoteReducers`].
1484 pub reducers: RemoteReducers,
1485 /// Access to setting the call-flags of each reducer defined for each reducer defined by the module
1486 /// via extension traits implemented for [`SetReducerFlags`].
1487 ///
1488 /// This type is currently unstable and may be removed without a major version bump.
1489 pub set_reducer_flags: SetReducerFlags,
1490 /// The event which caused these callbacks to run.
1491 pub event: {event_type},
1492 imp: __sdk::DbContextImpl<RemoteModule>,
1493}}
1494
1495impl __sdk::AbstractEventContext for {struct_and_trait_name} {{
1496 type Event = {event_type};
1497 fn event(&self) -> &Self::Event {{
1498 &self.event
1499 }}
1500 fn new(imp: __sdk::DbContextImpl<RemoteModule>, event: Self::Event) -> Self {{
1501 Self {{
1502 db: RemoteTables {{ imp: imp.clone() }},
1503 reducers: RemoteReducers {{ imp: imp.clone() }},
1504 set_reducer_flags: SetReducerFlags {{ imp: imp.clone() }},
1505 event,
1506 imp,
1507 }}
1508 }}
1509}}
1510",
1511 );
1512 } else {
1513 debug_assert!(event_type.is_none() && event_type_doc_link.is_none());
1514 write!(
1515 out,
1516 "
1517/// An [`__sdk::DbContext`] passed to {passed_to_callbacks_doc_link}.
1518pub struct {struct_and_trait_name} {{
1519 /// Access to tables defined by the module via extension traits implemented for [`RemoteTables`].
1520 pub db: RemoteTables,
1521 /// Access to reducers defined by the module via extension traits implemented for [`RemoteReducers`].
1522 pub reducers: RemoteReducers,
1523 /// Access to setting the call-flags of each reducer defined for each reducer defined by the module
1524 /// via extension traits implemented for [`SetReducerFlags`].
1525 ///
1526 /// This type is currently unstable and may be removed without a major version bump.
1527 pub set_reducer_flags: SetReducerFlags,
1528 imp: __sdk::DbContextImpl<RemoteModule>,
1529}}
1530
1531impl __sdk::AbstractEventContext for {struct_and_trait_name} {{
1532 type Event = ();
1533 fn event(&self) -> &Self::Event {{
1534 &()
1535 }}
1536 fn new(imp: __sdk::DbContextImpl<RemoteModule>, _event: Self::Event) -> Self {{
1537 Self {{
1538 db: RemoteTables {{ imp: imp.clone() }},
1539 reducers: RemoteReducers {{ imp: imp.clone() }},
1540 set_reducer_flags: SetReducerFlags {{ imp: imp.clone() }},
1541 imp,
1542 }}
1543 }}
1544}}
1545",
1546 );
1547 }
1548
1549 write!(
1550 out,
1551 "
1552impl __sdk::InModule for {struct_and_trait_name} {{
1553 type Module = RemoteModule;
1554}}
1555
1556impl __sdk::DbContext for {struct_and_trait_name} {{
1557 type DbView = RemoteTables;
1558 type Reducers = RemoteReducers;
1559 type SetReducerFlags = SetReducerFlags;
1560
1561 fn db(&self) -> &Self::DbView {{
1562 &self.db
1563 }}
1564 fn reducers(&self) -> &Self::Reducers {{
1565 &self.reducers
1566 }}
1567 fn set_reducer_flags(&self) -> &Self::SetReducerFlags {{
1568 &self.set_reducer_flags
1569 }}
1570
1571 fn is_active(&self) -> bool {{
1572 self.imp.is_active()
1573 }}
1574
1575 fn disconnect(&self) -> __sdk::Result<()> {{
1576 self.imp.disconnect()
1577 }}
1578
1579 type SubscriptionBuilder = __sdk::SubscriptionBuilder<RemoteModule>;
1580
1581 fn subscription_builder(&self) -> Self::SubscriptionBuilder {{
1582 __sdk::SubscriptionBuilder::new(&self.imp)
1583 }}
1584
1585 fn try_identity(&self) -> Option<__sdk::Identity> {{
1586 self.imp.try_identity()
1587 }}
1588 fn connection_id(&self) -> __sdk::ConnectionId {{
1589 self.imp.connection_id()
1590 }}
1591}}
1592
1593impl __sdk::{struct_and_trait_name} for {struct_and_trait_name} {{}}
1594"
1595 );
1596}
1597
1598fn print_imports(module: &ModuleDef, out: &mut Indenter, imports: Imports) {
1600 for typeref in imports {
1601 let module_name = type_ref_module_name(module, typeref);
1602 let type_name = type_ref_name(module, typeref);
1603 writeln!(out, "use super::{module_name}::{type_name};");
1604 }
1605}
1606
1607fn gen_and_print_imports(
1613 module: &ModuleDef,
1614 out: &mut Indenter,
1615 roots: &[(Identifier, AlgebraicTypeUse)],
1616 dont_import: &[AlgebraicTypeRef],
1617) {
1618 let mut imports = BTreeSet::new();
1619
1620 for (_, ty) in roots {
1621 ty.for_each_ref(|r| {
1622 imports.insert(r);
1623 });
1624 }
1625 for skip in dont_import {
1626 imports.remove(skip);
1627 }
1628
1629 print_imports(module, out, imports);
1630}