frame_decode/methods/
custom_value_decoder.rs

1// Copyright (C) 2022-2025 Parity Technologies (UK) Ltd. (admin@parity.io)
2// This file is a part of the frame-decode 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::methods::custom_value_type_info::{
17    CustomValueInfo, CustomValueInfoError, CustomValueTypeInfo,
18};
19use crate::utils::{DecodeErrorTrace, decode_with_error_tracing};
20use alloc::vec::Vec;
21use scale_type_resolver::TypeResolver;
22
23/// An error returned trying to decode a custom value.
24#[non_exhaustive]
25#[allow(missing_docs)]
26#[derive(Clone, Debug, thiserror::Error)]
27pub enum CustomValueDecodeError<TypeId> {
28    #[error("Cannot get custom value info: {0}")]
29    CannotGetInfo(CustomValueInfoError),
30    #[error("Cannot decode custom value: {reason}")]
31    CannotDecodeValue {
32        ty: TypeId,
33        reason: DecodeErrorTrace,
34    },
35    #[error("There were leftover bytes attempting to decode the custom value")]
36    LeftoverBytes { bytes: Vec<u8> },
37}
38
39/// Decode a custom value from the provided information.
40pub fn decode_custom_value<'info, 'resolver, Info, Resolver, V>(
41    name: &str,
42    info: &'info Info,
43    type_resolver: &'resolver V::TypeResolver,
44    visitor: V,
45) -> Result<V::Value<'info, 'resolver>, CustomValueDecodeError<Info::TypeId>>
46where
47    Info: CustomValueTypeInfo,
48    Info::TypeId: Clone + core::fmt::Debug,
49    Resolver: TypeResolver<TypeId = Info::TypeId>,
50    V: scale_decode::Visitor<TypeResolver = Resolver>,
51    V::Error: core::fmt::Debug,
52{
53    let info = info
54        .custom_value_info(name)
55        .map_err(CustomValueDecodeError::CannotGetInfo)?;
56
57    decode_custom_value_with_info(&info, type_resolver, visitor)
58}
59
60/// Decode a custom value given the [`CustomValueInfo`] and a resolver to resolve the custom value type.
61pub fn decode_custom_value_with_info<'info, 'resolver, V>(
62    info: &CustomValueInfo<'info, <V::TypeResolver as TypeResolver>::TypeId>,
63    type_resolver: &'resolver V::TypeResolver,
64    visitor: V,
65) -> Result<
66    V::Value<'info, 'resolver>,
67    CustomValueDecodeError<<V::TypeResolver as TypeResolver>::TypeId>,
68>
69where
70    V: scale_decode::Visitor,
71    V::Error: core::fmt::Debug,
72{
73    let type_id = info.type_id.clone();
74    let cursor = &mut &*info.bytes;
75
76    let value = decode_with_error_tracing(cursor, type_id.clone(), type_resolver, visitor)
77        .map_err(|e| CustomValueDecodeError::CannotDecodeValue {
78            ty: type_id,
79            reason: e,
80        })?;
81
82    if !cursor.is_empty() {
83        Err(CustomValueDecodeError::LeftoverBytes {
84            bytes: cursor.to_vec(),
85        })
86    } else {
87        Ok(value)
88    }
89}