scale_encode/impls/variant.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119
// Copyright (C) 2023 Parity Technologies (UK) Ltd. (admin@parity.io)
// This file is a part of the scale-encode crate.
//
// 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.
use super::composite::{Composite, CompositeField};
use crate::error::{Error, ErrorKind, Kind};
use alloc::{format, string::ToString, vec::Vec};
use codec::Encode;
use scale_type_resolver::{visitor, TypeResolver};
/// This type represents named or unnamed composite values, and can be used
/// to help generate `EncodeAsType` impls. It's primarily used by the exported
/// macros to do just that.
///
/// ```rust
/// use scale_encode::{
/// Error, EncodeAsType, Composite, CompositeField, Variant, TypeResolver
/// };
///
/// enum MyType {
/// SomeField(bool),
/// OtherField { foo: u64, bar: String }
/// }
///
/// impl EncodeAsType for MyType {
/// fn encode_as_type_to<R: TypeResolver>(
/// &self,
/// type_id: R::TypeId,
/// types: &R,
/// out: &mut Vec<u8>
/// ) -> Result<(), Error> {
/// match self {
/// MyType::SomeField(b) => Variant {
/// name: "SomeField",
/// fields: Composite::new([
/// (None, CompositeField::new(b)),
/// ].into_iter())
/// }.encode_variant_as_type_to(type_id, types, out),
/// MyType::OtherField { foo, bar } => Variant {
/// name: "OtherField",
/// fields: Composite::new([
/// (Some("foo"), CompositeField::new(foo)),
/// (Some("bar"), CompositeField::new(bar))
/// ].into_iter())
/// }.encode_variant_as_type_to(type_id, types, out)
/// }
/// }
/// }
/// ```
pub struct Variant<'a, R, Vals> {
/// The name of the variant we'll try to encode into.
pub name: &'a str,
/// The fields of the variant that we wish to encode.
pub fields: Composite<R, Vals>,
}
impl<'a, R, Vals> Variant<'a, R, Vals>
where
R: TypeResolver + 'a,
Vals: ExactSizeIterator<Item = (Option<&'a str>, CompositeField<'a, R>)> + Clone,
{
/// A shortcut for [`Self::encode_variant_as_type_to()`] which internally
/// allocates a [`Vec`] and returns it.
pub fn encode_variant_as_type(&self, type_id: R::TypeId, types: &R) -> Result<Vec<u8>, Error> {
let mut out = Vec::new();
self.encode_variant_as_type_to(type_id, types, &mut out)?;
Ok(out)
}
/// Encode the variant as the provided type to the output bytes.
pub fn encode_variant_as_type_to(
&self,
type_id: R::TypeId,
types: &R,
out: &mut Vec<u8>,
) -> Result<(), Error> {
let type_id = super::find_single_entry_with_same_repr(type_id, types);
let v = visitor::new(type_id.clone(), |type_id, _| {
Err(Error::new(ErrorKind::WrongShape {
actual: Kind::Str,
expected_id: format!("{type_id:?}"),
}))
})
.visit_variant(|type_id, _, vars| {
let mut res = None;
for var in vars {
if var.name == self.name {
res = Some(var);
break;
}
}
let Some(mut var) = res else {
return Err(Error::new(ErrorKind::CannotFindVariant {
name: self.name.to_string(),
expected_id: format!("{type_id:?}"),
}));
};
var.index.encode_to(out);
self.fields
.encode_composite_fields_to(&mut var.fields, types, out)
});
super::resolve_type_and_encode(types, type_id, v)
}
}