crux_macros/
effect_derive.rs

1use darling::{ast, util, FromDeriveInput, FromField, ToTokens};
2use proc_macro2::{Literal, TokenStream};
3use proc_macro_error::{abort_call_site, OptionExt};
4use quote::{format_ident, quote};
5use std::collections::BTreeMap;
6use syn::{DeriveInput, GenericArgument, Ident, PathArguments, Type};
7
8#[derive(FromDeriveInput, Debug)]
9#[darling(attributes(effect), supports(struct_named))]
10struct EffectStructReceiver {
11    ident: Ident,
12    name: Option<Ident>,
13    data: ast::Data<util::Ignored, EffectFieldReceiver>,
14}
15
16#[derive(FromField, Debug)]
17#[darling(attributes(effect))]
18pub struct EffectFieldReceiver {
19    ident: Option<Ident>,
20    ty: Type,
21    #[darling(default)]
22    skip: bool,
23}
24
25struct Field {
26    capability: Type,
27    variant: Ident,
28    event: Type,
29    skip: bool,
30}
31
32impl From<&EffectFieldReceiver> for Field {
33    fn from(f: &EffectFieldReceiver) -> Self {
34        let (capability, variant, event) = split_on_generic(&f.ty);
35        Field {
36            capability,
37            variant,
38            event,
39            skip: f.skip,
40        }
41    }
42}
43
44impl ToTokens for EffectStructReceiver {
45    #[allow(clippy::too_many_lines)]
46    fn to_tokens(&self, tokens: &mut TokenStream) {
47        let ident = &self.ident;
48
49        let (effect_name, ffi_effect_name, ffi_effect_rename) = match self.name {
50            Some(ref name) => {
51                let ffi_ef_name = format_ident!("{}Ffi", name);
52                let ffi_ef_rename = Literal::string(&name.to_string());
53
54                (quote!(#name), quote!(#ffi_ef_name), quote!(#ffi_ef_rename))
55            }
56            None => (quote!(Effect), quote!(EffectFfi), quote!("Effect")),
57        };
58
59        let fields = self
60            .data
61            .as_ref()
62            .take_struct()
63            .expect_or_abort("should be a struct")
64            .fields;
65
66        let fields: BTreeMap<Ident, Field> = fields
67            .into_iter()
68            .map(|f| (f.ident.clone().unwrap(), f.into()))
69            .collect();
70
71        let events: Vec<_> = fields.values().map(|Field { event, .. }| event).collect();
72        if !events
73            .windows(2)
74            .all(|win| win[0].to_token_stream().to_string() == win[1].to_token_stream().to_string())
75        {
76            abort_call_site!("all fields should be generic over the same event type");
77        }
78        let event = events
79            .first()
80            .expect_or_abort("Capabilities struct has no fields");
81
82        let mut variants = Vec::new();
83        let mut with_context_fields = Vec::new();
84        let mut ffi_variants = Vec::new();
85        let mut match_arms = Vec::new();
86        let mut filters = Vec::new();
87
88        for (
89            field_name,
90            Field {
91                capability,
92                variant,
93                event,
94                skip,
95            },
96        ) in &fields
97        {
98            if *skip {
99                let msg = format!("Requesting effects from capability \"{variant}\" is impossible because it was skipped",);
100                with_context_fields.push(quote! {
101                    #field_name: #capability::new(context.specialize(|_| unreachable!(#msg)))
102                });
103            } else {
104                with_context_fields.push(quote! {
105                    #field_name: #capability::new(context.specialize(#effect_name::#variant))
106                });
107
108                variants.push(quote! {
109                    #variant(::crux_core::Request<<#capability<#event> as ::crux_core::capability::Capability<#event>>::Operation>)
110                });
111
112                ffi_variants.push(quote! { #variant(<#capability<#event> as ::crux_core::capability::Capability<#event>>::Operation) });
113
114                match_arms.push(quote! { #effect_name::#variant(request) => request.serialize(#ffi_effect_name::#variant) });
115
116                let filter_fn = format_ident!("is_{}", field_name);
117                let map_fn = format_ident!("into_{}", field_name);
118                let expect_fn = format_ident!("expect_{}", field_name);
119                let name_as_str = field_name.to_string();
120                filters.push(quote! {
121                    impl #effect_name {
122                        pub fn #filter_fn(&self) -> bool {
123                            if let #effect_name::#variant(_) = self {
124                                true
125                            } else {
126                                false
127                            }
128                        }
129                        pub fn #map_fn(self) -> Option<crux_core::Request<<#capability<#event> as ::crux_core::capability::Capability<#event>>::Operation>> {
130                            if let #effect_name::#variant(request) = self {
131                                Some(request)
132                            } else {
133                                None
134                            }
135                        }
136                        #[track_caller]
137                        pub fn #expect_fn(self) -> crux_core::Request<<#capability<#event> as ::crux_core::capability::Capability<#event>>::Operation> {
138                            if let #effect_name::#variant(request) = self {
139                                request
140                            } else {
141                                panic!("not a {} effect", #name_as_str)
142                            }
143                        }
144                    }
145                    impl From<crux_core::Request<<#capability<#event> as ::crux_core::capability::Capability<#event>>::Operation>> for #effect_name {
146                        fn from(value: crux_core::Request<<#capability<#event> as ::crux_core::capability::Capability<#event>>::Operation>) -> Self {
147                            Self::#variant(value)
148                        }
149                    }
150                });
151            }
152        }
153
154        tokens.extend(quote! {
155            #[derive(Debug)]
156            pub enum #effect_name {
157                #(#variants ,)*
158            }
159
160            #[derive(::serde::Serialize, ::serde::Deserialize)]
161            #[serde(rename = #ffi_effect_rename)]
162            pub enum #ffi_effect_name {
163                #(#ffi_variants ,)*
164            }
165
166            impl ::crux_core::Effect for #effect_name {
167                type Ffi = #ffi_effect_name;
168
169                fn serialize(self) -> (Self::Ffi, ::crux_core::bridge::ResolveSerialized) {
170                    match self {
171                        #(#match_arms ,)*
172                    }
173                }
174            }
175
176            impl ::crux_core::WithContext<#event, #effect_name> for #ident {
177                fn new_with_context(context: ::crux_core::capability::ProtoContext<#effect_name, #event>) -> #ident {
178                    #ident {
179                        #(#with_context_fields ,)*
180                    }
181                }
182            }
183
184            #(#filters)*
185        });
186    }
187}
188
189pub(crate) fn effect_impl(input: &DeriveInput) -> TokenStream {
190    let input = match EffectStructReceiver::from_derive_input(input) {
191        Ok(v) => v,
192        Err(e) => {
193            return e.write_errors();
194        }
195    };
196
197    quote!(#input)
198}
199
200fn split_on_generic(ty: &Type) -> (Type, Ident, Type) {
201    let ty = ty.clone();
202    match ty {
203        Type::Path(mut path) if path.qself.is_none() => {
204            // Get the last segment of the path where the generic parameter should be
205
206            let last = path.path.segments.last_mut().expect("type has no segments");
207            let type_name = last.ident.clone();
208            let type_params = std::mem::take(&mut last.arguments);
209
210            // It should have only one angle-bracketed param
211            let generic_arg = match type_params {
212                PathArguments::AngleBracketed(params) => params.args.first().cloned(),
213                _ => None,
214            };
215
216            // This argument must be a type
217            match generic_arg {
218                Some(GenericArgument::Type(t2)) => Some((Type::Path(path), type_name, t2)),
219                _ => None,
220            }
221        }
222        _ => None,
223    }
224    .expect_or_abort("capabilities should be generic over a single event type")
225}
226
227#[cfg(test)]
228mod tests {
229    use darling::{FromDeriveInput, FromMeta, ToTokens};
230    use quote::quote;
231    use syn::{parse_str, Type};
232
233    use crate::effect_derive::EffectStructReceiver;
234
235    use super::split_on_generic;
236
237    #[test]
238    fn defaults() {
239        let input = r"
240            #[derive(Effect)]
241            pub struct Capabilities {
242                pub render: Render<Event>,
243            }
244        ";
245        let input = parse_str(input).unwrap();
246        let input = EffectStructReceiver::from_derive_input(&input).unwrap();
247
248        let actual = quote!(#input);
249
250        insta::assert_snapshot!(pretty_print(&actual), @r##"
251        #[derive(Debug)]
252        pub enum Effect {
253            Render(
254                ::crux_core::Request<
255                    <Render<Event> as ::crux_core::capability::Capability<Event>>::Operation,
256                >,
257            ),
258        }
259        #[derive(::serde::Serialize, ::serde::Deserialize)]
260        #[serde(rename = "Effect")]
261        pub enum EffectFfi {
262            Render(<Render<Event> as ::crux_core::capability::Capability<Event>>::Operation),
263        }
264        impl ::crux_core::Effect for Effect {
265            type Ffi = EffectFfi;
266            fn serialize(self) -> (Self::Ffi, ::crux_core::bridge::ResolveSerialized) {
267                match self {
268                    Effect::Render(request) => request.serialize(EffectFfi::Render),
269                }
270            }
271        }
272        impl ::crux_core::WithContext<Event, Effect> for Capabilities {
273            fn new_with_context(
274                context: ::crux_core::capability::ProtoContext<Effect, Event>,
275            ) -> Capabilities {
276                Capabilities {
277                    render: Render::new(context.specialize(Effect::Render)),
278                }
279            }
280        }
281        impl Effect {
282            pub fn is_render(&self) -> bool {
283                if let Effect::Render(_) = self { true } else { false }
284            }
285            pub fn into_render(
286                self,
287            ) -> Option<
288                crux_core::Request<
289                    <Render<Event> as ::crux_core::capability::Capability<Event>>::Operation,
290                >,
291            > {
292                if let Effect::Render(request) = self { Some(request) } else { None }
293            }
294            #[track_caller]
295            pub fn expect_render(
296                self,
297            ) -> crux_core::Request<
298                <Render<Event> as ::crux_core::capability::Capability<Event>>::Operation,
299            > {
300                if let Effect::Render(request) = self {
301                    request
302                } else {
303                    panic!("not a {} effect", "render")
304                }
305            }
306        }
307        impl From<
308            crux_core::Request<
309                <Render<Event> as ::crux_core::capability::Capability<Event>>::Operation,
310            >,
311        > for Effect {
312            fn from(
313                value: crux_core::Request<
314                    <Render<Event> as ::crux_core::capability::Capability<Event>>::Operation,
315                >,
316            ) -> Self {
317                Self::Render(value)
318            }
319        }
320        "##);
321    }
322
323    #[test]
324    fn effect_skip() {
325        let input = r"
326            #[derive(Effect)]
327            pub struct Capabilities {
328                pub render: Render<Event>,
329                #[effect(skip)]
330                pub compose: Compose<Event>,
331            }
332        ";
333        let input = parse_str(input).unwrap();
334        let input = EffectStructReceiver::from_derive_input(&input).unwrap();
335
336        let actual = quote!(#input);
337
338        insta::assert_snapshot!(pretty_print(&actual), @r##"
339        #[derive(Debug)]
340        pub enum Effect {
341            Render(
342                ::crux_core::Request<
343                    <Render<Event> as ::crux_core::capability::Capability<Event>>::Operation,
344                >,
345            ),
346        }
347        #[derive(::serde::Serialize, ::serde::Deserialize)]
348        #[serde(rename = "Effect")]
349        pub enum EffectFfi {
350            Render(<Render<Event> as ::crux_core::capability::Capability<Event>>::Operation),
351        }
352        impl ::crux_core::Effect for Effect {
353            type Ffi = EffectFfi;
354            fn serialize(self) -> (Self::Ffi, ::crux_core::bridge::ResolveSerialized) {
355                match self {
356                    Effect::Render(request) => request.serialize(EffectFfi::Render),
357                }
358            }
359        }
360        impl ::crux_core::WithContext<Event, Effect> for Capabilities {
361            fn new_with_context(
362                context: ::crux_core::capability::ProtoContext<Effect, Event>,
363            ) -> Capabilities {
364                Capabilities {
365                    compose: Compose::new(
366                        context
367                            .specialize(|_| {
368                                unreachable!(
369                                    "Requesting effects from capability \"Compose\" is impossible because it was skipped"
370                                )
371                            }),
372                    ),
373                    render: Render::new(context.specialize(Effect::Render)),
374                }
375            }
376        }
377        impl Effect {
378            pub fn is_render(&self) -> bool {
379                if let Effect::Render(_) = self { true } else { false }
380            }
381            pub fn into_render(
382                self,
383            ) -> Option<
384                crux_core::Request<
385                    <Render<Event> as ::crux_core::capability::Capability<Event>>::Operation,
386                >,
387            > {
388                if let Effect::Render(request) = self { Some(request) } else { None }
389            }
390            #[track_caller]
391            pub fn expect_render(
392                self,
393            ) -> crux_core::Request<
394                <Render<Event> as ::crux_core::capability::Capability<Event>>::Operation,
395            > {
396                if let Effect::Render(request) = self {
397                    request
398                } else {
399                    panic!("not a {} effect", "render")
400                }
401            }
402        }
403        impl From<
404            crux_core::Request<
405                <Render<Event> as ::crux_core::capability::Capability<Event>>::Operation,
406            >,
407        > for Effect {
408            fn from(
409                value: crux_core::Request<
410                    <Render<Event> as ::crux_core::capability::Capability<Event>>::Operation,
411                >,
412            ) -> Self {
413                Self::Render(value)
414            }
415        }
416        "##);
417    }
418
419    #[test]
420    #[allow(clippy::too_many_lines)]
421    fn full() {
422        let input = r#"
423            #[derive(Effect)]
424            #[effect(name = "MyEffect")]
425            pub struct MyCapabilities {
426                pub http: crux_http::Http<MyEvent>,
427                pub key_value: KeyValue<MyEvent>,
428                pub platform: Platform<MyEvent>,
429                pub render: Render<MyEvent>,
430                pub time: Time<MyEvent>,
431            }
432        "#;
433        let input = parse_str(input).unwrap();
434        let input = EffectStructReceiver::from_derive_input(&input).unwrap();
435
436        let actual = quote!(#input);
437
438        insta::assert_snapshot!(pretty_print(&actual), @r#"
439        #[derive(Debug)]
440        pub enum MyEffect {
441            Http(
442                ::crux_core::Request<
443                    <crux_http::Http<
444                        MyEvent,
445                    > as ::crux_core::capability::Capability<MyEvent>>::Operation,
446                >,
447            ),
448            KeyValue(
449                ::crux_core::Request<
450                    <KeyValue<
451                        MyEvent,
452                    > as ::crux_core::capability::Capability<MyEvent>>::Operation,
453                >,
454            ),
455            Platform(
456                ::crux_core::Request<
457                    <Platform<
458                        MyEvent,
459                    > as ::crux_core::capability::Capability<MyEvent>>::Operation,
460                >,
461            ),
462            Render(
463                ::crux_core::Request<
464                    <Render<MyEvent> as ::crux_core::capability::Capability<MyEvent>>::Operation,
465                >,
466            ),
467            Time(
468                ::crux_core::Request<
469                    <Time<MyEvent> as ::crux_core::capability::Capability<MyEvent>>::Operation,
470                >,
471            ),
472        }
473        #[derive(::serde::Serialize, ::serde::Deserialize)]
474        #[serde(rename = "MyEffect")]
475        pub enum MyEffectFfi {
476            Http(
477                <crux_http::Http<
478                    MyEvent,
479                > as ::crux_core::capability::Capability<MyEvent>>::Operation,
480            ),
481            KeyValue(
482                <KeyValue<MyEvent> as ::crux_core::capability::Capability<MyEvent>>::Operation,
483            ),
484            Platform(
485                <Platform<MyEvent> as ::crux_core::capability::Capability<MyEvent>>::Operation,
486            ),
487            Render(<Render<MyEvent> as ::crux_core::capability::Capability<MyEvent>>::Operation),
488            Time(<Time<MyEvent> as ::crux_core::capability::Capability<MyEvent>>::Operation),
489        }
490        impl ::crux_core::Effect for MyEffect {
491            type Ffi = MyEffectFfi;
492            fn serialize(self) -> (Self::Ffi, ::crux_core::bridge::ResolveSerialized) {
493                match self {
494                    MyEffect::Http(request) => request.serialize(MyEffectFfi::Http),
495                    MyEffect::KeyValue(request) => request.serialize(MyEffectFfi::KeyValue),
496                    MyEffect::Platform(request) => request.serialize(MyEffectFfi::Platform),
497                    MyEffect::Render(request) => request.serialize(MyEffectFfi::Render),
498                    MyEffect::Time(request) => request.serialize(MyEffectFfi::Time),
499                }
500            }
501        }
502        impl ::crux_core::WithContext<MyEvent, MyEffect> for MyCapabilities {
503            fn new_with_context(
504                context: ::crux_core::capability::ProtoContext<MyEffect, MyEvent>,
505            ) -> MyCapabilities {
506                MyCapabilities {
507                    http: crux_http::Http::new(context.specialize(MyEffect::Http)),
508                    key_value: KeyValue::new(context.specialize(MyEffect::KeyValue)),
509                    platform: Platform::new(context.specialize(MyEffect::Platform)),
510                    render: Render::new(context.specialize(MyEffect::Render)),
511                    time: Time::new(context.specialize(MyEffect::Time)),
512                }
513            }
514        }
515        impl MyEffect {
516            pub fn is_http(&self) -> bool {
517                if let MyEffect::Http(_) = self { true } else { false }
518            }
519            pub fn into_http(
520                self,
521            ) -> Option<
522                crux_core::Request<
523                    <crux_http::Http<
524                        MyEvent,
525                    > as ::crux_core::capability::Capability<MyEvent>>::Operation,
526                >,
527            > {
528                if let MyEffect::Http(request) = self { Some(request) } else { None }
529            }
530            #[track_caller]
531            pub fn expect_http(
532                self,
533            ) -> crux_core::Request<
534                <crux_http::Http<
535                    MyEvent,
536                > as ::crux_core::capability::Capability<MyEvent>>::Operation,
537            > {
538                if let MyEffect::Http(request) = self {
539                    request
540                } else {
541                    panic!("not a {} effect", "http")
542                }
543            }
544        }
545        impl From<
546            crux_core::Request<
547                <crux_http::Http<
548                    MyEvent,
549                > as ::crux_core::capability::Capability<MyEvent>>::Operation,
550            >,
551        > for MyEffect {
552            fn from(
553                value: crux_core::Request<
554                    <crux_http::Http<
555                        MyEvent,
556                    > as ::crux_core::capability::Capability<MyEvent>>::Operation,
557                >,
558            ) -> Self {
559                Self::Http(value)
560            }
561        }
562        impl MyEffect {
563            pub fn is_key_value(&self) -> bool {
564                if let MyEffect::KeyValue(_) = self { true } else { false }
565            }
566            pub fn into_key_value(
567                self,
568            ) -> Option<
569                crux_core::Request<
570                    <KeyValue<
571                        MyEvent,
572                    > as ::crux_core::capability::Capability<MyEvent>>::Operation,
573                >,
574            > {
575                if let MyEffect::KeyValue(request) = self { Some(request) } else { None }
576            }
577            #[track_caller]
578            pub fn expect_key_value(
579                self,
580            ) -> crux_core::Request<
581                <KeyValue<MyEvent> as ::crux_core::capability::Capability<MyEvent>>::Operation,
582            > {
583                if let MyEffect::KeyValue(request) = self {
584                    request
585                } else {
586                    panic!("not a {} effect", "key_value")
587                }
588            }
589        }
590        impl From<
591            crux_core::Request<
592                <KeyValue<MyEvent> as ::crux_core::capability::Capability<MyEvent>>::Operation,
593            >,
594        > for MyEffect {
595            fn from(
596                value: crux_core::Request<
597                    <KeyValue<
598                        MyEvent,
599                    > as ::crux_core::capability::Capability<MyEvent>>::Operation,
600                >,
601            ) -> Self {
602                Self::KeyValue(value)
603            }
604        }
605        impl MyEffect {
606            pub fn is_platform(&self) -> bool {
607                if let MyEffect::Platform(_) = self { true } else { false }
608            }
609            pub fn into_platform(
610                self,
611            ) -> Option<
612                crux_core::Request<
613                    <Platform<
614                        MyEvent,
615                    > as ::crux_core::capability::Capability<MyEvent>>::Operation,
616                >,
617            > {
618                if let MyEffect::Platform(request) = self { Some(request) } else { None }
619            }
620            #[track_caller]
621            pub fn expect_platform(
622                self,
623            ) -> crux_core::Request<
624                <Platform<MyEvent> as ::crux_core::capability::Capability<MyEvent>>::Operation,
625            > {
626                if let MyEffect::Platform(request) = self {
627                    request
628                } else {
629                    panic!("not a {} effect", "platform")
630                }
631            }
632        }
633        impl From<
634            crux_core::Request<
635                <Platform<MyEvent> as ::crux_core::capability::Capability<MyEvent>>::Operation,
636            >,
637        > for MyEffect {
638            fn from(
639                value: crux_core::Request<
640                    <Platform<
641                        MyEvent,
642                    > as ::crux_core::capability::Capability<MyEvent>>::Operation,
643                >,
644            ) -> Self {
645                Self::Platform(value)
646            }
647        }
648        impl MyEffect {
649            pub fn is_render(&self) -> bool {
650                if let MyEffect::Render(_) = self { true } else { false }
651            }
652            pub fn into_render(
653                self,
654            ) -> Option<
655                crux_core::Request<
656                    <Render<MyEvent> as ::crux_core::capability::Capability<MyEvent>>::Operation,
657                >,
658            > {
659                if let MyEffect::Render(request) = self { Some(request) } else { None }
660            }
661            #[track_caller]
662            pub fn expect_render(
663                self,
664            ) -> crux_core::Request<
665                <Render<MyEvent> as ::crux_core::capability::Capability<MyEvent>>::Operation,
666            > {
667                if let MyEffect::Render(request) = self {
668                    request
669                } else {
670                    panic!("not a {} effect", "render")
671                }
672            }
673        }
674        impl From<
675            crux_core::Request<
676                <Render<MyEvent> as ::crux_core::capability::Capability<MyEvent>>::Operation,
677            >,
678        > for MyEffect {
679            fn from(
680                value: crux_core::Request<
681                    <Render<MyEvent> as ::crux_core::capability::Capability<MyEvent>>::Operation,
682                >,
683            ) -> Self {
684                Self::Render(value)
685            }
686        }
687        impl MyEffect {
688            pub fn is_time(&self) -> bool {
689                if let MyEffect::Time(_) = self { true } else { false }
690            }
691            pub fn into_time(
692                self,
693            ) -> Option<
694                crux_core::Request<
695                    <Time<MyEvent> as ::crux_core::capability::Capability<MyEvent>>::Operation,
696                >,
697            > {
698                if let MyEffect::Time(request) = self { Some(request) } else { None }
699            }
700            #[track_caller]
701            pub fn expect_time(
702                self,
703            ) -> crux_core::Request<
704                <Time<MyEvent> as ::crux_core::capability::Capability<MyEvent>>::Operation,
705            > {
706                if let MyEffect::Time(request) = self {
707                    request
708                } else {
709                    panic!("not a {} effect", "time")
710                }
711            }
712        }
713        impl From<
714            crux_core::Request<
715                <Time<MyEvent> as ::crux_core::capability::Capability<MyEvent>>::Operation,
716            >,
717        > for MyEffect {
718            fn from(
719                value: crux_core::Request<
720                    <Time<MyEvent> as ::crux_core::capability::Capability<MyEvent>>::Operation,
721                >,
722            ) -> Self {
723                Self::Time(value)
724            }
725        }
726        "#);
727    }
728
729    #[test]
730    #[allow(clippy::should_panic_without_expect)]
731    #[should_panic]
732    fn should_panic_when_multiple_event_types() {
733        let input = r"
734            #[derive(Effect)]
735            pub struct Capabilities {
736                pub render: Render<MyEvent>,
737                pub time: Time<YourEvent>,
738            }
739        ";
740        let input = parse_str(input).unwrap();
741        let input = EffectStructReceiver::from_derive_input(&input).unwrap();
742
743        let mut actual = quote!();
744        input.to_tokens(&mut actual);
745    }
746
747    fn pretty_print(ts: &proc_macro2::TokenStream) -> String {
748        let file = syn::parse_file(&ts.to_string()).unwrap();
749        prettyplease::unparse(&file)
750    }
751
752    #[test]
753    fn split_event_types_preserves_path() {
754        let ty = Type::from_string("crux_core::render::Render<Event>").unwrap();
755
756        let (actual_type, actual_ident, actual_event) = split_on_generic(&ty);
757
758        assert_eq!(
759            quote!(#actual_type).to_string(),
760            quote!(crux_core::render::Render).to_string()
761        );
762
763        assert_eq!(
764            quote!(#actual_ident).to_string(),
765            quote!(Render).to_string()
766        );
767
768        assert_eq!(quote!(#actual_event).to_string(), quote!(Event).to_string());
769    }
770}