Skip to main content

modrpc_codegen/codegen/rust/
mod.rs

1use genco::prelude::*;
2
3use mproto_codegen::codegen::{
4    name_util::{camel_to_snake_case, snake_to_upper_camel_case},
5    rust::{rust_type_default_value, rust_type_param_list},
6};
7
8use crate::{
9    ast::{Interface, InterfaceObject},
10    Database,
11};
12
13pub use project::{rust_project_gen, rust_proto_package_gen, rust_role_impl_gen};
14
15mod project;
16
17fn import_qualified(
18    module: Option<impl AsRef<str>>,
19    name: impl AsRef<str>,
20) -> rust::Tokens {
21    if let Some(module) = module {
22        quote! { $(rust::import(format!("{}_modrpc", module.as_ref()), name.as_ref())) }
23    } else {
24        quote! { $(name.as_ref()) }
25    }
26}
27
28pub fn rust_interface(
29    db: &Database,
30    interface: &Interface,
31) -> rust::Tokens {
32    let interface_builder = &rust::import("modrpc", "InterfaceBuilder");
33    let interface_event = &rust::import("modrpc", "InterfaceEvent");
34    let interface_schema = &rust::import("modrpc", "InterfaceSchema");
35
36    let mproto_cx = &mproto_codegen::codegen::CodegenCx::new_with_type_params(
37        db.mproto_db(), Some("crate::proto"), true, &interface.type_params,
38    );
39
40    let mut events_field_tokens = rust::Tokens::new();
41    let mut events_constr_tokens = rust::Tokens::new();
42    for events_list in &interface.events {
43        for event in &events_list.events {
44            let event_type_tokens = mproto_codegen::codegen::rust::rust_type_tokens(mproto_cx, &event.ty);
45            events_field_tokens = quote!{
46                $events_field_tokens
47                pub $(&event.name): $interface_event<$event_type_tokens>,
48            };
49
50            let event_name_tokens = quote! { $("\"")$(&event.name)$("\"") };
51            events_constr_tokens = quote! {
52                $events_constr_tokens
53                $(&event.name): ib.event($event_name_tokens),
54            };
55        }
56    }
57
58    let mut objects_field_tokens = rust::Tokens::new();
59    let mut objects_constr_tokens = rust::Tokens::new();
60    for object in &interface.objects {
61        if db.lookup_interface(&object.construct).is_none() {
62            panic!("failed to lookup interface '{}'", object.construct.name);
63        };
64
65        let object_type_args = &mproto_codegen::codegen::rust::rust_type_arg_list(
66            mproto_cx, &object.type_args, None,
67        );
68
69        let object_interface_import = import_qualified(
70            object.construct.module.as_ref(),
71            format!("{}Interface", object.construct.name),
72        );
73        objects_field_tokens = quote! {
74            $objects_field_tokens
75            pub $(&object.name): $(object_interface_import)$(object_type_args),
76        };
77
78        objects_constr_tokens = quote! {
79            $objects_constr_tokens
80            $(&object.name): $(&object.construct.name)Interface::new(ib),
81        };
82    }
83
84    let type_params = &rust_type_param_list(&interface.type_params, None, None);
85
86    let tokens: rust::Tokens = quote! {
87        pub struct $(&interface.name)Interface$(type_params) {
88            $events_field_tokens
89            $objects_field_tokens
90        }
91
92        impl$(type_params) $interface_schema for $(&interface.name)Interface$(type_params) {
93            fn new(ib: &mut $interface_builder) -> Self {
94                Self {
95                    $events_constr_tokens
96                    $objects_constr_tokens
97                }
98            }
99        }
100    };
101
102    tokens
103}
104
105pub fn rust_interface_role(
106    db: &Database,
107    interface: &Interface,
108    role_name: &str,
109) -> rust::Tokens {
110    let role_setup = &rust::import("modrpc", "RoleSetup");
111    let event_tx = &rust::import("modrpc", "EventTx");
112    let event_rx_builder = &rust::import("modrpc", "EventRxBuilder");
113    let interface_role = &rust::import("modrpc", "InterfaceRole");
114    let proto_interface = &rust::import("crate::interface", format!("{}Interface", interface.name));
115    let init_state = &rust::import("crate::proto", format!("{}InitState", interface.name));
116    let role_config = &rust::import("crate::proto", format!("{}{}Config", interface.name, role_name));
117
118    let interface_role_name = &format!("{}{}", interface.name, role_name);
119    let interface_role_name_snake = &camel_to_snake_case(interface_role_name);
120
121    let mproto_cx = &mproto_codegen::codegen::CodegenCx::new_with_type_params(
122        db.mproto_db(), Some("crate::proto"), true, &interface.type_params,
123    );
124
125    let mut events_hook_field_tokens = rust::Tokens::new();
126    let mut events_hook_constr_field_tokens = rust::Tokens::new();
127    let mut events_stubs_field_tokens = rust::Tokens::new();
128    let mut events_stubs_constr_field_tokens = rust::Tokens::new();
129    let mut events_clone_tokens = rust::Tokens::new();
130    for events_list in &interface.events {
131        let role_needs_event_tx =
132            events_list.from_roles.iter().find(|x| x == &role_name).is_some();
133        let role_needs_event_rx =
134            events_list.to_roles.iter().find(|x| x == &role_name).is_some();
135
136        for event in &events_list.events {
137            let payload_ty = mproto_codegen::codegen::rust::rust_type_tokens(&mproto_cx, &event.ty);
138
139            let hook_field_tokens: rust::Tokens =
140                if role_needs_event_tx {
141                    quote! {
142                        pub $(&event.name): $event_tx<$(&payload_ty)>,
143                    }
144                } else { quote! { } };
145            let hook_constr_tokens: rust::Tokens =
146                if role_needs_event_tx {
147                    quote! {
148                        $(&event.name): setup.event_tx(i.$(&event.name)),
149                    }
150                } else { quote! { } };
151            let stubs_field_tokens: rust::Tokens =
152                if role_needs_event_rx {
153                    quote! {
154                        pub $(&event.name): $event_rx_builder<$(&payload_ty)>,
155                    }
156                } else { quote! { } };
157            let stubs_constr_tokens: rust::Tokens =
158                if role_needs_event_rx {
159                    quote! {
160                        $(&event.name): setup.event_rx(i.$(&event.name)),
161                    }
162                } else { quote! { } };
163            let clone_tokens: rust::Tokens =
164                if role_needs_event_tx {
165                    quote! {
166                        $(&event.name): self.$(&event.name).clone(),
167                    }
168                } else { quote! { } };
169
170            events_hook_field_tokens = quote! {
171                $events_hook_field_tokens
172                $hook_field_tokens
173            };
174            events_hook_constr_field_tokens = quote! {
175                $events_hook_constr_field_tokens
176                $hook_constr_tokens
177            };
178            events_stubs_field_tokens = quote! {
179                $events_stubs_field_tokens
180                $stubs_field_tokens
181            };
182            events_stubs_constr_field_tokens = quote! {
183                $events_stubs_constr_field_tokens
184                $stubs_constr_tokens
185            };
186            events_clone_tokens = quote! {
187                $events_clone_tokens
188                $clone_tokens
189            };
190        }
191    }
192
193    let mut objects_hook_field_tokens = rust::Tokens::new();
194    let mut objects_hook_constr_tokens = rust::Tokens::new();
195    let mut objects_hook_constr_stubs_tokens = rust::Tokens::new();
196    let mut objects_hook_constr_field_tokens = rust::Tokens::new();
197    let mut objects_stubs_field_tokens = rust::Tokens::new();
198    let mut objects_clone_tokens = rust::Tokens::new();
199    let mut i = 0;
200    for object in &interface.objects {
201        let Some(object_interface) = db.lookup_interface(&object.construct) else {
202            panic!("failed to lookup interface '{}'", object.construct.name);
203        };
204
205        let object_role_names = object.get_roles_for_parent_role(object_interface, role_name);
206        if object_role_names.is_empty() {
207            panic!("object '{}' failed to find roles '{}'", object.name, role_name);
208        };
209
210        for object_role_name in &object_role_names {
211            let construct_module = object.construct.module.as_ref();
212            let object_interface_role_name = &format!("{}{}", object.construct.name, object_role_name);
213
214            let object_field_name =
215                if object_role_names.len() > 1 {
216                    &format!("{}_{}", object.name, camel_to_snake_case(object_role_name))
217                } else {
218                    &object.name
219                };
220
221            let object_type_args = &mproto_codegen::codegen::rust::rust_type_arg_list(
222                mproto_cx, &object.type_args, None,
223            );
224
225            let hook_field_tokens = quote! {
226                pub $(object_field_name): $(import_qualified(construct_module, object_interface_role_name))$(object_type_args),
227            };
228
229            let object_config: &rust::Tokens =
230                &if object_interface.has_config_for_role(object_role_name) {
231                    quote! { config.$(object_field_name) }
232                } else {
233                    quote! { $(import_qualified(construct_module, format!("{}Config", object_interface_role_name))) { } }
234                };
235
236            let object_init: &rust::Tokens =
237                &if object_interface.has_state(db) {
238                    quote! { init.$(object_field_name) }
239                } else {
240                    quote! { $(import_qualified(construct_module, format!("{}InitState", object.construct.name))) { } }
241                };
242
243            let mut hook_constr_tokens = quote! {
244                let ($(object_field_name)_stubs, $(object_field_name)_hooks) =
245                    $(import_qualified(construct_module, format!("{}Role", object_interface_role_name)))::setup_worker(
246                        &i.$(&object.name), setup, &$(object_config), &$(object_init),
247                    );
248                let $(object_field_name)_builder = $(import_qualified(construct_module, format!("{}Builder", object_interface_role_name)))::new(
249                    $("\"")$(interface_role_name_snake).$(&object.name)$("\""),
250                    $(object_field_name)_hooks,
251                    $(object_field_name)_stubs,
252                    &$(object_config),
253                    $(object_init).clone(),
254                );
255                let $(object_field_name) = $(object_field_name)_builder.create_handle(setup);
256            };
257            let hook_constr_field_tokens = quote! {
258                $(object_field_name),
259            };
260
261            let clone_tokens = quote! {
262                $(object_field_name): self.$(object_field_name).clone(),
263            };
264
265            // For impl setup, need to determine if the user needs to provide any `impl`s for this
266            // role. If the user doesn't need to provide any `impl`s, we auto-implement the stubs for
267            // this object.
268
269            let stubs_field_tokens: rust::Tokens;
270            let hook_constr_stubs_tokens: rust::Tokens;
271            if object_interface.requires_impls_for_role(object_role_name) {
272                // User needs to provide impls
273
274                stubs_field_tokens = quote! {
275                    pub $(object_field_name): $(import_qualified(construct_module, format!("{}Builder", object_interface_role_name)))$(object_type_args),
276                };
277
278                hook_constr_stubs_tokens = quote! {
279                    $(object_field_name): $(object_field_name)_builder,
280                };
281            } else {
282                // User does not need to provide impls
283
284                hook_constr_tokens = quote! {
285                    $hook_constr_tokens
286                    $(object_field_name)_builder.build(setup);
287                };
288
289                stubs_field_tokens = quote! { };
290
291                hook_constr_stubs_tokens = quote! { };
292            }
293
294            hook_constr_tokens = quote! {
295                setup.push_object_path($("\"")$(&object.name)$("\""));
296                $hook_constr_tokens
297                setup.pop_object_path();
298            };
299
300            if i > 0 {
301                objects_hook_field_tokens = quote! {
302                    $objects_hook_field_tokens
303                    $hook_field_tokens
304                };
305                objects_hook_constr_tokens = quote! {
306                    $objects_hook_constr_tokens
307                    $hook_constr_tokens
308                };
309                objects_hook_constr_stubs_tokens = quote! {
310                    $objects_hook_constr_stubs_tokens
311                    $hook_constr_stubs_tokens
312                };
313                objects_hook_constr_field_tokens = quote! {
314                    $objects_hook_constr_field_tokens
315                    $hook_constr_field_tokens
316                };
317                objects_stubs_field_tokens = quote! {
318                    $objects_stubs_field_tokens
319                    $stubs_field_tokens
320                };
321                objects_clone_tokens = quote! {
322                    $objects_clone_tokens
323                    $clone_tokens
324                };
325            } else {
326                objects_hook_field_tokens = hook_field_tokens;
327                objects_stubs_field_tokens = stubs_field_tokens;
328                objects_hook_constr_tokens = hook_constr_tokens;
329                objects_hook_constr_stubs_tokens = hook_constr_stubs_tokens;
330                objects_hook_constr_field_tokens = hook_constr_field_tokens;
331                objects_clone_tokens = clone_tokens;
332            }
333
334            i += 1;
335        }
336    }
337
338    ////////////////////////////////////
339
340    let type_params = &rust_type_param_list(&interface.type_params, None, None);
341    let type_params_no_lifetime = &rust_type_param_list(&interface.type_params, None, None);
342    let type_params_no_lifetime_bounded = &rust_type_param_list(&interface.type_params, None, Some(quote! { mproto::Owned }));
343
344    let mut interface_type_params_tokens = rust::Tokens::new();
345    if interface.type_params.len() > 0 {
346        interface_type_params_tokens = quote! { $(&interface.type_params[0]) };
347        for type_param in &interface.type_params[1..] {
348            interface_type_params_tokens = quote! { $interface_type_params_tokens, $(type_param) };
349        }
350    }
351    let interface_type_params_tokens = &interface_type_params_tokens;
352
353    ////////////////////////////////////
354
355    let phantom_data: &rust::Tokens =
356        if interface.type_params.len() > 1 {
357            &quote! { _phantom: std::marker::PhantomData<($interface_type_params_tokens)>, }
358        } else if interface.type_params.len() == 1 {
359            &quote! { _phantom: std::marker::PhantomData<$interface_type_params_tokens>, }
360        } else {
361            &quote! { }
362        };
363
364    let role_config_def = db.mproto_db().lookup_type_def(
365        &mproto_codegen::ast::QualifiedIdentifier::local(
366            format!("{}{}Config", interface.name, role_name)
367        )
368    )
369    .expect("role config struct def");
370
371    let init_state_with_args: rust::Tokens =
372        if interface.has_state(db) {
373            quote! { $(init_state)$(type_params_no_lifetime) }
374        } else {
375            quote! { $(init_state) }
376        };
377
378    quote! {
379        pub struct $(interface_role_name)Hooks$(type_params) {
380            $events_hook_field_tokens
381            $objects_hook_field_tokens
382            $phantom_data
383        }
384
385        pub struct $(interface_role_name)Stubs$(type_params) {
386            $events_stubs_field_tokens
387            $objects_stubs_field_tokens
388            $phantom_data
389        }
390
391        pub struct $(interface_role_name)Role$(type_params_no_lifetime) {
392            $phantom_data
393        }
394
395        impl$(type_params_no_lifetime_bounded) $interface_role for $(&interface.name)$(role_name)Role$(type_params_no_lifetime) {
396            type Interface = $(proto_interface)$(type_params_no_lifetime);
397            type Config = $(role_config)$(rust_type_param_list(&role_config_def.params, None, None));
398            type Init = $init_state_with_args;
399            type Stubs = $(interface_role_name)Stubs$(type_params);
400            type Hooks = $(interface_role_name)Hooks$(type_params);
401
402            fn setup_worker(
403                i: &Self::Interface,
404                setup: &mut $role_setup,
405                config: &Self::Config,
406                init: &Self::Init,
407            ) -> (Self::Stubs, Self::Hooks) {
408                $objects_hook_constr_tokens
409
410                (
411                    Self::Stubs {
412                        $events_stubs_constr_field_tokens
413                        $objects_hook_constr_stubs_tokens
414                        $(if !interface.type_params.is_empty() { _phantom: std::marker::PhantomData, })
415                    },
416                    Self::Hooks {
417                        $events_hook_constr_field_tokens
418                        $objects_hook_constr_field_tokens
419                        $(if !interface.type_params.is_empty() { _phantom: std::marker::PhantomData, })
420                    },
421                )
422            }
423        }
424
425        impl$(type_params) Clone for $(interface_role_name)Hooks$(type_params) {
426            fn clone(&self) -> Self {
427                Self {
428                    $events_clone_tokens
429                    $objects_clone_tokens
430                    $(if !interface.type_params.is_empty() { _phantom: std::marker::PhantomData, })
431                }
432            }
433        }
434    }
435}
436
437pub fn rust_interface_role_impl(
438    db: &Database,
439    interface: &Interface,
440    role_name: &str,
441) -> rust::Tokens {
442    if !interface.has_private_event_impls_for_role(role_name)
443        && !interface.has_methods_for_role(role_name)
444        && !interface.has_config_for_role(role_name)
445        && !interface.has_impls_for_role(role_name)
446    {
447        return quote! { };
448    }
449
450    let role_config = &rust::import("crate::proto", format!("{}{}Config", interface.name, role_name));
451    let init_state = &rust::import("crate::proto", format!("{}InitState", interface.name));
452    let role_setup = &rust::import("modrpc", "RoleSetup");
453
454    let interface_role_name = &format!("{}{}", interface.name, role_name);
455
456    let mproto_cx = &mproto_codegen::codegen::CodegenCx::new_with_type_params(
457        db.mproto_db(), Some("crate::proto"), true, &interface.type_params,
458    );
459
460    let type_params = &rust_type_param_list(&interface.type_params, None, None);
461    let type_params_bounded = &rust_type_param_list(&interface.type_params, None, Some(quote! { mproto::Owned }));
462    let type_params_no_lifetime = &rust_type_param_list(&interface.type_params, None, None);
463
464    let mut event_handler_stubs = rust::Tokens::new();
465    for events_list in &interface.events {
466        if !events_list.to_roles.iter().find(|x| x.as_str() == role_name).is_some() { continue; }
467
468        for event in &events_list.events {
469            if !event.is_private { continue; }
470
471            let payload_ty = mproto_codegen::codegen::rust::rust_type_tokens(mproto_cx, &event.ty);
472
473            event_handler_stubs = quote! {
474                $event_handler_stubs
475                let $(&event.name) = self.stubs.$(&event.name)
476                    .queued(setup, async move |_source, _event: $(payload_ty)| {
477                    })
478                    .load_balance();
479            };
480        }
481    }
482
483    let mut methods = rust::Tokens::new();
484    for methods_list in &interface.methods {
485        if !methods_list.roles.iter().find(|x| x.as_str() == role_name).is_some() { continue; }
486
487        for method in &methods_list.methods {
488            let input_ty = &mproto_codegen::codegen::rust::rust_type_tokens(mproto_cx, &method.input_ty);
489            let output_ty = &mproto_codegen::codegen::rust::rust_type_tokens(mproto_cx, &method.output_ty);
490
491            methods = quote! {
492                $methods
493                pub $(
494                    if method.is_async { async } else { }
495                ) fn $(&method.name)(
496                    &self,
497                    input: impl mproto::Encode + for<'a> mproto::Decode<'a> + mproto::Compatible<$input_ty>,
498                ) -> $output_ty {
499                    $(rust_type_default_value(
500                        mproto_cx,
501                        &method.output_ty,
502                    ))
503                }
504            };
505        }
506    }
507
508    if !methods.is_empty() {
509        methods = quote! {
510            impl$(type_params_bounded) $(interface_role_name)$(type_params) {
511                $methods
512            }
513        };
514    }
515
516    let role_config_def = db.mproto_db().lookup_type_def(
517        &mproto_codegen::ast::QualifiedIdentifier::local(
518            format!("{}{}Config", interface.name, role_name)
519        )
520    )
521    .expect("role config struct def");
522
523    let init_state_with_args: &rust::Tokens =
524        &if interface.has_state(db) {
525            quote! { $(init_state)$(type_params_no_lifetime) }
526        } else {
527            quote! { $(init_state) }
528        };
529
530    quote! {
531        #[derive(Clone)]
532        pub struct $(interface_role_name)$(type_params) {
533            hooks: crate::$(interface_role_name)Hooks$(type_params),
534        }
535
536        pub struct $(interface_role_name)Builder$(type_params) {
537            pub name: &'static str,
538            pub hooks: crate::$(interface_role_name)Hooks$(type_params),
539            pub stubs: crate::$(interface_role_name)Stubs$(type_params),
540            pub init: $init_state_with_args,
541        }
542
543        impl$(type_params_bounded) $(interface_role_name)Builder$(type_params) {
544            pub fn new(
545                name: &'static str,
546                hooks: crate::$(interface_role_name)Hooks$(type_params),
547                stubs: crate::$(interface_role_name)Stubs$(type_params),
548                config: &$(role_config)$(rust_type_param_list(&role_config_def.params, None, None)),
549                init: $init_state_with_args,
550            ) -> Self {
551                Self { name, hooks, stubs, init }
552            }
553
554            pub fn create_handle(
555                &self,
556                setup: &$role_setup,
557            ) -> crate::$(interface_role_name)$(type_params) {
558                crate::$(interface_role_name) {
559                    hooks: self.hooks.clone(),
560                }
561            }
562
563            pub fn build(
564                self,
565                setup: &$role_setup,
566            ) {
567                $event_handler_stubs
568            }
569        }
570
571        $methods
572    }
573}
574
575pub fn request_impl_stub(
576    mproto_cx: &mproto_codegen::codegen::CodegenCx,
577    object: &InterfaceObject,
578) -> rust::Tokens {
579    let object_name_upper_camel = &snake_to_upper_camel_case(&object.name);
580
581    let request_ty = mproto_codegen::codegen::rust::rust_type_lazy_tokens(
582        mproto_cx, &object.type_args[0],
583    );
584    let response_ty = mproto_codegen::codegen::rust::rust_type_tokens(
585        mproto_cx, &object.type_args[1],
586    );
587
588    quote! {
589        pub struct $(object_name_upper_camel)Handler {
590        }
591
592        impl std_modrpc::RequestHandlerReply for $(object_name_upper_camel)Handler {
593            type Input<'a> = $(request_ty);
594            type Output = $(response_ty);
595
596            async fn call<'a>(
597                &'a mut self,
598                mut cx: std_modrpc::RequestContext<'a, Self::Output>,
599                request: Self::Input<'a>,
600            ) -> Option<()> {
601                None
602            }
603        }
604    }
605}
606
607pub struct RustInterfaceImpl {
608    pub main_rs: rust::Tokens,
609    pub handlers: Vec<(String, rust::Tokens)>,
610}
611
612impl RustInterfaceImpl {
613    pub fn generate(
614        mproto_db: &mproto_codegen::Database,
615        package_name: &str,
616        interface: &Interface,
617        role_name: &str,
618    ) -> Self {
619        let interface_name = &interface.name;
620        let interface_role_name = &format!("{}{}", interface.name, role_name);
621        let package_name = &package_name.replace("-", "_");
622
623        let def_crate = format!("{}_modrpc", package_name);
624        let def_crate = def_crate.as_str();
625        let def_source = Some(def_crate);
626
627        let mproto_cx = &mproto_codegen::codegen::CodegenCx::new_with_type_params(
628            mproto_db, def_source, true, &interface.type_params,
629        );
630
631        let rc = &rust::import("std::rc", "Rc");
632        let ref_cell = &rust::import("std::cell", "RefCell");
633        let block_on = &rust::import("futures_lite::future", "block_on");
634        let modmesh_session = &rust::import("modmesh_endpoint", "ModmeshSession");
635
636        let mut register_handlers = rust::Tokens::new();
637        let mut handlers = Vec::new();
638        for object in &interface.objects {
639            if object.construct.name == "Request" && object.construct.module.as_ref().map(|s| s.as_ref()) == Some("std") {
640                // TODO also ensure that this interface role acts as the request server role.
641
642                let object_name_upper_camel = &snake_to_upper_camel_case(&object.name);
643                register_handlers = quote! {
644                    $register_handlers
645                    cx.stubs.$(&object.name).build_replier(s, $(object_name_upper_camel)Handler {});
646                };
647
648                handlers.push((object.name.clone(), request_impl_stub(mproto_cx, object)));
649            }
650        }
651
652        let main_rs = quote! {
653            struct $(interface_role_name)State {
654            }
655
656            fn main() {
657                let state = Rc::new(RefCell::new($(interface_role_name)State {
658                }));
659
660                loop {
661                    $block_on(async move {
662                        if let Ok(mut modmesh) = $modmesh_session::connect(("127.0.0.1", 13016)).await {
663                            modmesh.host_service::<$def_crate::$interface_role_name>(
664                                "untitled-service".into(),
665                                $(def_crate)::$(interface_name)InitState { },
666                                |cx| start_$(package_name)(cx, state.clone())
667                            ).await;
668
669                            modmesh.run().await;
670                        }
671
672                        // TODO
673                        std::thread::sleep(std::time::Duration::from_secs(5));
674                        println!("Retrying modmesh connection...");
675                    });
676                }
677            }
678
679            fn start_$(package_name)(
680                cx: modrpc::RoleWorkerContext<$def_crate::$(interface_role_name)Role>,
681                state: $rc<$ref_cell<$(interface_role_name)State>>,
682            ) {
683                $register_handlers
684            }
685        };
686
687        Self {
688            main_rs,
689            handlers,
690        }
691    }
692}
693
694pub fn rust_interface_init_state_tokens(
695    db: &mut Database,
696    interface: &Interface,
697) -> rust::Tokens {
698    let init_state_struct = crate::codegen::interface_init_state(db, interface);
699    let init_state_struct_id = db.mproto_db_mut().local_mut().new_type_def(init_state_struct);
700
701    let mproto_cx = &mproto_codegen::codegen::CodegenCx::new_with_type_params(
702        db.mproto_db(), Some("crate::proto"), true, &interface.type_params,
703    );
704
705    quote! {
706        $(mproto_codegen::codegen::rust::rust_type_def(
707            &mproto_cx,
708            db.mproto_db().local().type_def(init_state_struct_id)
709        ))
710    }
711}
712
713pub fn rust_interface_role_config_tokens(
714    db: &mut Database,
715    interface: &Interface,
716    role_name: &str,
717) -> rust::Tokens {
718    let config_struct = crate::codegen::interface_role_config(
719        db,
720        &mproto_codegen::codegen::CodegenCx::new_with_type_params(
721            db.mproto_db(), Some("crate::proto"), true, &interface.type_params,
722        ),
723        interface,
724        role_name,
725    );
726    let config_struct_id = db.mproto_db_mut().local_mut().new_type_def(config_struct);
727
728    quote! {
729        $(mproto_codegen::codegen::rust::rust_type_def(
730            &mproto_codegen::codegen::CodegenCx::new_with_type_params(
731                db.mproto_db(), Some("crate::proto"), true, &interface.type_params,
732            ),
733            db.mproto_db().local().type_def(config_struct_id)
734        ))
735    }
736}