scale_encode/impls/composite.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 crate::{
17 error::{Error, ErrorKind, Kind, Location},
18 EncodeAsType, Field, FieldIter, TypeResolver,
19};
20use alloc::collections::BTreeMap;
21use alloc::{format, string::ToString, vec::Vec};
22use scale_type_resolver::visitor;
23
24/// This trait exists to get around object safety issues using [`EncodeAsType`].
25/// It's object safe and automatically implemented for any type which implements
26/// [`EncodeAsType`]. We need this to construct generic [`Composite`] types.
27trait EncodeAsTypeWithResolver<R: TypeResolver> {
28 fn encode_as_type_with_resolver_to(
29 &self,
30 type_id: R::TypeId,
31 types: &R,
32 out: &mut Vec<u8>,
33 ) -> Result<(), Error>;
34}
35impl<T: EncodeAsType, R: TypeResolver> EncodeAsTypeWithResolver<R> for T {
36 fn encode_as_type_with_resolver_to(
37 &self,
38 type_id: R::TypeId,
39 types: &R,
40 out: &mut Vec<u8>,
41 ) -> Result<(), Error> {
42 self.encode_as_type_to(type_id, types, out)
43 }
44}
45
46/// A struct representing a single composite field. To be used in conjunction
47/// with the [`Composite`] struct to construct generic composite shaped types.
48/// this basically takes a type which implements [`EncodeAsType`] and turns it
49/// into something object safe.
50pub struct CompositeField<'a, R> {
51 val: &'a dyn EncodeAsTypeWithResolver<R>,
52}
53
54impl<'a, R> Copy for CompositeField<'a, R> {}
55impl<'a, R> Clone for CompositeField<'a, R> {
56 fn clone(&self) -> Self {
57 *self
58 }
59}
60impl<'a, R> core::fmt::Debug for CompositeField<'a, R> {
61 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
62 f.write_str("CompositeField")
63 }
64}
65
66impl<'a, R: TypeResolver> CompositeField<'a, R> {
67 /// Construct a new composite field given some type which implements
68 /// [`EncodeAsType`].
69 pub fn new<T: EncodeAsType>(val: &'a T) -> Self {
70 CompositeField { val }
71 }
72
73 /// SCALE encode this composite field to bytes based on the underlying type.
74 pub fn encode_composite_field_to(
75 &self,
76 type_id: R::TypeId,
77 types: &R,
78 out: &mut Vec<u8>,
79 ) -> Result<(), Error> {
80 self.val
81 .encode_as_type_with_resolver_to(type_id, types, out)
82 }
83}
84
85/// This type represents named or unnamed composite values, and can be used to help generate
86/// `EncodeAsType` impls. It's primarily used by the exported macros to do just that.
87///
88/// ```rust
89/// use scale_encode::{
90/// Error, EncodeAsType, Composite, CompositeField, TypeResolver
91/// };
92///
93/// struct MyType {
94/// foo: bool,
95/// bar: u64,
96/// wibble: String
97/// }
98///
99/// impl EncodeAsType for MyType {
100/// fn encode_as_type_to<R: TypeResolver>(
101/// &self,
102/// type_id: R::TypeId,
103/// types: &R,
104/// out: &mut Vec<u8>
105/// ) -> Result<(), Error> {
106/// Composite::new([
107/// (Some("foo"), CompositeField::new(&self.foo)),
108/// (Some("bar"), CompositeField::new(&self.bar)),
109/// (Some("wibble"), CompositeField::new(&self.wibble))
110/// ].into_iter()).encode_composite_as_type_to(type_id, types, out)
111/// }
112/// }
113/// ```
114///
115/// [`Composite`] cannot implement [`EncodeAsType`] itself, because it is tied to being
116/// encoded with a specific `R: TypeResolver`, whereas things implementing [`EncodeAsType`]
117/// need to be encodable using _any_ [`TypeResolver`]. This is ultimately because
118/// [`EncodeAsType`] is not object safe, which prevents it from being used to describe
119/// [`CompositeFields`][CompositeField].
120pub struct Composite<R, Vals> {
121 vals: Vals,
122 marker: core::marker::PhantomData<R>,
123}
124
125impl<'a, R, Vals> Composite<R, Vals>
126where
127 R: TypeResolver + 'a,
128 Vals: ExactSizeIterator<Item = (Option<&'a str>, CompositeField<'a, R>)> + Clone,
129{
130 /// Construct a new [`Composite`] type by providing an iterator over
131 /// the fields that it contains.
132 ///
133 /// ```rust
134 /// use scale_encode::{ Composite, CompositeField };
135 /// use scale_info::PortableRegistry;
136 ///
137 /// Composite::<PortableRegistry, _>::new([
138 /// (Some("foo"), CompositeField::new(&123)),
139 /// (Some("bar"), CompositeField::new(&"hello"))
140 /// ].into_iter());
141 /// ```
142 pub fn new(vals: Vals) -> Self {
143 Composite {
144 vals,
145 marker: core::marker::PhantomData,
146 }
147 }
148
149 /// A shortcut for [`Self::encode_composite_as_type_to()`] which internally
150 /// allocates a [`Vec`] and returns it.
151 pub fn encode_composite_as_type(
152 &self,
153 type_id: R::TypeId,
154 types: &R,
155 ) -> Result<Vec<u8>, Error> {
156 let mut out = Vec::new();
157 self.encode_composite_as_type_to(type_id, types, &mut out)?;
158 Ok(out)
159 }
160
161 /// Encode this composite value as the provided type to the output bytes.
162 pub fn encode_composite_as_type_to(
163 &self,
164 type_id: R::TypeId,
165 types: &R,
166 out: &mut Vec<u8>,
167 ) -> Result<(), Error> {
168 let vals_iter = self.vals.clone();
169 let vals_iter_len = vals_iter.len();
170
171 // Skip through any single field composites/tuples without names. If there
172 // are names, we may want to line up input field(s) on them.
173 let type_id = skip_through_single_unnamed_fields(type_id, types);
174
175 let v = visitor::new(
176 (type_id.clone(), out, vals_iter),
177 |(type_id, out, mut vals_iter), _| {
178 // Rather than immediately giving up, we should at least see whether
179 // we can skip one level in to our value and encode that.
180 if vals_iter_len == 1 {
181 return vals_iter
182 .next()
183 .expect("1 value expected")
184 .1
185 .encode_composite_field_to(type_id, types, out);
186 }
187
188 // If we get here, then it means the value we were given had more than
189 // one field, and the type we were given was ultimately some one-field thing
190 // that contained a non composite/tuple type, so it would never work out.
191 Err(Error::new(ErrorKind::WrongShape {
192 actual: Kind::Struct,
193 expected_id: format!("{type_id:?}"),
194 }))
195 },
196 )
197 .visit_not_found(|(type_id, _, _)| {
198 Err(Error::new(ErrorKind::TypeNotFound(format!("{type_id:?}"))))
199 })
200 .visit_composite(|(type_id, out, mut vals_iter), _, mut fields| {
201 // If vals are named, we may need to line them up with some named composite.
202 // If they aren't named, we only care about lining up based on matching lengths.
203 let is_named_vals = vals_iter.clone().any(|(name, _)| name.is_some());
204
205 // If there is exactly one val that isn't named, then we know it won't line
206 // up with this composite then, so try encoding one level in.
207 if !is_named_vals && vals_iter_len == 1 {
208 return vals_iter
209 .next()
210 .expect("1 value expected")
211 .1
212 .encode_composite_field_to(type_id, types, out);
213 }
214
215 self.encode_composite_fields_to(&mut fields, types, out)
216 })
217 .visit_tuple(|(type_id, out, mut vals_iter), type_ids| {
218 // If there is exactly one val, it won't line up with the tuple then, so
219 // try encoding one level in instead.
220 if vals_iter_len == 1 {
221 return vals_iter
222 .next()
223 .unwrap()
224 .1
225 .encode_composite_field_to(type_id, types, out);
226 }
227
228 let mut fields = type_ids.map(Field::unnamed);
229 self.encode_composite_fields_to(
230 &mut fields as &mut dyn FieldIter<'_, R::TypeId>,
231 types,
232 out,
233 )
234 });
235
236 super::resolve_type_and_encode(types, type_id, v)
237 }
238
239 /// A shortcut for [`Self::encode_composite_fields_to()`] which internally
240 /// allocates a [`Vec`] and returns it.
241 pub fn encode_composite_fields(
242 &self,
243 fields: &mut dyn FieldIter<'_, R::TypeId>,
244 types: &R,
245 ) -> Result<Vec<u8>, Error> {
246 let mut out = Vec::new();
247 self.encode_composite_fields_to(fields, types, &mut out)?;
248 Ok(out)
249 }
250
251 /// Encode the composite fields as the provided field description to the output bytes
252 pub fn encode_composite_fields_to(
253 &self,
254 fields: &mut dyn FieldIter<'_, R::TypeId>,
255 types: &R,
256 out: &mut Vec<u8>,
257 ) -> Result<(), Error> {
258 let vals_iter = self.vals.clone();
259
260 // Most of the time there aren't too many fields, so avoid allocation in most cases:
261 let fields = smallvec::SmallVec::<[_; 16]>::from_iter(fields);
262
263 // Both the target and source type have to have named fields for us to use
264 // names to line them up.
265 let is_named = {
266 let is_target_named = fields.iter().any(|f| f.name.is_some());
267 let is_source_named = vals_iter.clone().any(|(name, _)| name.is_some());
268 is_target_named && is_source_named
269 };
270
271 if is_named {
272 // target + source fields are named, so hash source values by name and
273 // then encode to the target type by matching the names. If fields are
274 // named, we don't even mind if the number of fields doesn't line up;
275 // we just ignore any fields we provided that aren't needed.
276 let source_fields_by_name: BTreeMap<&str, CompositeField<'a, R>> = vals_iter
277 .map(|(name, val)| (name.unwrap_or(""), val))
278 .collect();
279
280 for field in fields {
281 // Find the field in our source type:
282 let name = field.name.unwrap_or("");
283 let Some(value) = source_fields_by_name.get(name) else {
284 return Err(Error::new(ErrorKind::CannotFindField {
285 name: name.to_string(),
286 }));
287 };
288
289 // Encode the value to the output:
290 value
291 .encode_composite_field_to(field.id, types, out)
292 .map_err(|e| e.at_field(name.to_string()))?;
293 }
294
295 Ok(())
296 } else {
297 let fields_len = fields.len();
298
299 // target fields aren't named, so encode by order only. We need the field length
300 // to line up for this to work.
301 if fields_len != vals_iter.len() {
302 return Err(Error::new(ErrorKind::WrongLength {
303 actual_len: vals_iter.len(),
304 expected_len: fields_len,
305 }));
306 }
307
308 for (idx, (field, (name, val))) in fields.iter().zip(vals_iter).enumerate() {
309 val.encode_composite_field_to(field.id.clone(), types, out)
310 .map_err(|e| {
311 let loc = if let Some(name) = name {
312 Location::field(name.to_string())
313 } else {
314 Location::idx(idx)
315 };
316 e.at(loc)
317 })?;
318 }
319 Ok(())
320 }
321 }
322}
323
324// Single unnamed fields carry no useful information and can be skipped through.
325// Single named fields may still be useful to line up with named composites.
326fn skip_through_single_unnamed_fields<R: TypeResolver>(type_id: R::TypeId, types: &R) -> R::TypeId {
327 let v = visitor::new(type_id.clone(), |type_id, _| type_id)
328 .visit_composite(|type_id, _, fields| {
329 // If exactly 1 unnamed field, recurse into it, else return current type ID.
330 let Some(f) = fields.next() else {
331 return type_id;
332 };
333 if fields.next().is_some() || f.name.is_some() {
334 return type_id;
335 };
336 skip_through_single_unnamed_fields(f.id, types)
337 })
338 .visit_tuple(|type_id, type_ids| {
339 // Else if exactly 1 tuple entry, recurse into it, else return current type ID.
340 let Some(new_type_id) = type_ids.next() else {
341 return type_id;
342 };
343 if type_ids.next().is_some() {
344 return type_id;
345 };
346 skip_through_single_unnamed_fields(new_type_id, types)
347 });
348
349 types.resolve_type(type_id.clone(), v).unwrap_or(type_id)
350}