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