scale_encode/impls/
variant.rs

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