fedimint_core/
macros.rs

1/// Define "dyn newtype" (a newtype over `dyn Trait`)
2///
3/// This is a simple pattern that make working with `dyn Trait`s
4/// easier, by hiding their details.
5///
6/// A "dyn newtype" `Deref`s to the underlying `&dyn Trait`, making
7/// it easy to access the encapsulated operations, while hiding
8/// the boxing details.
9#[macro_export]
10macro_rules! dyn_newtype_define {
11    (   $(#[$outer:meta])*
12        $vis:vis $name:ident<$lifetime:lifetime>(Box<$trait:ident>)
13    ) => {
14        $crate::_dyn_newtype_define_inner!{
15            $(#[$outer])*
16            $vis $name<$lifetime>(Box<$trait>)
17        }
18        $crate::_dyn_newtype_impl_deref_mut!($name<$lifetime>);
19    };
20    (   $(#[$outer:meta])*
21        $vis:vis $name:ident(Box<$trait:ident>)
22    ) => {
23        $crate::_dyn_newtype_define_inner!{
24            $(#[$outer])*
25            $vis $name(Box<$trait>)
26        }
27        $crate::_dyn_newtype_impl_deref_mut!($name);
28    };
29    (   $(#[$outer:meta])*
30        $vis:vis $name:ident<$lifetime:lifetime>(Arc<$trait:ident>)
31    ) => {
32        $crate::_dyn_newtype_define_inner!{
33            $(#[$outer])*
34            $vis $name<$lifetime>(Arc<$trait>)
35        }
36    };
37    (   $(#[$outer:meta])*
38        $vis:vis $name:ident(Arc<$trait:ident>)
39    ) => {
40        $crate::_dyn_newtype_define_inner!{
41            $(#[$outer])*
42            $vis $name(Arc<$trait>)
43        }
44    };
45}
46
47#[macro_export]
48macro_rules! _dyn_newtype_define_inner {
49    (   $(#[$outer:meta])*
50        $vis:vis $name:ident($container:ident<$trait:ident>)
51    ) => {
52        $(#[$outer])*
53        $vis struct $name { inner: $container<$crate::maybe_add_send_sync!(dyn $trait + 'static)> }
54
55        impl std::ops::Deref for $name {
56            type Target = $crate::maybe_add_send_sync!(dyn $trait + 'static);
57
58            fn deref(&self) -> &<Self as std::ops::Deref>::Target {
59                &*self.inner
60            }
61
62        }
63
64        impl $name {
65            pub fn get_mut(&mut self) -> Option<&mut <Self as std::ops::Deref>::Target> {
66                Arc::get_mut(&mut self.inner)
67            }
68        }
69
70        impl<I> From<I> for $name
71        where
72            I: $trait + $crate::task::MaybeSend + $crate::task::MaybeSync + 'static,
73        {
74            fn from(i: I) -> Self {
75                Self { inner: $container::new(i) }
76            }
77        }
78
79        impl std::fmt::Debug for $name {
80            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
81                std::fmt::Debug::fmt(&self.inner, f)
82            }
83        }
84    };
85    (   $(#[$outer:meta])*
86        $vis:vis $name:ident<$lifetime:lifetime>($container:ident<$trait:ident>)
87    ) => {
88        $(#[$outer])*
89        $vis struct $name<$lifetime> { inner: $container<dyn $trait<$lifetime> + Send + $lifetime> }
90
91        impl<$lifetime> std::ops::Deref for $name<$lifetime> {
92            type Target = $crate::maybe_add_send!(dyn $trait<$lifetime> + $lifetime);
93
94            fn deref(&self) -> &<Self as std::ops::Deref>::Target {
95                &*self.inner
96            }
97        }
98
99        impl<$lifetime, I> From<I> for $name<$lifetime>
100        where
101            I: $trait<$lifetime> + $crate::task::MaybeSend + $lifetime,
102        {
103            fn from(i: I) -> Self {
104                Self($container::new(i))
105            }
106        }
107    };
108}
109
110/// Implements the `Display` trait for dyn newtypes whose traits implement
111/// `Display`
112#[macro_export]
113macro_rules! dyn_newtype_display_passthrough {
114    ($newtype:ty) => {
115        impl std::fmt::Display for $newtype {
116            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
117                std::fmt::Display::fmt(&self.inner, f)
118            }
119        }
120    };
121}
122
123/// Define a "module plugin dyn-newtype" which is like a standard "dyn newtype",
124/// but with associated "module_instance_id".
125#[macro_export]
126macro_rules! module_plugin_dyn_newtype_define{
127    (   $(#[$outer:meta])*
128        $vis:vis $name:ident<$lifetime:lifetime>(Box<$trait:ident>)
129    ) => {
130        $crate::_dyn_newtype_define_with_instance_id_inner!{
131            $(#[$outer])*
132            $vis $name<$lifetime>(Box<$trait>)
133        }
134        $crate::_dyn_newtype_impl_deref_mut!($name<$lifetime>);
135    };
136    (   $(#[$outer:meta])*
137        $vis:vis $name:ident(Box<$trait:ident>)
138    ) => {
139        $crate::_dyn_newtype_define_with_instance_id_inner!{
140            $(#[$outer])*
141            $vis $name(Box<$trait>)
142        }
143        $crate::_dyn_newtype_impl_deref_mut!($name);
144    };
145    (   $(#[$outer:meta])*
146        $vis:vis $name:ident<$lifetime:lifetime>(Arc<$trait:ident>)
147    ) => {
148        $crate::_dyn_newtype_define_with_instance_id_inner!{
149            $(#[$outer])*
150            $vis $name<$lifetime>(Arc<$trait>)
151        }
152    };
153    (   $(#[$outer:meta])*
154        $vis:vis $name:ident(Arc<$trait:ident>)
155    ) => {
156        $crate::_dyn_newtype_define_with_instance_id_inner!{
157            $(#[$outer])*
158            $vis $name(Arc<$trait>)
159        }
160    };
161}
162
163#[macro_export]
164macro_rules! _dyn_newtype_define_with_instance_id_inner {
165    (   $(#[$outer:meta])*
166        $vis:vis $name:ident($container:ident<$trait:ident>)
167    ) => {
168        $(#[$outer])*
169        $vis struct $name {
170            module_instance_id: $crate::core::ModuleInstanceId,
171            inner: $container<$crate::maybe_add_send_sync!(dyn $trait + 'static)>,
172        }
173
174        impl std::ops::Deref for $name {
175            type Target = $crate::maybe_add_send_sync!(dyn $trait + 'static);
176
177            fn deref(&self) -> &<Self as std::ops::Deref>::Target {
178                &*self.inner
179            }
180
181        }
182
183        impl $name {
184            pub fn module_instance_id(&self) -> ::fedimint_core::core::ModuleInstanceId {
185                self.module_instance_id
186            }
187
188            pub fn from_typed<I>(
189                module_instance_id: ::fedimint_core::core::ModuleInstanceId,
190                typed: I
191            ) -> Self
192            where
193                I: $trait + $crate::task::MaybeSend + $crate::task::MaybeSync + 'static {
194
195                Self { inner: $container::new(typed), module_instance_id }
196            }
197
198            pub fn from_parts(
199                module_instance_id: $crate::core::ModuleInstanceId,
200                dynbox: $container<$crate::maybe_add_send_sync!(dyn $trait + 'static)>
201            ) -> Self {
202                Self { inner: dynbox, module_instance_id }
203            }
204        }
205
206        impl std::fmt::Debug for $name {
207            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
208                let mut d = f.debug_struct(stringify!($name));
209                d.field("id", &self.module_instance_id);
210                if let Some(kind) = self.module_kind() {
211                    d.field("kind", &kind);
212                } else {
213                    d.field("kind", &"?");
214
215                }
216                d
217                    .field("inner", &self.inner)
218                    .finish()
219            }
220        }
221    };
222    (   $(#[$outer:meta])*
223        $vis:vis $name:ident<$lifetime:lifetime>($container:ident<$trait:ident>)
224    ) => {
225        $(#[$outer])*
226        $vis struct $name<$lifetime>{ inner: $container<dyn $trait<$lifetime> + Send + $lifetime>, module_instance_id: ModuleInstanceId }
227
228        impl $name {
229            pub fn module_instance_id(&self) -> ::fedimint_core::core::ModuleInstanceId {
230                self.1
231            }
232
233            pub fn from_typed<I>(module_instance_id: ::fedimint_core::core::ModuleInstanceId, typed: I) -> Self
234            where
235                I: $trait + $crate::task::MaybeSend + $crate::task::MaybeSync + 'static {
236
237                Self { inner: $container::new(typed), module_instance_id }
238            }
239        }
240
241        impl<$lifetime> std::ops::Deref for $name<$lifetime> {
242            type Target = $crate::maybe_add_send_sync!(dyn $trait + 'static);
243
244            fn deref(&self) -> &<Self as std::ops::Deref>::Target {
245                &*self.inner
246            }
247        }
248    };
249}
250
251#[macro_export]
252macro_rules! _dyn_newtype_impl_deref_mut {
253    ($name:ident<$lifetime:lifetime>) => {
254        impl<$lifetime> std::ops::DerefMut for $name<$lifetime> {
255            fn deref_mut(&mut self) -> &mut <Self as std::ops::Deref>::Target {
256                &mut *self.inner
257            }
258        }
259    };
260    ($name:ident) => {
261        impl std::ops::DerefMut for $name {
262            fn deref_mut(&mut self) -> &mut <Self as std::ops::Deref>::Target {
263                &mut *self.inner
264            }
265        }
266    };
267}
268
269/// Implement `Clone` on a "dyn newtype"
270///
271/// ... by calling `clone` method on the underlying
272/// `dyn Trait`.
273///
274/// Cloning `dyn Trait`s is non trivial due to object-safety.
275///
276/// Note: the underlying `dyn Trait` needs to implement
277/// a `fn clone(&self) -> Newtype` for this to work,
278/// and this macro does not check or do anything about it.
279///
280/// If the newtype is using `Arc` you probably want
281/// to just use standard `#[derive(Clone)]` to clone
282/// the `Arc` itself.
283#[macro_export]
284macro_rules! dyn_newtype_impl_dyn_clone_passthrough {
285    ($name:ident) => {
286        impl Clone for $name {
287            fn clone(&self) -> Self {
288                self.0.clone()
289            }
290        }
291    };
292}
293
294#[macro_export]
295macro_rules! module_plugin_dyn_newtype_clone_passthrough {
296    ($name:ident) => {
297        impl Clone for $name {
298            fn clone(&self) -> Self {
299                self.inner.clone(self.module_instance_id)
300            }
301        }
302    };
303}
304
305/// Implement `Encodable` and `Decodable` for a "module dyn newtype"
306///
307/// "Module dyn newtype" is just a "dyn newtype" used by general purpose
308/// Fedimint code to abstract away details of mint modules.
309#[macro_export]
310macro_rules! module_plugin_dyn_newtype_encode_decode {
311    ($name:ident) => {
312        impl Encodable for $name {
313            fn consensus_encode<W: std::io::Write>(
314                &self,
315                writer: &mut W,
316            ) -> Result<usize, std::io::Error> {
317                let mut written = self.module_instance_id.consensus_encode(writer)?;
318
319                let mut buf = Vec::with_capacity(512);
320                let buf_written = self.inner.consensus_encode_dyn(&mut buf)?;
321                assert_eq!(buf.len(), buf_written);
322
323                written += buf.consensus_encode(writer)?;
324
325                Ok(written)
326            }
327        }
328
329        impl Decodable for $name {
330            fn consensus_decode_partial_from_finite_reader<R: std::io::Read>(
331                reader: &mut R,
332                decoders: &$crate::module::registry::ModuleDecoderRegistry,
333            ) -> Result<Self, fedimint_core::encoding::DecodeError> {
334                let module_instance_id =
335                    fedimint_core::core::ModuleInstanceId::consensus_decode_partial_from_finite_reader(
336                        reader, decoders,
337                    )?;
338                let val = match decoders.get(module_instance_id) {
339                    Some(decoder) => {
340                        let total_len_u64 =
341                            u64::consensus_decode_partial_from_finite_reader(reader, decoders)?;
342                        decoder.decode_complete(
343                            reader,
344                            total_len_u64,
345                            module_instance_id,
346                            decoders,
347                        )?
348                    }
349                    None => match decoders.decoding_mode() {
350                        $crate::module::registry::DecodingMode::Reject => {
351                            return Err(fedimint_core::encoding::DecodeError::new_custom(
352                                anyhow::anyhow!(
353                                    "Module decoder not available for module instance: {module_instance_id} when decoding {}", std::any::type_name::<Self>()
354                                ),
355                            ));
356                        }
357                        $crate::module::registry::DecodingMode::Fallback => $name::from_typed(
358                            module_instance_id,
359                            $crate::core::DynUnknown(
360                                Vec::<u8>::consensus_decode_partial_from_finite_reader(
361                                    reader,
362                                    &Default::default(),
363                                )?,
364                            ),
365                        ),
366                    },
367                };
368
369                Ok(val)
370            }
371        }
372    };
373}
374
375/// Define a "plugin" trait
376///
377/// "Plugin trait" is a trait that a developer of a mint module
378/// needs to implement when implementing mint module. It uses associated
379/// types with trait bounds to guide the developer.
380///
381/// Blanket implementations are used to convert the "plugin trait",
382/// incompatible with `dyn Trait` into "module types" and corresponding
383/// "module dyn newtypes", erasing the exact type and used in a common
384/// Fedimint code.
385#[macro_export]
386macro_rules! module_plugin_static_trait_define{
387    (   $(#[$outer:meta])*
388        $dyn_newtype:ident, $static_trait:ident, $dyn_trait:ident, { $($extra_methods:tt)* }, { $($extra_impls:tt)* }
389    ) => {
390        pub trait $static_trait:
391            std::fmt::Debug + std::fmt::Display + std::cmp::PartialEq + std::hash::Hash + DynEncodable + Decodable + Encodable + Clone + IntoDynInstance<DynType = $dyn_newtype> + Send + Sync + 'static
392        {
393            const KIND : ModuleKind;
394
395            $($extra_methods)*
396        }
397
398        impl $dyn_trait for ::fedimint_core::core::DynUnknown {
399            fn as_any(&self) -> &(dyn Any + Send + Sync) {
400                self
401            }
402
403            fn module_kind(&self) -> Option<ModuleKind> {
404                None
405            }
406
407            fn clone(&self, instance_id: ::fedimint_core::core::ModuleInstanceId) -> $dyn_newtype {
408                $dyn_newtype::from_typed(instance_id, <Self as Clone>::clone(self))
409            }
410
411            fn dyn_hash(&self) -> u64 {
412                use std::hash::Hash;
413                let mut s = std::collections::hash_map::DefaultHasher::new();
414                self.hash(&mut s);
415                std::hash::Hasher::finish(&s)
416            }
417
418            $($extra_impls)*
419        }
420
421        impl<T> $dyn_trait for T
422        where
423            T: $static_trait + DynEncodable + 'static + Send + Sync,
424        {
425            fn as_any(&self) -> &(dyn Any + Send + Sync) {
426                self
427            }
428
429            fn module_kind(&self) -> Option<ModuleKind> {
430                Some(<Self as $static_trait>::KIND)
431            }
432
433            fn clone(&self, instance_id: ::fedimint_core::core::ModuleInstanceId) -> $dyn_newtype {
434                $dyn_newtype::from_typed(instance_id, <Self as Clone>::clone(self))
435            }
436
437            fn dyn_hash(&self) -> u64 {
438                let mut s = std::collections::hash_map::DefaultHasher::new();
439                self.hash(&mut s);
440                std::hash::Hasher::finish(&s)
441            }
442
443            $($extra_impls)*
444        }
445
446        impl std::hash::Hash for $dyn_newtype {
447            fn hash<H>(&self, state: &mut H)
448            where
449                H: std::hash::Hasher
450            {
451                self.module_instance_id.hash(state);
452                self.inner.dyn_hash().hash(state);
453            }
454        }
455    };
456}
457
458/// A copy of `module_lugin_static_trait_define` but for `ClientConfig`.
459///
460/// `ClientConfig` is a snowflake that requires `: Serialize` and conditional
461/// implementation for `DynUnknown`. The macro is getting gnarly, so seems
462/// easier to copy-paste-modify, than pile up conditional argument.
463#[macro_export]
464macro_rules! module_plugin_static_trait_define_config{
465    (   $(#[$outer:meta])*
466        $dyn_newtype:ident, $static_trait:ident, $dyn_trait:ident, { $($extra_methods:tt)* }, { $($extra_impls:tt)* }, { $($extra_impls_unknown:tt)* }
467    ) => {
468        pub trait $static_trait:
469            std::fmt::Debug + std::fmt::Display + std::cmp::PartialEq + std::hash::Hash + DynEncodable + Decodable + Encodable + Clone + IntoDynInstance<DynType = $dyn_newtype> + Send + Sync + serde::Serialize + serde::de::DeserializeOwned + 'static
470        {
471            const KIND : ::fedimint_core::core::ModuleKind;
472            $($extra_methods)*
473        }
474
475        impl $dyn_trait for ::fedimint_core::core::DynUnknown {
476            fn as_any(&self) -> &(dyn Any + Send + Sync) {
477                self
478            }
479
480            fn module_kind(&self) -> Option<::fedimint_core::core::ModuleKind> {
481                None
482            }
483
484            fn clone(&self, instance_id: ::fedimint_core::core::ModuleInstanceId) -> $dyn_newtype {
485                $dyn_newtype::from_typed(instance_id, <Self as Clone>::clone(self))
486            }
487
488            fn dyn_hash(&self) -> u64 {
489                use std::hash::Hash;
490                let mut s = std::collections::hash_map::DefaultHasher::new();
491                self.hash(&mut s);
492                std::hash::Hasher::finish(&s)
493            }
494
495            $($extra_impls_unknown)*
496        }
497
498        impl<T> $dyn_trait for T
499        where
500            T: $static_trait + DynEncodable + 'static + Send + Sync,
501        {
502            fn as_any(&self) -> &(dyn Any + Send + Sync) {
503                self
504            }
505
506            fn module_kind(&self) -> Option<::fedimint_core::core::ModuleKind> {
507                Some(<T as $static_trait>::KIND)
508            }
509
510            fn clone(&self, instance_id: ::fedimint_core::core::ModuleInstanceId) -> $dyn_newtype {
511                $dyn_newtype::from_typed(instance_id, <Self as Clone>::clone(self))
512            }
513
514            fn dyn_hash(&self) -> u64 {
515                let mut s = std::collections::hash_map::DefaultHasher::new();
516                self.hash(&mut s);
517                std::hash::Hasher::finish(&s)
518            }
519
520            $($extra_impls)*
521        }
522
523        impl std::hash::Hash for $dyn_newtype {
524            fn hash<H>(&self, state: &mut H)
525            where
526                H: std::hash::Hasher
527            {
528                self.module_instance_id.hash(state);
529                self.inner.dyn_hash().hash(state);
530            }
531        }
532    };
533}
534
535/// Implements the necessary traits for all configuration related types of a
536/// `FederationServer` module.
537#[macro_export]
538macro_rules! plugin_types_trait_impl_config {
539    ($common_gen:ty, $gen:ty, $gen_local:ty, $gen_consensus:ty, $cfg:ty, $cfg_local:ty, $cfg_private:ty, $cfg_consensus:ty, $cfg_client:ty) => {
540        impl fedimint_core::config::ModuleInitParams for $gen {
541            type Local = $gen_local;
542            type Consensus = $gen_consensus;
543
544            fn from_parts(local: Self::Local, consensus: Self::Consensus) -> Self {
545                Self { local, consensus }
546            }
547
548            fn to_parts(self) -> (Self::Local, Self::Consensus) {
549                (self.local, self.consensus)
550            }
551        }
552
553        impl fedimint_core::config::TypedServerModuleConsensusConfig for $cfg_consensus {
554            fn kind(&self) -> fedimint_core::core::ModuleKind {
555                <$common_gen as fedimint_core::module::CommonModuleInit>::KIND
556            }
557
558            fn version(&self) -> fedimint_core::module::ModuleConsensusVersion {
559                <$common_gen as fedimint_core::module::CommonModuleInit>::CONSENSUS_VERSION
560            }
561        }
562
563        impl fedimint_core::config::TypedServerModuleConfig for $cfg {
564            type Local = $cfg_local;
565            type Private = $cfg_private;
566            type Consensus = $cfg_consensus;
567
568            fn from_parts(
569                local: Self::Local,
570                private: Self::Private,
571                consensus: Self::Consensus,
572            ) -> Self {
573                Self {
574                    local,
575                    private,
576                    consensus,
577                }
578            }
579
580            fn to_parts(self) -> (ModuleKind, Self::Local, Self::Private, Self::Consensus) {
581                (
582                    <$common_gen as fedimint_core::module::CommonModuleInit>::KIND,
583                    self.local,
584                    self.private,
585                    self.consensus,
586                )
587            }
588        }
589    };
590}
591
592/// Implements the necessary traits for all associated types of a
593/// `FederationServer` module.
594#[macro_export]
595macro_rules! plugin_types_trait_impl_common {
596    ($kind:expr, $types:ty, $client_config:ty, $input:ty, $output:ty, $outcome:ty, $ci:ty, $input_error:ty, $output_error:ty) => {
597        impl fedimint_core::module::ModuleCommon for $types {
598            type ClientConfig = $client_config;
599            type Input = $input;
600            type Output = $output;
601            type OutputOutcome = $outcome;
602            type ConsensusItem = $ci;
603            type InputError = $input_error;
604            type OutputError = $output_error;
605        }
606
607        impl fedimint_core::core::ClientConfig for $client_config {
608            const KIND: ModuleKind = $kind;
609        }
610
611        impl fedimint_core::core::IntoDynInstance for $client_config {
612            type DynType = fedimint_core::core::DynClientConfig;
613
614            fn into_dyn(self, instance_id: ModuleInstanceId) -> Self::DynType {
615                fedimint_core::core::DynClientConfig::from_typed(instance_id, self)
616            }
617        }
618
619        impl fedimint_core::core::Input for $input {
620            const KIND: ModuleKind = $kind;
621        }
622
623        impl fedimint_core::core::IntoDynInstance for $input {
624            type DynType = fedimint_core::core::DynInput;
625
626            fn into_dyn(self, instance_id: ModuleInstanceId) -> Self::DynType {
627                fedimint_core::core::DynInput::from_typed(instance_id, self)
628            }
629        }
630
631        impl fedimint_core::core::Output for $output {
632            const KIND: ModuleKind = $kind;
633        }
634
635        impl fedimint_core::core::IntoDynInstance for $output {
636            type DynType = fedimint_core::core::DynOutput;
637
638            fn into_dyn(self, instance_id: ModuleInstanceId) -> Self::DynType {
639                fedimint_core::core::DynOutput::from_typed(instance_id, self)
640            }
641        }
642
643        impl fedimint_core::core::OutputOutcome for $outcome {
644            const KIND: ModuleKind = $kind;
645        }
646
647        impl fedimint_core::core::IntoDynInstance for $outcome {
648            type DynType = fedimint_core::core::DynOutputOutcome;
649
650            fn into_dyn(self, instance_id: ModuleInstanceId) -> Self::DynType {
651                fedimint_core::core::DynOutputOutcome::from_typed(instance_id, self)
652            }
653        }
654
655        impl fedimint_core::core::ModuleConsensusItem for $ci {
656            const KIND: ModuleKind = $kind;
657        }
658
659        impl fedimint_core::core::IntoDynInstance for $ci {
660            type DynType = fedimint_core::core::DynModuleConsensusItem;
661
662            fn into_dyn(self, instance_id: ModuleInstanceId) -> Self::DynType {
663                fedimint_core::core::DynModuleConsensusItem::from_typed(instance_id, self)
664            }
665        }
666
667        impl fedimint_core::core::InputError for $input_error {
668            const KIND: ModuleKind = $kind;
669        }
670
671        impl fedimint_core::core::IntoDynInstance for $input_error {
672            type DynType = fedimint_core::core::DynInputError;
673
674            fn into_dyn(self, instance_id: ModuleInstanceId) -> Self::DynType {
675                fedimint_core::core::DynInputError::from_typed(instance_id, self)
676            }
677        }
678
679        impl fedimint_core::core::OutputError for $output_error {
680            const KIND: ModuleKind = $kind;
681        }
682
683        impl fedimint_core::core::IntoDynInstance for $output_error {
684            type DynType = fedimint_core::core::DynOutputError;
685
686            fn into_dyn(self, instance_id: ModuleInstanceId) -> Self::DynType {
687                fedimint_core::core::DynOutputError::from_typed(instance_id, self)
688            }
689        }
690    };
691}
692
693#[macro_export]
694macro_rules! erased_eq_no_instance_id {
695    ($newtype:ty) => {
696        fn erased_eq_no_instance_id(&self, other: &$newtype) -> bool {
697            let other: &Self = other
698                .as_any()
699                .downcast_ref()
700                .expect("Type is ensured in previous step");
701
702            self == other
703        }
704    };
705}
706
707#[macro_export]
708macro_rules! module_plugin_dyn_newtype_eq_passthrough {
709    ($newtype:ty) => {
710        impl PartialEq for $newtype {
711            fn eq(&self, other: &Self) -> bool {
712                if self.module_instance_id != other.module_instance_id {
713                    return false;
714                }
715                self.erased_eq_no_instance_id(other)
716            }
717        }
718
719        impl Eq for $newtype {}
720    };
721}
722
723#[macro_export]
724macro_rules! module_plugin_dyn_newtype_display_passthrough {
725    ($newtype:ty) => {
726        impl std::fmt::Display for $newtype {
727            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
728                f.write_fmt(format_args!("{}-{}", self.module_instance_id, self.inner))
729            }
730        }
731    };
732}
733
734// TODO: use concat_ident for error name once it lands in stable, see https://github.com/rust-lang/rust/issues/29599
735/// Macro for defining module associated types.
736///
737/// Wraps a type into an enum with a default variant, this allows to add new
738/// versions of the type in the future. Depending on context unknown versions
739/// may be ignored or lead to errors. E.g. the client might just ignore an
740/// unknown input version since it cannot originate from itself while the server
741/// would reject it for not being able to validate its correctness.
742///
743/// Adding extensibility this way is a last line of defense against breaking
744/// changes, most often other ways of introducing new functionality should be
745/// preferred (e.g. new module versions, pure client-side changes, …).
746#[macro_export]
747macro_rules! extensible_associated_module_type {
748    ($name:ident, $name_v0:ident, $error:ident) => {
749        #[derive(
750            Clone,
751            Eq,
752            PartialEq,
753            Hash,
754            serde::Deserialize,
755            serde::Serialize,
756            fedimint_core::encoding::Encodable,
757            fedimint_core::encoding::Decodable,
758        )]
759        pub enum $name {
760            V0($name_v0),
761            #[encodable_default]
762            Default {
763                variant: u64,
764                bytes: Vec<u8>,
765            },
766        }
767
768        impl $name {
769            pub fn maybe_v0_ref(&self) -> Option<&$name_v0> {
770                match self {
771                    $name::V0(v0) => Some(v0),
772                    $name::Default { .. } => None,
773                }
774            }
775
776            pub fn ensure_v0_ref(&self) -> Result<&$name_v0, $error> {
777                match self {
778                    $name::V0(v0) => Ok(v0),
779                    $name::Default { variant, .. } => Err($error { variant: *variant }),
780                }
781            }
782        }
783
784        impl std::fmt::Debug for $name {
785            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
786                use $crate::bitcoin::hashes::hex::DisplayHex;
787                match self {
788                    $name::V0(v0) => {
789                        v0.fmt(f)?;
790                    }
791                    $name::Default { variant, bytes } => {
792                        f.debug_struct(stringify!($name))
793                            .field("variant", variant)
794                            .field("bytes", &bytes.as_hex())
795                            .finish()?;
796                    }
797                }
798                Ok(())
799            }
800        }
801
802        #[derive(
803            Debug,
804            thiserror::Error,
805            Clone,
806            Eq,
807            PartialEq,
808            Hash,
809            serde::Deserialize,
810            serde::Serialize,
811            fedimint_core::encoding::Encodable,
812            fedimint_core::encoding::Decodable,
813        )]
814        #[error("Unknown {} variant {variant}", stringify!($name))]
815        pub struct $error {
816            pub variant: u64,
817        }
818
819        impl std::fmt::Display for $name {
820            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
821                match self {
822                    $name::V0(inner) => std::fmt::Display::fmt(inner, f),
823                    $name::Default { variant, .. } => {
824                        write!(f, "Unknown {} (variant={variant})", stringify!($name))
825                    }
826                }
827            }
828        }
829    };
830}