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}