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 }