scale_decode/error/
mod.rs

1// Copyright (C) 2023 Parity Technologies (UK) Ltd. (admin@parity.io)
2// This file is a part of the scale-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
16//! An error that is emitted whenever some decoding fails.
17mod context;
18
19pub use context::{Context, Location};
20
21use crate::visitor::DecodeError;
22use alloc::{borrow::Cow, boxed::Box, string::String, vec::Vec};
23use core::fmt::Display;
24
25/// An error produced while attempting to decode some type.
26#[derive(Debug)]
27pub struct Error {
28    context: Context,
29    kind: ErrorKind,
30}
31
32impl core::error::Error for Error {}
33
34impl Error {
35    /// Construct a new error given an error kind.
36    pub fn new(kind: ErrorKind) -> Error {
37        Error { context: Context::new(), kind }
38    }
39    /// Construct a new, custom error.
40    pub fn custom(error: impl core::error::Error + Send + Sync + 'static) -> Error {
41        Error::new(ErrorKind::Custom(Box::new(error)))
42    }
43    /// Construct a custom error from a static string.
44    pub fn custom_str(error: &'static str) -> Error {
45        #[derive(Debug, thiserror::Error)]
46        #[error("{0}")]
47        pub struct StrError(pub &'static str);
48
49        Error::new(ErrorKind::Custom(Box::new(StrError(error))))
50    }
51    /// Construct a custom error from an owned string.
52    pub fn custom_string(error: String) -> Error {
53        #[derive(Debug, thiserror::Error)]
54        #[error("{0}")]
55        pub struct StringError(String);
56
57        Error::new(ErrorKind::Custom(Box::new(StringError(error))))
58    }
59    /// Retrieve more information about what went wrong.
60    pub fn kind(&self) -> &ErrorKind {
61        &self.kind
62    }
63    /// Retrieve details about where the error occurred.
64    pub fn context(&self) -> &Context {
65        &self.context
66    }
67    /// Give some context to the error.
68    pub fn at(mut self, loc: Location) -> Self {
69        self.context.push(loc);
70        Error { context: self.context, kind: self.kind }
71    }
72    /// Note which sequence index the error occurred in.
73    pub fn at_idx(mut self, idx: usize) -> Self {
74        self.context.push(Location::idx(idx));
75        Error { context: self.context, kind: self.kind }
76    }
77    /// Note which field the error occurred in.
78    pub fn at_field(mut self, field: impl Into<Cow<'static, str>>) -> Self {
79        self.context.push(Location::field(field));
80        Error { context: self.context, kind: self.kind }
81    }
82    /// Note which variant the error occurred in.
83    pub fn at_variant(mut self, variant: impl Into<Cow<'static, str>>) -> Self {
84        self.context.push(Location::variant(variant));
85        Error { context: self.context, kind: self.kind }
86    }
87}
88
89impl Display for Error {
90    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
91        let path = self.context.path();
92        let kind = &self.kind;
93        write!(f, "Error at {path}: {kind}")
94    }
95}
96
97impl From<DecodeError> for Error {
98    fn from(err: DecodeError) -> Error {
99        Error::new(err.into())
100    }
101}
102
103impl From<codec::Error> for Error {
104    fn from(err: codec::Error) -> Error {
105        let err: DecodeError = err.into();
106        Error::new(err.into())
107    }
108}
109
110/// The underlying nature of the error.
111#[derive(Debug, thiserror::Error)]
112pub enum ErrorKind {
113    /// Something went wrong decoding the bytes based on the type
114    /// and type registry provided.
115    #[error("Error decoding bytes given the type ID and registry provided: {_0}")]
116    VisitorDecodeError(#[from] DecodeError),
117    /// We cannot decode the number seen into the target type; it's out of range.
118    #[error("Number {value} is out of range")]
119    NumberOutOfRange {
120        /// A string representation of the numeric value that was out of range.
121        value: String,
122    },
123    /// We cannot find the variant we're trying to decode from in the target type.
124    #[error("Cannot find variant {got}; expects one of {expected:?}")]
125    CannotFindVariant {
126        /// The variant that we are given back from the encoded bytes.
127        got: String,
128        /// The possible variants that we can decode into.
129        expected: Vec<&'static str>,
130    },
131    /// The types line up, but the expected length of the target type is different from the length of the input value.
132    #[error("Cannot decode from type; expected length {expected_len} but got length {actual_len}")]
133    WrongLength {
134        /// Length of the type we are trying to decode from
135        actual_len: usize,
136        /// Length fo the type we're trying to decode into
137        expected_len: usize,
138    },
139    /// Cannot find a field that we need to decode to our target type
140    #[error("Field {name} does not exist in our encoded data")]
141    CannotFindField {
142        /// Name of the field which was not provided.
143        name: String,
144    },
145    /// A custom error.
146    #[error("Custom error: {0}")]
147    Custom(Box<dyn core::error::Error + Send + Sync + 'static>),
148}