From 201d9e5e60ead3c360f88a6a6da2d5c7f1559a97 Mon Sep 17 00:00:00 2001 From: Alexandru Vasile <60601340+lexnv@users.noreply.github.com> Date: Wed, 9 Oct 2024 17:16:01 +0300 Subject: [PATCH] v16: Add unstable metadata v16 (#82) * v16: Add unstable metadata v16 Signed-off-by: Alexandru Vasile * v16: Add deprecation info Signed-off-by: Alexandru Vasile * v16: Extrinsics with multiple versions Signed-off-by: Alexandru Vasile * v16: Rename signed extensions to transaction extensions Signed-off-by: Alexandru Vasile * v16: Remove the runtime type entirely Signed-off-by: Alexandru Vasile * v16: Remove BTreeMap> and use plain u8 instead to impl serialize Signed-off-by: Alexandru Vasile --------- Signed-off-by: Alexandru Vasile --- frame-metadata/src/lib.rs | 11 + frame-metadata/src/v16.rs | 671 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 682 insertions(+) create mode 100644 frame-metadata/src/v16.rs diff --git a/frame-metadata/src/lib.rs b/frame-metadata/src/lib.rs index a1f63ef..e59aee0 100644 --- a/frame-metadata/src/lib.rs +++ b/frame-metadata/src/lib.rs @@ -75,6 +75,10 @@ pub mod v14; #[cfg(feature = "current")] pub mod v15; +/// Unstable Metadata v16 +#[cfg(feature = "unstable")] +pub mod v16; + /// Metadata prefix. pub const META_RESERVED: u32 = 0x6174656d; // 'meta' warning for endianness. @@ -161,6 +165,12 @@ pub enum RuntimeMetadata { /// Version 15 for runtime metadata, as raw encoded bytes. #[cfg(not(feature = "current"))] V15(OpaqueMetadata), + /// Version 16 for runtime metadata. + #[cfg(feature = "unstable")] + V16(v16::RuntimeMetadataV16), + /// Version 16 for runtime metadata, as raw encoded bytes. + #[cfg(not(feature = "unstable"))] + V16(OpaqueMetadata), } impl RuntimeMetadata { @@ -183,6 +193,7 @@ impl RuntimeMetadata { RuntimeMetadata::V13(_) => 13, RuntimeMetadata::V14(_) => 14, RuntimeMetadata::V15(_) => 15, + RuntimeMetadata::V16(_) => u32::MAX, } } } diff --git a/frame-metadata/src/v16.rs b/frame-metadata/src/v16.rs new file mode 100644 index 0000000..dbe0eae --- /dev/null +++ b/frame-metadata/src/v16.rs @@ -0,0 +1,671 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#[cfg(feature = "decode")] +use codec::Decode; +#[cfg(feature = "serde_full")] +use serde::Serialize; + +use super::{RuntimeMetadataPrefixed, META_RESERVED}; +use codec::Encode; +use scale_info::{ + form::{Form, MetaForm, PortableForm}, + prelude::{collections::BTreeMap, vec::Vec}, + IntoPortable, PortableRegistry, Registry, +}; + +pub use super::v14::{StorageEntryModifier, StorageEntryType, StorageHasher}; + +/// Latest runtime metadata +pub type RuntimeMetadataLastVersion = RuntimeMetadataV16; + +impl From for super::RuntimeMetadataPrefixed { + fn from(metadata: RuntimeMetadataLastVersion) -> RuntimeMetadataPrefixed { + RuntimeMetadataPrefixed(META_RESERVED, super::RuntimeMetadata::V16(metadata)) + } +} + +/// The metadata of a runtime. +#[derive(Clone, PartialEq, Eq, Encode, Debug)] +#[cfg_attr(feature = "decode", derive(Decode))] +#[cfg_attr(feature = "serde_full", derive(Serialize))] +pub struct RuntimeMetadataV16 { + /// Type registry containing all types used in the metadata. + pub types: PortableRegistry, + /// Metadata of all the pallets. + pub pallets: Vec>, + /// Metadata of the extrinsic. + pub extrinsic: ExtrinsicMetadata, + /// Metadata of the Runtime API. + pub apis: Vec>, + /// The outer enums types as found in the runtime. + pub outer_enums: OuterEnums, + /// Allows users to add custom types to the metadata. + pub custom: CustomMetadata, +} + +impl RuntimeMetadataV16 { + /// Create a new instance of [`RuntimeMetadataV16`]. + pub fn new( + pallets: Vec, + extrinsic: ExtrinsicMetadata, + apis: Vec, + outer_enums: OuterEnums, + custom: CustomMetadata, + ) -> Self { + let mut registry = Registry::new(); + let pallets = registry.map_into_portable(pallets); + let extrinsic = extrinsic.into_portable(&mut registry); + let apis = registry.map_into_portable(apis); + let outer_enums = outer_enums.into_portable(&mut registry); + let custom = custom.into_portable(&mut registry); + + Self { + types: registry.into(), + pallets, + extrinsic, + apis, + outer_enums, + custom, + } + } +} + +/// Metadata of a runtime trait. +#[derive(Clone, PartialEq, Eq, Encode, Debug)] +#[cfg_attr(feature = "decode", derive(Decode))] +#[cfg_attr(feature = "serde_full", derive(Serialize))] +#[cfg_attr( + feature = "serde_full", + serde(bound(serialize = "T::Type: Serialize, T::String: Serialize")) +)] +pub struct RuntimeApiMetadata { + /// Trait name. + pub name: T::String, + /// Trait methods. + pub methods: Vec>, + /// Trait documentation. + pub docs: Vec, + /// Deprecation info + pub deprecation_info: DeprecationStatus, +} + +impl IntoPortable for RuntimeApiMetadata { + type Output = RuntimeApiMetadata; + + fn into_portable(self, registry: &mut Registry) -> Self::Output { + RuntimeApiMetadata { + name: self.name.into_portable(registry), + methods: registry.map_into_portable(self.methods), + docs: registry.map_into_portable(self.docs), + deprecation_info: self.deprecation_info.into_portable(registry), + } + } +} + +/// Metadata of a runtime method. +#[derive(Clone, PartialEq, Eq, Encode, Debug)] +#[cfg_attr(feature = "decode", derive(Decode))] +#[cfg_attr(feature = "serde_full", derive(Serialize))] +#[cfg_attr( + feature = "serde_full", + serde(bound(serialize = "T::Type: Serialize, T::String: Serialize")) +)] +pub struct RuntimeApiMethodMetadata { + /// Method name. + pub name: T::String, + /// Method parameters. + pub inputs: Vec>, + /// Method output. + pub output: T::Type, + /// Method documentation. + pub docs: Vec, + /// Deprecation info + pub deprecation_info: DeprecationStatus, +} + +impl IntoPortable for RuntimeApiMethodMetadata { + type Output = RuntimeApiMethodMetadata; + + fn into_portable(self, registry: &mut Registry) -> Self::Output { + RuntimeApiMethodMetadata { + name: self.name.into_portable(registry), + inputs: registry.map_into_portable(self.inputs), + output: registry.register_type(&self.output), + docs: registry.map_into_portable(self.docs), + deprecation_info: self.deprecation_info.into_portable(registry), + } + } +} + +/// Metadata of a runtime method parameter. +#[derive(Clone, PartialEq, Eq, Encode, Debug)] +#[cfg_attr(feature = "decode", derive(Decode))] +#[cfg_attr(feature = "serde_full", derive(Serialize))] +#[cfg_attr( + feature = "serde_full", + serde(bound(serialize = "T::Type: Serialize, T::String: Serialize")) +)] +pub struct RuntimeApiMethodParamMetadata { + /// Parameter name. + pub name: T::String, + /// Parameter type. + pub ty: T::Type, +} + +impl IntoPortable for RuntimeApiMethodParamMetadata { + type Output = RuntimeApiMethodParamMetadata; + + fn into_portable(self, registry: &mut Registry) -> Self::Output { + RuntimeApiMethodParamMetadata { + name: self.name.into_portable(registry), + ty: registry.register_type(&self.ty), + } + } +} + +/// Metadata of the extrinsic used by the runtime. +#[derive(Clone, PartialEq, Eq, Encode, Debug)] +#[cfg_attr(feature = "decode", derive(Decode))] +#[cfg_attr(feature = "serde_full", derive(Serialize))] +#[cfg_attr( + feature = "serde_full", + serde(bound(serialize = "T::Type: Serialize, T::String: Serialize")) +)] +pub struct ExtrinsicMetadata { + /// Extrinsic versions. + pub versions: Vec, + /// The type of the address that signes the extrinsic + pub address_ty: T::Type, + /// The type of the outermost Call enum. + pub call_ty: T::Type, + /// The type of the extrinsic's signature. + pub signature_ty: T::Type, + /// The type of the outermost Extra enum. + pub extra_ty: T::Type, + /// The transaction extensions in the order they appear in the extrinsic. + pub transaction_extensions: Vec>, +} + +impl IntoPortable for ExtrinsicMetadata { + type Output = ExtrinsicMetadata; + + fn into_portable(self, registry: &mut Registry) -> Self::Output { + ExtrinsicMetadata { + versions: self.versions, + address_ty: registry.register_type(&self.address_ty), + call_ty: registry.register_type(&self.call_ty), + signature_ty: registry.register_type(&self.signature_ty), + extra_ty: registry.register_type(&self.extra_ty), + transaction_extensions: registry.map_into_portable(self.transaction_extensions), + } + } +} + +/// Metadata of an extrinsic's transaction extension. +#[derive(Clone, PartialEq, Eq, Encode, Debug)] +#[cfg_attr(feature = "decode", derive(Decode))] +#[cfg_attr(feature = "serde_full", derive(Serialize))] +#[cfg_attr( + feature = "serde_full", + serde(bound(serialize = "T::Type: Serialize, T::String: Serialize")) +)] +pub struct TransactionExtensionMetadata { + /// The unique transaction extension identifier, which may be different from the type name. + pub identifier: T::String, + /// The type of the transaction extension, with the data to be included in the extrinsic. + pub ty: T::Type, + /// The type of the additional transaction data, with the data to be included in the signed payload. + pub additional_signed: T::Type, +} + +impl IntoPortable for TransactionExtensionMetadata { + type Output = TransactionExtensionMetadata; + + fn into_portable(self, registry: &mut Registry) -> Self::Output { + TransactionExtensionMetadata { + identifier: self.identifier.into_portable(registry), + ty: registry.register_type(&self.ty), + additional_signed: registry.register_type(&self.additional_signed), + } + } +} + +/// All metadata about an runtime pallet. +#[derive(Clone, PartialEq, Eq, Encode, Debug)] +#[cfg_attr(feature = "decode", derive(Decode))] +#[cfg_attr(feature = "serde_full", derive(Serialize))] +#[cfg_attr( + feature = "serde_full", + serde(bound(serialize = "T::Type: Serialize, T::String: Serialize")) +)] +pub struct PalletMetadata { + /// Pallet name. + pub name: T::String, + /// Pallet storage metadata. + pub storage: Option>, + /// Pallet calls metadata. + pub calls: Option>, + /// Pallet event metadata. + pub event: Option>, + /// Pallet constants metadata. + pub constants: Vec>, + /// Pallet error metadata. + pub error: Option>, + /// Config's trait associated types. + pub associated_types: Vec>, + /// Define the index of the pallet, this index will be used for the encoding of pallet event, + /// call and origin variants. + pub index: u8, + /// Pallet documentation. + pub docs: Vec, + /// Deprecation info + pub deprecation_info: DeprecationStatus, +} + +impl IntoPortable for PalletMetadata { + type Output = PalletMetadata; + + fn into_portable(self, registry: &mut Registry) -> Self::Output { + PalletMetadata { + name: self.name.into_portable(registry), + storage: self.storage.map(|storage| storage.into_portable(registry)), + calls: self.calls.map(|calls| calls.into_portable(registry)), + event: self.event.map(|event| event.into_portable(registry)), + constants: registry.map_into_portable(self.constants), + error: self.error.map(|error| error.into_portable(registry)), + associated_types: registry.map_into_portable(self.associated_types), + index: self.index, + docs: registry.map_into_portable(self.docs), + deprecation_info: self.deprecation_info.into_portable(registry), + } + } +} + +/// Metadata for all calls in a pallet +#[derive(Clone, PartialEq, Eq, Encode, Debug)] +#[cfg_attr(feature = "decode", derive(Decode))] +#[cfg_attr(feature = "serde_full", derive(Serialize))] +#[cfg_attr( + feature = "serde_full", + serde(bound(serialize = "T::Type: Serialize, T::String: Serialize")) +)] +pub struct PalletCallMetadata { + /// The corresponding enum type for the pallet call. + pub ty: T::Type, + /// Deprecation status of the pallet call + pub deprecation_info: DeprecationInfo, +} + +impl IntoPortable for PalletCallMetadata { + type Output = PalletCallMetadata; + + fn into_portable(self, registry: &mut Registry) -> Self::Output { + PalletCallMetadata { + ty: registry.register_type(&self.ty), + deprecation_info: self.deprecation_info.into_portable(registry), + } + } +} + +/// All metadata of the pallet's storage. +#[derive(Clone, PartialEq, Eq, Encode, Debug)] +#[cfg_attr(feature = "decode", derive(Decode))] +#[cfg_attr(feature = "serde_full", derive(Serialize))] +#[cfg_attr( + feature = "serde_full", + serde(bound(serialize = "T::Type: Serialize, T::String: Serialize")) +)] +pub struct PalletStorageMetadata { + /// The common prefix used by all storage entries. + pub prefix: T::String, + /// Metadata for all storage entries. + pub entries: Vec>, +} + +impl IntoPortable for PalletStorageMetadata { + type Output = PalletStorageMetadata; + + fn into_portable(self, registry: &mut Registry) -> Self::Output { + PalletStorageMetadata { + prefix: self.prefix.into_portable(registry), + entries: registry.map_into_portable(self.entries), + } + } +} + +/// Metadata about one storage entry. +#[derive(Clone, PartialEq, Eq, Encode, Debug)] +#[cfg_attr(feature = "decode", derive(Decode))] +#[cfg_attr(feature = "serde_full", derive(Serialize))] +#[cfg_attr( + feature = "serde_full", + serde(bound(serialize = "T::Type: Serialize, T::String: Serialize")) +)] +pub struct StorageEntryMetadata { + /// Variable name of the storage entry. + pub name: T::String, + /// An `Option` modifier of that storage entry. + pub modifier: StorageEntryModifier, + /// Type of the value stored in the entry. + pub ty: StorageEntryType, + /// Default value (SCALE encoded). + pub default: Vec, + /// Storage entry documentation. + pub docs: Vec, + /// Deprecation info + pub deprecation_info: DeprecationStatus, +} + +impl IntoPortable for StorageEntryMetadata { + type Output = StorageEntryMetadata; + + fn into_portable(self, registry: &mut Registry) -> Self::Output { + StorageEntryMetadata { + name: self.name.into_portable(registry), + modifier: self.modifier, + ty: self.ty.into_portable(registry), + default: self.default, + docs: registry.map_into_portable(self.docs), + deprecation_info: self.deprecation_info.into_portable(registry), + } + } +} + +/// Metadata about the pallet Event type. +#[derive(Clone, PartialEq, Eq, Encode, Debug)] +#[cfg_attr(feature = "decode", derive(Decode))] +#[cfg_attr(feature = "serde_full", derive(Serialize))] +#[cfg_attr( + feature = "serde_full", + serde(bound(serialize = "T::Type: Serialize, T::String: Serialize")) +)] +pub struct PalletEventMetadata { + /// The Event type. + pub ty: T::Type, + /// Deprecation info + pub deprecation_info: DeprecationInfo, +} + +impl IntoPortable for PalletEventMetadata { + type Output = PalletEventMetadata; + + fn into_portable(self, registry: &mut Registry) -> Self::Output { + PalletEventMetadata { + ty: registry.register_type(&self.ty), + deprecation_info: self.deprecation_info.into_portable(registry), + } + } +} + +/// Metadata about one pallet constant. +#[derive(Clone, PartialEq, Eq, Encode, Debug)] +#[cfg_attr(feature = "decode", derive(Decode))] +#[cfg_attr(feature = "serde_full", derive(Serialize))] +#[cfg_attr( + feature = "serde_full", + serde(bound(serialize = "T::Type: Serialize, T::String: Serialize")) +)] +pub struct PalletConstantMetadata { + /// Name of the pallet constant. + pub name: T::String, + /// Type of the pallet constant. + pub ty: T::Type, + /// Value stored in the constant (SCALE encoded). + pub value: Vec, + /// Documentation of the constant. + pub docs: Vec, + /// Deprecation info + pub deprecation_info: DeprecationStatus, +} + +impl IntoPortable for PalletConstantMetadata { + type Output = PalletConstantMetadata; + + fn into_portable(self, registry: &mut Registry) -> Self::Output { + PalletConstantMetadata { + name: self.name.into_portable(registry), + ty: registry.register_type(&self.ty), + value: self.value, + docs: registry.map_into_portable(self.docs), + deprecation_info: self.deprecation_info.into_portable(registry), + } + } +} + +/// Metadata about a pallet error. +#[derive(Clone, PartialEq, Eq, Encode, Debug)] +#[cfg_attr(feature = "decode", derive(Decode))] +#[cfg_attr(feature = "serde_full", derive(Serialize))] +#[cfg_attr( + feature = "serde_full", + serde(bound(serialize = "T::Type: Serialize, T::String: Serialize")) +)] +pub struct PalletErrorMetadata { + /// The error type information. + pub ty: T::Type, + /// Deprecation info + pub deprecation_info: DeprecationInfo, +} + +impl IntoPortable for PalletErrorMetadata { + type Output = PalletErrorMetadata; + + fn into_portable(self, registry: &mut Registry) -> Self::Output { + PalletErrorMetadata { + ty: registry.register_type(&self.ty), + deprecation_info: self.deprecation_info.into_portable(registry), + } + } +} + +/// Metadata of a pallet's associated type. +#[derive(Clone, PartialEq, Eq, Encode, Debug)] +#[cfg_attr(feature = "decode", derive(Decode))] +#[cfg_attr(feature = "serde_full", derive(Serialize))] +#[cfg_attr( + feature = "serde_full", + serde(bound(serialize = "T::Type: Serialize, T::String: Serialize")) +)] +pub struct PalletAssociatedTypeMetadata { + /// The name of the associated type. + pub name: T::String, + /// The type of the associated type. + pub ty: T::Type, + /// The documentation of the associated type. + pub docs: Vec, +} + +impl IntoPortable for PalletAssociatedTypeMetadata { + type Output = PalletAssociatedTypeMetadata; + + fn into_portable(self, registry: &mut Registry) -> Self::Output { + PalletAssociatedTypeMetadata { + name: self.name.into_portable(registry), + ty: registry.register_type(&self.ty), + docs: registry.map_into_portable(self.docs), + } + } +} + +/// Metadata for custom types. +/// +/// This map associates a string key to a `CustomValueMetadata`. +#[derive(Clone, PartialEq, Eq, Encode, Debug)] +#[cfg_attr(feature = "decode", derive(Decode))] +#[cfg_attr(feature = "serde_full", derive(Serialize))] +#[cfg_attr( + feature = "serde_full", + serde(bound(serialize = "T::Type: Serialize, T::String: Serialize")) +)] +pub struct CustomMetadata { + /// The custom map. + pub map: BTreeMap>, +} + +impl IntoPortable for CustomMetadata { + type Output = CustomMetadata; + + fn into_portable(self, registry: &mut Registry) -> Self::Output { + let map = self + .map + .into_iter() + .map(|(key, value)| (key.into_portable(registry), value.into_portable(registry))) + .collect(); + + CustomMetadata { map } + } +} + +/// The associated value of a custom metadata type. +#[derive(Clone, PartialEq, Eq, Encode, Debug)] +#[cfg_attr(feature = "decode", derive(Decode))] +#[cfg_attr(feature = "serde_full", derive(Serialize))] +#[cfg_attr( + feature = "serde_full", + serde(bound(serialize = "T::Type: Serialize, T::String: Serialize")) +)] +pub struct CustomValueMetadata { + /// The custom type. + pub ty: T::Type, + /// The custom value of this type. + pub value: Vec, +} + +impl IntoPortable for CustomValueMetadata { + type Output = CustomValueMetadata; + + fn into_portable(self, registry: &mut Registry) -> Self::Output { + CustomValueMetadata { + ty: registry.register_type(&self.ty), + value: self.value, + } + } +} + +/// The type of the outer enums. +#[derive(Clone, PartialEq, Eq, Encode, Debug)] +#[cfg_attr(feature = "decode", derive(Decode))] +#[cfg_attr(feature = "serde_full", derive(Serialize))] +#[cfg_attr( + feature = "serde_full", + serde(bound(serialize = "T::Type: Serialize, T::String: Serialize")) +)] +pub struct OuterEnums { + /// The type of the outer `RuntimeCall` enum. + pub call_enum_ty: T::Type, + /// The type of the outer `RuntimeEvent` enum. + pub event_enum_ty: T::Type, + /// The module error type of the + /// [`DispatchError::Module`](https://docs.rs/sp-runtime/24.0.0/sp_runtime/enum.DispatchError.html#variant.Module) variant. + /// + /// The `Module` variant will be 5 scale encoded bytes which are normally decoded into + /// an `{ index: u8, error: [u8; 4] }` struct. This type ID points to an enum type which instead + /// interprets the first `index` byte as a pallet variant, and the remaining `error` bytes as the + /// appropriate `pallet::Error` type. It is an equally valid way to decode the error bytes, and + /// can be more informative. + /// + /// # Note + /// + /// - This type cannot be used directly to decode `sp_runtime::DispatchError` from the + /// chain. It provides just the information needed to decode `sp_runtime::DispatchError::Module`. + /// - Decoding the 5 error bytes into this type will not always lead to all of the bytes being consumed; + /// many error types do not require all of the bytes to represent them fully. + pub error_enum_ty: T::Type, +} + +impl IntoPortable for OuterEnums { + type Output = OuterEnums; + + fn into_portable(self, registry: &mut Registry) -> Self::Output { + OuterEnums { + call_enum_ty: registry.register_type(&self.call_enum_ty), + event_enum_ty: registry.register_type(&self.event_enum_ty), + error_enum_ty: registry.register_type(&self.error_enum_ty), + } + } +} + +/// Deprecation status for an entry inside the metadata. +#[derive(Clone, PartialEq, Eq, Encode, Debug)] +#[cfg_attr(feature = "decode", derive(Decode))] +#[cfg_attr(feature = "serde_full", derive(Serialize))] +#[cfg_attr( + feature = "serde_full", + serde(bound(serialize = "T::Type: Serialize, T::String: Serialize")) +)] +pub enum DeprecationStatus { + /// Entry is not deprecated + NotDeprecated, + /// Deprecated without a note. + DeprecatedWithoutNote, + /// Entry is deprecated with an note and an optional `since` field. + Deprecated { + /// Note explaining the deprecation + note: T::String, + /// Optional value for denoting version when the deprecation occurred. + since: Option, + }, +} +impl IntoPortable for DeprecationStatus { + type Output = DeprecationStatus; + + fn into_portable(self, registry: &mut Registry) -> Self::Output { + match self { + Self::Deprecated { note, since } => { + let note = note.into_portable(registry); + let since = since.map(|x| x.into_portable(registry)); + DeprecationStatus::Deprecated { note, since } + } + Self::DeprecatedWithoutNote => DeprecationStatus::DeprecatedWithoutNote, + Self::NotDeprecated => DeprecationStatus::NotDeprecated, + } + } +} +/// Deprecation info for an enums/errors/calls. +/// Denotes full/partial deprecation of the type +#[derive(Clone, PartialEq, Eq, Encode, Debug)] +#[cfg_attr(feature = "decode", derive(Decode))] +#[cfg_attr(feature = "serde_full", derive(Serialize))] +#[cfg_attr( + feature = "serde_full", + serde(bound(serialize = "T::Type: Serialize, T::String: Serialize")) +)] +pub enum DeprecationInfo { + /// Type is not deprecated + NotDeprecated, + /// Entry is fully deprecated. + ItemDeprecated(DeprecationStatus), + /// Entry is partially deprecated. + VariantsDeprecated(BTreeMap>), +} +impl IntoPortable for DeprecationInfo { + type Output = DeprecationInfo; + + fn into_portable(self, registry: &mut Registry) -> Self::Output { + match self { + Self::VariantsDeprecated(entries) => { + let entries = entries + .into_iter() + .map(|(k, entry)| (k, entry.into_portable(registry))); + DeprecationInfo::VariantsDeprecated(entries.collect()) + } + Self::ItemDeprecated(deprecation) => { + DeprecationInfo::ItemDeprecated(deprecation.into_portable(registry)) + } + Self::NotDeprecated => DeprecationInfo::NotDeprecated, + } + } +}