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<(), std::io::Error> {
317                self.module_instance_id.consensus_encode(writer)?;
318
319                let mut buf = Vec::with_capacity(512);
320                self.inner.consensus_encode_dyn(&mut buf)?;
321
322                buf.consensus_encode(writer)?;
323
324                Ok(())
325            }
326        }
327
328        impl Decodable for $name {
329            fn consensus_decode_partial_from_finite_reader<R: std::io::Read>(
330                reader: &mut R,
331                decoders: &$crate::module::registry::ModuleDecoderRegistry,
332            ) -> Result<Self, fedimint_core::encoding::DecodeError> {
333                let module_instance_id =
334                    fedimint_core::core::ModuleInstanceId::consensus_decode_partial_from_finite_reader(
335                        reader, decoders,
336                    )?;
337                let val = match decoders.get(module_instance_id) {
338                    Some(decoder) => {
339                        let total_len_u64 =
340                            u64::consensus_decode_partial_from_finite_reader(reader, decoders)?;
341                        decoder.decode_complete(
342                            reader,
343                            total_len_u64,
344                            module_instance_id,
345                            decoders,
346                        )?
347                    }
348                    None => match decoders.decoding_mode() {
349                        $crate::module::registry::DecodingMode::Reject => {
350                            return Err(fedimint_core::encoding::DecodeError::new_custom(
351                                anyhow::anyhow!(
352                                    "Module decoder not available for module instance: {module_instance_id} when decoding {}", std::any::type_name::<Self>()
353                                ),
354                            ));
355                        }
356                        $crate::module::registry::DecodingMode::Fallback => $name::from_typed(
357                            module_instance_id,
358                            $crate::core::DynUnknown(
359                                Vec::<u8>::consensus_decode_partial_from_finite_reader(
360                                    reader,
361                                    &Default::default(),
362                                )?,
363                            ),
364                        ),
365                    },
366                };
367
368                Ok(val)
369            }
370        }
371    };
372}
373
374/// Define a "plugin" trait
375///
376/// "Plugin trait" is a trait that a developer of a mint module
377/// needs to implement when implementing mint module. It uses associated
378/// types with trait bounds to guide the developer.
379///
380/// Blanket implementations are used to convert the "plugin trait",
381/// incompatible with `dyn Trait` into "module types" and corresponding
382/// "module dyn newtypes", erasing the exact type and used in a common
383/// Fedimint code.
384#[macro_export]
385macro_rules! module_plugin_static_trait_define{
386    (   $(#[$outer:meta])*
387        $dyn_newtype:ident, $static_trait:ident, $dyn_trait:ident, { $($extra_methods:tt)* }, { $($extra_impls:tt)* }
388    ) => {
389        pub trait $static_trait:
390            std::fmt::Debug + std::fmt::Display + std::cmp::PartialEq + std::hash::Hash + DynEncodable + Decodable + Encodable + Clone + IntoDynInstance<DynType = $dyn_newtype> + Send + Sync + 'static
391        {
392            const KIND : ModuleKind;
393
394            $($extra_methods)*
395        }
396
397        impl $dyn_trait for ::fedimint_core::core::DynUnknown {
398            fn as_any(&self) -> &(dyn Any + Send + Sync) {
399                self
400            }
401
402            fn module_kind(&self) -> Option<ModuleKind> {
403                None
404            }
405
406            fn clone(&self, instance_id: ::fedimint_core::core::ModuleInstanceId) -> $dyn_newtype {
407                $dyn_newtype::from_typed(instance_id, <Self as Clone>::clone(self))
408            }
409
410            fn dyn_hash(&self) -> u64 {
411                use std::hash::Hash;
412                let mut s = std::collections::hash_map::DefaultHasher::new();
413                self.hash(&mut s);
414                std::hash::Hasher::finish(&s)
415            }
416
417            $($extra_impls)*
418        }
419
420        impl<T> $dyn_trait for T
421        where
422            T: $static_trait + DynEncodable + 'static + Send + Sync,
423        {
424            fn as_any(&self) -> &(dyn Any + Send + Sync) {
425                self
426            }
427
428            fn module_kind(&self) -> Option<ModuleKind> {
429                Some(<Self as $static_trait>::KIND)
430            }
431
432            fn clone(&self, instance_id: ::fedimint_core::core::ModuleInstanceId) -> $dyn_newtype {
433                $dyn_newtype::from_typed(instance_id, <Self as Clone>::clone(self))
434            }
435
436            fn dyn_hash(&self) -> u64 {
437                let mut s = std::collections::hash_map::DefaultHasher::new();
438                self.hash(&mut s);
439                std::hash::Hasher::finish(&s)
440            }
441
442            $($extra_impls)*
443        }
444
445        impl std::hash::Hash for $dyn_newtype {
446            fn hash<H>(&self, state: &mut H)
447            where
448                H: std::hash::Hasher
449            {
450                self.module_instance_id.hash(state);
451                self.inner.dyn_hash().hash(state);
452            }
453        }
454    };
455}
456
457/// A copy of `module_lugin_static_trait_define` but for `ClientConfig`.
458///
459/// `ClientConfig` is a snowflake that requires `: Serialize` and conditional
460/// implementation for `DynUnknown`. The macro is getting gnarly, so seems
461/// easier to copy-paste-modify, than pile up conditional argument.
462#[macro_export]
463macro_rules! module_plugin_static_trait_define_config{
464    (   $(#[$outer:meta])*
465        $dyn_newtype:ident, $static_trait:ident, $dyn_trait:ident, { $($extra_methods:tt)* }, { $($extra_impls:tt)* }, { $($extra_impls_unknown:tt)* }
466    ) => {
467        pub trait $static_trait:
468            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
469        {
470            const KIND : ::fedimint_core::core::ModuleKind;
471            $($extra_methods)*
472        }
473
474        impl $dyn_trait for ::fedimint_core::core::DynUnknown {
475            fn as_any(&self) -> &(dyn Any + Send + Sync) {
476                self
477            }
478
479            fn module_kind(&self) -> Option<::fedimint_core::core::ModuleKind> {
480                None
481            }
482
483            fn clone(&self, instance_id: ::fedimint_core::core::ModuleInstanceId) -> $dyn_newtype {
484                $dyn_newtype::from_typed(instance_id, <Self as Clone>::clone(self))
485            }
486
487            fn dyn_hash(&self) -> u64 {
488                use std::hash::Hash;
489                let mut s = std::collections::hash_map::DefaultHasher::new();
490                self.hash(&mut s);
491                std::hash::Hasher::finish(&s)
492            }
493
494            $($extra_impls_unknown)*
495        }
496
497        impl<T> $dyn_trait for T
498        where
499            T: $static_trait + DynEncodable + 'static + Send + Sync,
500        {
501            fn as_any(&self) -> &(dyn Any + Send + Sync) {
502                self
503            }
504
505            fn module_kind(&self) -> Option<::fedimint_core::core::ModuleKind> {
506                Some(<T as $static_trait>::KIND)
507            }
508
509            fn clone(&self, instance_id: ::fedimint_core::core::ModuleInstanceId) -> $dyn_newtype {
510                $dyn_newtype::from_typed(instance_id, <Self as Clone>::clone(self))
511            }
512
513            fn dyn_hash(&self) -> u64 {
514                let mut s = std::collections::hash_map::DefaultHasher::new();
515                self.hash(&mut s);
516                std::hash::Hasher::finish(&s)
517            }
518
519            $($extra_impls)*
520        }
521
522        impl std::hash::Hash for $dyn_newtype {
523            fn hash<H>(&self, state: &mut H)
524            where
525                H: std::hash::Hasher
526            {
527                self.module_instance_id.hash(state);
528                self.inner.dyn_hash().hash(state);
529            }
530        }
531    };
532}
533
534/// Implements the necessary traits for all configuration related types of a
535/// `FederationServer` module.
536#[macro_export]
537macro_rules! plugin_types_trait_impl_config {
538    ($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) => {
539        impl fedimint_core::config::ModuleInitParams for $gen {
540            type Local = $gen_local;
541            type Consensus = $gen_consensus;
542
543            fn from_parts(local: Self::Local, consensus: Self::Consensus) -> Self {
544                Self { local, consensus }
545            }
546
547            fn to_parts(self) -> (Self::Local, Self::Consensus) {
548                (self.local, self.consensus)
549            }
550        }
551
552        impl fedimint_core::config::TypedServerModuleConsensusConfig for $cfg_consensus {
553            fn kind(&self) -> fedimint_core::core::ModuleKind {
554                <$common_gen as fedimint_core::module::CommonModuleInit>::KIND
555            }
556
557            fn version(&self) -> fedimint_core::module::ModuleConsensusVersion {
558                <$common_gen as fedimint_core::module::CommonModuleInit>::CONSENSUS_VERSION
559            }
560        }
561
562        impl fedimint_core::config::TypedServerModuleConfig for $cfg {
563            type Local = $cfg_local;
564            type Private = $cfg_private;
565            type Consensus = $cfg_consensus;
566
567            fn from_parts(
568                local: Self::Local,
569                private: Self::Private,
570                consensus: Self::Consensus,
571            ) -> Self {
572                Self {
573                    local,
574                    private,
575                    consensus,
576                }
577            }
578
579            fn to_parts(self) -> (ModuleKind, Self::Local, Self::Private, Self::Consensus) {
580                (
581                    <$common_gen as fedimint_core::module::CommonModuleInit>::KIND,
582                    self.local,
583                    self.private,
584                    self.consensus,
585                )
586            }
587        }
588    };
589}
590
591/// Implements the necessary traits for all associated types of a
592/// `FederationServer` module.
593#[macro_export]
594macro_rules! plugin_types_trait_impl_common {
595    ($kind:expr_2021, $types:ty, $client_config:ty, $input:ty, $output:ty, $outcome:ty, $ci:ty, $input_error:ty, $output_error:ty) => {
596        impl fedimint_core::module::ModuleCommon for $types {
597            type ClientConfig = $client_config;
598            type Input = $input;
599            type Output = $output;
600            type OutputOutcome = $outcome;
601            type ConsensusItem = $ci;
602            type InputError = $input_error;
603            type OutputError = $output_error;
604        }
605
606        impl fedimint_core::core::ClientConfig for $client_config {
607            const KIND: ModuleKind = $kind;
608        }
609
610        impl fedimint_core::core::IntoDynInstance for $client_config {
611            type DynType = fedimint_core::core::DynClientConfig;
612
613            fn into_dyn(self, instance_id: ModuleInstanceId) -> Self::DynType {
614                fedimint_core::core::DynClientConfig::from_typed(instance_id, self)
615            }
616        }
617
618        impl fedimint_core::core::Input for $input {
619            const KIND: ModuleKind = $kind;
620        }
621
622        impl fedimint_core::core::IntoDynInstance for $input {
623            type DynType = fedimint_core::core::DynInput;
624
625            fn into_dyn(self, instance_id: ModuleInstanceId) -> Self::DynType {
626                fedimint_core::core::DynInput::from_typed(instance_id, self)
627            }
628        }
629
630        impl fedimint_core::core::Output for $output {
631            const KIND: ModuleKind = $kind;
632        }
633
634        impl fedimint_core::core::IntoDynInstance for $output {
635            type DynType = fedimint_core::core::DynOutput;
636
637            fn into_dyn(self, instance_id: ModuleInstanceId) -> Self::DynType {
638                fedimint_core::core::DynOutput::from_typed(instance_id, self)
639            }
640        }
641
642        impl fedimint_core::core::OutputOutcome for $outcome {
643            const KIND: ModuleKind = $kind;
644        }
645
646        impl fedimint_core::core::IntoDynInstance for $outcome {
647            type DynType = fedimint_core::core::DynOutputOutcome;
648
649            fn into_dyn(self, instance_id: ModuleInstanceId) -> Self::DynType {
650                fedimint_core::core::DynOutputOutcome::from_typed(instance_id, self)
651            }
652        }
653
654        impl fedimint_core::core::ModuleConsensusItem for $ci {
655            const KIND: ModuleKind = $kind;
656        }
657
658        impl fedimint_core::core::IntoDynInstance for $ci {
659            type DynType = fedimint_core::core::DynModuleConsensusItem;
660
661            fn into_dyn(self, instance_id: ModuleInstanceId) -> Self::DynType {
662                fedimint_core::core::DynModuleConsensusItem::from_typed(instance_id, self)
663            }
664        }
665
666        impl fedimint_core::core::InputError for $input_error {
667            const KIND: ModuleKind = $kind;
668        }
669
670        impl fedimint_core::core::IntoDynInstance for $input_error {
671            type DynType = fedimint_core::core::DynInputError;
672
673            fn into_dyn(self, instance_id: ModuleInstanceId) -> Self::DynType {
674                fedimint_core::core::DynInputError::from_typed(instance_id, self)
675            }
676        }
677
678        impl fedimint_core::core::OutputError for $output_error {
679            const KIND: ModuleKind = $kind;
680        }
681
682        impl fedimint_core::core::IntoDynInstance for $output_error {
683            type DynType = fedimint_core::core::DynOutputError;
684
685            fn into_dyn(self, instance_id: ModuleInstanceId) -> Self::DynType {
686                fedimint_core::core::DynOutputError::from_typed(instance_id, self)
687            }
688        }
689    };
690}
691
692#[macro_export]
693macro_rules! erased_eq_no_instance_id {
694    ($newtype:ty) => {
695        fn erased_eq_no_instance_id(&self, other: &$newtype) -> bool {
696            let other: &Self = other
697                .as_any()
698                .downcast_ref()
699                .expect("Type is ensured in previous step");
700
701            self == other
702        }
703    };
704}
705
706#[macro_export]
707macro_rules! module_plugin_dyn_newtype_eq_passthrough {
708    ($newtype:ty) => {
709        impl PartialEq for $newtype {
710            fn eq(&self, other: &Self) -> bool {
711                if self.module_instance_id != other.module_instance_id {
712                    return false;
713                }
714                self.erased_eq_no_instance_id(other)
715            }
716        }
717
718        impl Eq for $newtype {}
719    };
720}
721
722#[macro_export]
723macro_rules! module_plugin_dyn_newtype_display_passthrough {
724    ($newtype:ty) => {
725        impl std::fmt::Display for $newtype {
726            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
727                f.write_fmt(format_args!("{}-{}", self.module_instance_id, self.inner))
728            }
729        }
730    };
731}
732
733// TODO: use concat_ident for error name once it lands in stable, see https://github.com/rust-lang/rust/issues/29599
734/// Macro for defining module associated types.
735///
736/// Wraps a type into an enum with a default variant, this allows to add new
737/// versions of the type in the future. Depending on context unknown versions
738/// may be ignored or lead to errors. E.g. the client might just ignore an
739/// unknown input version since it cannot originate from itself while the server
740/// would reject it for not being able to validate its correctness.
741///
742/// Adding extensibility this way is a last line of defense against breaking
743/// changes, most often other ways of introducing new functionality should be
744/// preferred (e.g. new module versions, pure client-side changes, …).
745#[macro_export]
746macro_rules! extensible_associated_module_type {
747    ($name:ident, $name_v0:ident, $error:ident) => {
748        #[derive(
749            Clone,
750            Eq,
751            PartialEq,
752            Hash,
753            serde::Deserialize,
754            serde::Serialize,
755            fedimint_core::encoding::Encodable,
756            fedimint_core::encoding::Decodable,
757        )]
758        pub enum $name {
759            V0($name_v0),
760            #[encodable_default]
761            Default {
762                variant: u64,
763                bytes: Vec<u8>,
764            },
765        }
766
767        impl $name {
768            pub fn maybe_v0_ref(&self) -> Option<&$name_v0> {
769                match self {
770                    $name::V0(v0) => Some(v0),
771                    $name::Default { .. } => None,
772                }
773            }
774
775            pub fn ensure_v0_ref(&self) -> Result<&$name_v0, $error> {
776                match self {
777                    $name::V0(v0) => Ok(v0),
778                    $name::Default { variant, .. } => Err($error { variant: *variant }),
779                }
780            }
781        }
782
783        impl std::fmt::Debug for $name {
784            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
785                use $crate::bitcoin::hashes::hex::DisplayHex;
786                match self {
787                    $name::V0(v0) => {
788                        std::fmt::Debug::fmt(v0, f)?;
789                    }
790                    $name::Default { variant, bytes } => {
791                        f.debug_struct(stringify!($name))
792                            .field("variant", variant)
793                            .field("bytes", &bytes.as_hex())
794                            .finish()?;
795                    }
796                }
797                Ok(())
798            }
799        }
800
801        #[derive(
802            Debug,
803            thiserror::Error,
804            Clone,
805            Eq,
806            PartialEq,
807            Hash,
808            serde::Deserialize,
809            serde::Serialize,
810            fedimint_core::encoding::Encodable,
811            fedimint_core::encoding::Decodable,
812        )]
813        #[error("Unknown {} variant {variant}", stringify!($name))]
814        pub struct $error {
815            pub variant: u64,
816        }
817
818        impl std::fmt::Display for $name {
819            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
820                match self {
821                    $name::V0(inner) => std::fmt::Display::fmt(inner, f),
822                    $name::Default { variant, .. } => {
823                        write!(f, "Unknown {} (variant={variant})", stringify!($name))
824                    }
825                }
826            }
827        }
828    };
829}