spacetimedb_codegen/
rust.rs

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