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}