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