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