/ fedimint-core / src / macros.rs
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]
 10  macro_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]
 48  macro_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]
113  macro_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]
126  macro_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]
164  macro_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>(module_instance_id: ::fedimint_core::core::ModuleInstanceId, typed: I) -> Self
189              where
190                  I: $trait + $crate::task::MaybeSend + $crate::task::MaybeSync + 'static {
191  
192                  Self { inner: $container::new(typed), module_instance_id }
193              }
194  
195              pub fn from_parts(module_instance_id: $crate::core::ModuleInstanceId, dynbox: $container<$crate::maybe_add_send_sync!(dyn $trait + 'static)>) -> Self {
196                  Self { inner: dynbox, module_instance_id }
197              }
198          }
199  
200          impl std::fmt::Debug for $name {
201              fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
202                  std::fmt::Debug::fmt(&self.inner, f)
203              }
204          }
205      };
206      (   $(#[$outer:meta])*
207          $vis:vis $name:ident<$lifetime:lifetime>($container:ident<$trait:ident>)
208      ) => {
209          $(#[$outer])*
210          $vis struct $name<$lifetime>{ inner: $container<dyn $trait<$lifetime> + Send + $lifetime>, module_instance_id: ModuleInstanceId }
211  
212          impl $name {
213              pub fn module_instance_id(&self) -> ::fedimint_core::core::ModuleInstanceId {
214                  self.1
215              }
216  
217              pub fn from_typed<I>(module_instance_id: ::fedimint_core::core::ModuleInstanceId, typed: I) -> Self
218              where
219                  I: $trait + $crate::task::MaybeSend + $crate::task::MaybeSync + 'static {
220  
221                  Self { inner: $container::new(typed), module_instance_id }
222              }
223          }
224  
225          impl<$lifetime> std::ops::Deref for $name<$lifetime> {
226              type Target = $crate::maybe_add_send_sync!(dyn $trait + 'static);
227  
228              fn deref(&self) -> &<Self as std::ops::Deref>::Target {
229                  &*self.inner
230              }
231          }
232      };
233  }
234  
235  #[macro_export]
236  macro_rules! _dyn_newtype_impl_deref_mut {
237      ($name:ident<$lifetime:lifetime>) => {
238          impl<$lifetime> std::ops::DerefMut for $name<$lifetime> {
239              fn deref_mut(&mut self) -> &mut <Self as std::ops::Deref>::Target {
240                  &mut *self.inner
241              }
242          }
243      };
244      ($name:ident) => {
245          impl std::ops::DerefMut for $name {
246              fn deref_mut(&mut self) -> &mut <Self as std::ops::Deref>::Target {
247                  &mut *self.inner
248              }
249          }
250      };
251  }
252  
253  /// Implement `Clone` on a "dyn newtype"
254  ///
255  /// ... by calling `clone` method on the underlying
256  /// `dyn Trait`.
257  ///
258  /// Cloning `dyn Trait`s is non trivial due to object-safety.
259  ///
260  /// Note: the underlying `dyn Trait` needs to implement
261  /// a `fn clone(&self) -> Newtype` for this to work,
262  /// and this macro does not check or do anything about it.
263  ///
264  /// If the newtype is using `Arc` you probably want
265  /// to just use standard `#[derive(Clone)]` to clone
266  /// the `Arc` itself.
267  #[macro_export]
268  macro_rules! dyn_newtype_impl_dyn_clone_passthrough {
269      ($name:ident) => {
270          impl Clone for $name {
271              fn clone(&self) -> Self {
272                  self.0.clone()
273              }
274          }
275      };
276  }
277  
278  #[macro_export]
279  macro_rules! module_plugin_dyn_newtype_clone_passthrough {
280      ($name:ident) => {
281          impl Clone for $name {
282              fn clone(&self) -> Self {
283                  self.inner.clone(self.module_instance_id)
284              }
285          }
286      };
287  }
288  
289  /// Implement `Encodable` and `Decodable` for a "module dyn newtype"
290  ///
291  /// "Module dyn newtype" is just a "dyn newtype" used by general purpose
292  /// Fedimint code to abstract away details of mint modules.
293  #[macro_export]
294  macro_rules! module_plugin_dyn_newtype_encode_decode {
295      ($name:ident) => {
296          impl Encodable for $name {
297              fn consensus_encode<W: std::io::Write>(
298                  &self,
299                  writer: &mut W,
300              ) -> Result<usize, std::io::Error> {
301                  let mut written = self.module_instance_id.consensus_encode(writer)?;
302  
303                  let mut buf = Vec::with_capacity(512);
304                  let buf_written = self.inner.consensus_encode_dyn(&mut buf)?;
305                  assert_eq!(buf.len(), buf_written);
306  
307                  written += buf.consensus_encode(writer)?;
308  
309                  Ok(written)
310              }
311          }
312  
313          impl Decodable for $name {
314              fn consensus_decode_from_finite_reader<R: std::io::Read>(
315                  reader: &mut R,
316                  decoders: &$crate::module::registry::ModuleDecoderRegistry,
317              ) -> Result<Self, fedimint_core::encoding::DecodeError> {
318                  let module_instance_id =
319                      fedimint_core::core::ModuleInstanceId::consensus_decode_from_finite_reader(
320                          reader, decoders,
321                      )?;
322                  let val = match decoders.get(module_instance_id) {
323                      Some(decoder) => {
324                          let total_len_u64 =
325                              u64::consensus_decode_from_finite_reader(reader, decoders)?;
326                          decoder.decode_complete(
327                              reader,
328                              total_len_u64,
329                              module_instance_id,
330                              decoders,
331                          )?
332                      }
333                      None => match decoders.decoding_mode() {
334                          $crate::module::registry::DecodingMode::Reject => {
335                              return Err(fedimint_core::encoding::DecodeError::new_custom(
336                                  anyhow::anyhow!(
337                                      "Module decoder not available: {module_instance_id} when decoding {}", std::any::type_name::<Self>()
338                                  ),
339                              ));
340                          }
341                          $crate::module::registry::DecodingMode::Fallback => $name::from_typed(
342                              module_instance_id,
343                              $crate::core::DynUnknown(
344                                  Vec::<u8>::consensus_decode_from_finite_reader(
345                                      reader,
346                                      &Default::default(),
347                                  )?,
348                              ),
349                          ),
350                      },
351                  };
352  
353                  Ok(val)
354              }
355          }
356      };
357  }
358  
359  /// Define a "plugin" trait
360  ///
361  /// "Plugin trait" is a trait that a developer of a mint module
362  /// needs to implement when implementing mint module. It uses associated
363  /// types with trait bounds to guide the developer.
364  ///
365  /// Blanket implementations are used to convert the "plugin trait",
366  /// incompatible with `dyn Trait` into "module types" and corresponding
367  /// "module dyn newtypes", erasing the exact type and used in a common
368  /// Fedimint code.
369  #[macro_export]
370  macro_rules! module_plugin_static_trait_define{
371      (   $(#[$outer:meta])*
372          $dyn_newtype:ident, $static_trait:ident, $dyn_trait:ident, { $($extra_methods:tt)* }, { $($extra_impls:tt)* }
373      ) => {
374          pub trait $static_trait:
375              std::fmt::Debug + std::fmt::Display + std::cmp::PartialEq + std::hash::Hash + DynEncodable + Decodable + Encodable + Clone + IntoDynInstance<DynType = $dyn_newtype> + Send + Sync + 'static
376          {
377              $($extra_methods)*
378          }
379  
380          impl $dyn_trait for ::fedimint_core::core::DynUnknown {
381              fn as_any(&self) -> &(dyn Any + Send + Sync) {
382                  self
383              }
384  
385              fn clone(&self, instance_id: ::fedimint_core::core::ModuleInstanceId) -> $dyn_newtype {
386                  $dyn_newtype::from_typed(instance_id, <Self as Clone>::clone(self))
387              }
388  
389              fn dyn_hash(&self) -> u64 {
390                  use std::hash::Hash;
391                  let mut s = std::collections::hash_map::DefaultHasher::new();
392                  self.hash(&mut s);
393                  std::hash::Hasher::finish(&s)
394              }
395  
396              $($extra_impls)*
397          }
398  
399          impl<T> $dyn_trait for T
400          where
401              T: $static_trait + DynEncodable + 'static + Send + Sync,
402          {
403              fn as_any(&self) -> &(dyn Any + Send + Sync) {
404                  self
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                  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 std::hash::Hash for $dyn_newtype {
421              fn hash<H>(&self, state: &mut H)
422              where
423                  H: std::hash::Hasher
424              {
425                  self.module_instance_id.hash(state);
426                  self.inner.dyn_hash().hash(state);
427              }
428          }
429      };
430  }
431  
432  /// A copy of `module_lugin_static_trait_define` but for `ClientConfig`, which
433  /// is a snowflake that requires `: Serialize` and conditional implementation
434  /// for `DynUnknown`. The macro is getting gnarly, so seems easier to
435  /// copy-paste-modify, than pile up conditional argument.
436  #[macro_export]
437  macro_rules! module_plugin_static_trait_define_config{
438      (   $(#[$outer:meta])*
439          $dyn_newtype:ident, $static_trait:ident, $dyn_trait:ident, { $($extra_methods:tt)* }, { $($extra_impls:tt)* }, { $($extra_impls_unknown:tt)* }
440      ) => {
441          pub trait $static_trait:
442              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
443          {
444              $($extra_methods)*
445          }
446  
447          impl $dyn_trait for ::fedimint_core::core::DynUnknown {
448              fn as_any(&self) -> &(dyn Any + Send + Sync) {
449                  self
450              }
451  
452              fn clone(&self, instance_id: ::fedimint_core::core::ModuleInstanceId) -> $dyn_newtype {
453                  $dyn_newtype::from_typed(instance_id, <Self as Clone>::clone(self))
454              }
455  
456              fn dyn_hash(&self) -> u64 {
457                  use std::hash::Hash;
458                  let mut s = std::collections::hash_map::DefaultHasher::new();
459                  self.hash(&mut s);
460                  std::hash::Hasher::finish(&s)
461              }
462  
463              $($extra_impls_unknown)*
464          }
465  
466          impl<T> $dyn_trait for T
467          where
468              T: $static_trait + DynEncodable + 'static + Send + Sync,
469          {
470              fn as_any(&self) -> &(dyn Any + Send + Sync) {
471                  self
472              }
473  
474              fn clone(&self, instance_id: ::fedimint_core::core::ModuleInstanceId) -> $dyn_newtype {
475                  $dyn_newtype::from_typed(instance_id, <Self as Clone>::clone(self))
476              }
477  
478              fn dyn_hash(&self) -> u64 {
479                  let mut s = std::collections::hash_map::DefaultHasher::new();
480                  self.hash(&mut s);
481                  std::hash::Hasher::finish(&s)
482              }
483  
484              $($extra_impls)*
485          }
486  
487          impl std::hash::Hash for $dyn_newtype {
488              fn hash<H>(&self, state: &mut H)
489              where
490                  H: std::hash::Hasher
491              {
492                  self.module_instance_id.hash(state);
493                  self.inner.dyn_hash().hash(state);
494              }
495          }
496      };
497  }
498  
499  /// Implements the necessary traits for all configuration related types of a
500  /// `FederationServer` module.
501  #[macro_export]
502  macro_rules! plugin_types_trait_impl_config {
503      ($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) => {
504          impl fedimint_core::config::ModuleInitParams for $gen {
505              type Local = $gen_local;
506              type Consensus = $gen_consensus;
507  
508              fn from_parts(local: Self::Local, consensus: Self::Consensus) -> Self {
509                  Self { local, consensus }
510              }
511  
512              fn to_parts(self) -> (Self::Local, Self::Consensus) {
513                  (self.local, self.consensus)
514              }
515          }
516  
517          impl fedimint_core::config::TypedServerModuleConsensusConfig for $cfg_consensus {
518              fn kind(&self) -> fedimint_core::core::ModuleKind {
519                  <$common_gen as fedimint_core::module::CommonModuleInit>::KIND
520              }
521  
522              fn version(&self) -> fedimint_core::module::ModuleConsensusVersion {
523                  <$common_gen as fedimint_core::module::CommonModuleInit>::CONSENSUS_VERSION
524              }
525          }
526  
527          impl fedimint_core::config::TypedServerModuleConfig for $cfg {
528              type Local = $cfg_local;
529              type Private = $cfg_private;
530              type Consensus = $cfg_consensus;
531  
532              fn from_parts(
533                  local: Self::Local,
534                  private: Self::Private,
535                  consensus: Self::Consensus,
536              ) -> Self {
537                  Self {
538                      local,
539                      private,
540                      consensus,
541                  }
542              }
543  
544              fn to_parts(self) -> (ModuleKind, Self::Local, Self::Private, Self::Consensus) {
545                  (
546                      <$common_gen as fedimint_core::module::CommonModuleInit>::KIND,
547                      self.local,
548                      self.private,
549                      self.consensus,
550                  )
551              }
552          }
553      };
554  }
555  
556  /// Implements the necessary traits for all associated types of a
557  /// `FederationServer` module.
558  #[macro_export]
559  macro_rules! plugin_types_trait_impl_common {
560      ($types:ty, $client_config:ty, $input:ty, $output:ty, $outcome:ty, $ci:ty, $input_error:ty, $output_error:ty) => {
561          impl fedimint_core::module::ModuleCommon for $types {
562              type ClientConfig = $client_config;
563              type Input = $input;
564              type Output = $output;
565              type OutputOutcome = $outcome;
566              type ConsensusItem = $ci;
567              type InputError = $input_error;
568              type OutputError = $output_error;
569          }
570  
571          impl fedimint_core::core::ClientConfig for $client_config {}
572  
573          impl fedimint_core::core::IntoDynInstance for $client_config {
574              type DynType = fedimint_core::core::DynClientConfig;
575  
576              fn into_dyn(self, instance_id: ModuleInstanceId) -> Self::DynType {
577                  fedimint_core::core::DynClientConfig::from_typed(instance_id, self)
578              }
579          }
580  
581          impl fedimint_core::core::Input for $input {}
582  
583          impl fedimint_core::core::IntoDynInstance for $input {
584              type DynType = fedimint_core::core::DynInput;
585  
586              fn into_dyn(self, instance_id: ModuleInstanceId) -> Self::DynType {
587                  fedimint_core::core::DynInput::from_typed(instance_id, self)
588              }
589          }
590  
591          impl fedimint_core::core::Output for $output {}
592  
593          impl fedimint_core::core::IntoDynInstance for $output {
594              type DynType = fedimint_core::core::DynOutput;
595  
596              fn into_dyn(self, instance_id: ModuleInstanceId) -> Self::DynType {
597                  fedimint_core::core::DynOutput::from_typed(instance_id, self)
598              }
599          }
600  
601          impl fedimint_core::core::OutputOutcome for $outcome {}
602  
603          impl fedimint_core::core::IntoDynInstance for $outcome {
604              type DynType = fedimint_core::core::DynOutputOutcome;
605  
606              fn into_dyn(self, instance_id: ModuleInstanceId) -> Self::DynType {
607                  fedimint_core::core::DynOutputOutcome::from_typed(instance_id, self)
608              }
609          }
610  
611          impl fedimint_core::core::ModuleConsensusItem for $ci {}
612  
613          impl fedimint_core::core::IntoDynInstance for $ci {
614              type DynType = fedimint_core::core::DynModuleConsensusItem;
615  
616              fn into_dyn(self, instance_id: ModuleInstanceId) -> Self::DynType {
617                  fedimint_core::core::DynModuleConsensusItem::from_typed(instance_id, self)
618              }
619          }
620  
621          impl fedimint_core::core::InputError for $input_error {}
622  
623          impl fedimint_core::core::IntoDynInstance for $input_error {
624              type DynType = fedimint_core::core::DynInputError;
625  
626              fn into_dyn(self, instance_id: ModuleInstanceId) -> Self::DynType {
627                  fedimint_core::core::DynInputError::from_typed(instance_id, self)
628              }
629          }
630  
631          impl fedimint_core::core::OutputError for $output_error {}
632  
633          impl fedimint_core::core::IntoDynInstance for $output_error {
634              type DynType = fedimint_core::core::DynOutputError;
635  
636              fn into_dyn(self, instance_id: ModuleInstanceId) -> Self::DynType {
637                  fedimint_core::core::DynOutputError::from_typed(instance_id, self)
638              }
639          }
640      };
641  }
642  
643  #[macro_export]
644  macro_rules! erased_eq_no_instance_id {
645      ($newtype:ty) => {
646          fn erased_eq_no_instance_id(&self, other: &$newtype) -> bool {
647              let other: &Self = other
648                  .as_any()
649                  .downcast_ref()
650                  .expect("Type is ensured in previous step");
651  
652              self == other
653          }
654      };
655  }
656  
657  #[macro_export]
658  macro_rules! module_plugin_dyn_newtype_eq_passthrough {
659      ($newtype:ty) => {
660          impl PartialEq for $newtype {
661              fn eq(&self, other: &Self) -> bool {
662                  if self.module_instance_id != other.module_instance_id {
663                      return false;
664                  }
665                  self.erased_eq_no_instance_id(other)
666              }
667          }
668  
669          impl Eq for $newtype {}
670      };
671  }
672  
673  #[macro_export]
674  macro_rules! module_plugin_dyn_newtype_display_passthrough {
675      ($newtype:ty) => {
676          impl std::fmt::Display for $newtype {
677              fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
678                  f.write_fmt(format_args!("{}-{}", self.module_instance_id, self.inner))
679              }
680          }
681      };
682  }
683  
684  // TODO: use concat_ident for error name once it lands in stable, see https://github.com/rust-lang/rust/issues/29599
685  /// Wraps a type into an enum with a default variant, this allows to add new
686  /// versions of the type in the future. Depending on context unknown versions
687  /// may be ignored or lead to errors. E.g. the client might just ignore an
688  /// unknown input version since it cannot originate from itself while the server
689  /// would reject it for not being able to validate its correctness.
690  ///
691  /// Adding extensibility this way is a last line of defense against breaking
692  /// changes, most often other ways of introducing new functionality should be
693  /// preferred (e.g. new module versions, pure client-side changes, …).
694  #[macro_export]
695  macro_rules! extensible_associated_module_type {
696      ($name:ident, $name_v0:ident, $error:ident) => {
697          #[derive(
698              Debug,
699              Clone,
700              Eq,
701              PartialEq,
702              Hash,
703              serde::Deserialize,
704              serde::Serialize,
705              fedimint_core::encoding::Encodable,
706              fedimint_core::encoding::Decodable,
707          )]
708          pub enum $name {
709              V0($name_v0),
710              #[encodable_default]
711              Default {
712                  variant: u64,
713                  bytes: Vec<u8>,
714              },
715          }
716  
717          impl $name {
718              pub fn maybe_v0_ref(&self) -> Option<&$name_v0> {
719                  match self {
720                      $name::V0(v0) => Some(v0),
721                      $name::Default { .. } => None,
722                  }
723              }
724  
725              pub fn ensure_v0_ref(&self) -> Result<&$name_v0, $error> {
726                  match self {
727                      $name::V0(v0) => Ok(v0),
728                      $name::Default { variant, .. } => Err($error { variant: *variant }),
729                  }
730              }
731          }
732  
733          #[derive(
734              Debug,
735              thiserror::Error,
736              Clone,
737              Eq,
738              PartialEq,
739              Hash,
740              serde::Deserialize,
741              serde::Serialize,
742              fedimint_core::encoding::Encodable,
743              fedimint_core::encoding::Decodable,
744          )]
745          #[error("Unknown {} variant {variant}", stringify!($name))]
746          pub struct $error {
747              pub variant: u64,
748          }
749  
750          impl std::fmt::Display for $name {
751              fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
752                  match self {
753                      $name::V0(inner) => std::fmt::Display::fmt(inner, f),
754                      $name::Default { variant, .. } => {
755                          write!(f, "Unknown {} (variant={variant})", stringify!($name))
756                      }
757                  }
758              }
759          }
760      };
761  }