scale_encode/error/
mod.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
16//! An error that is emitted whenever some encoding fails.
17mod context;
18
19use alloc::{borrow::Cow, boxed::Box, string::String};
20use core::fmt::Display;
21
22pub use context::{Context, Location};
23
24/// An error produced while attempting to encode some type.
25#[derive(Debug)]
26pub struct Error {
27    context: Context,
28    kind: ErrorKind,
29}
30
31impl core::error::Error for Error {}
32
33impl Error {
34    /// Construct a new error given an error kind.
35    pub fn new(kind: ErrorKind) -> Error {
36        Error {
37            context: Context::new(),
38            kind,
39        }
40    }
41    /// Construct a new, custom error.
42    pub fn custom(error: impl core::error::Error + Send + Sync + 'static) -> Error {
43        Error::new(ErrorKind::Custom(Box::new(error)))
44    }
45    /// Construct a custom error from a static string.
46    pub fn custom_str(error: &'static str) -> Error {
47        #[derive(Debug, thiserror::Error)]
48        #[error("{0}")]
49        pub struct StrError(pub &'static str);
50
51        Error::new(ErrorKind::Custom(Box::new(StrError(error))))
52    }
53    /// Construct a custom error from an owned string.
54    pub fn custom_string(error: String) -> Error {
55        #[derive(Debug, thiserror::Error)]
56        #[error("{0}")]
57        pub struct StringError(String);
58
59        Error::new(ErrorKind::Custom(Box::new(StringError(error))))
60    }
61    /// Retrieve more information about what went wrong.
62    pub fn kind(&self) -> &ErrorKind {
63        &self.kind
64    }
65    /// Retrieve details about where the error occurred.
66    pub fn context(&self) -> &Context {
67        &self.context
68    }
69    /// Give some context to the error.
70    pub fn at(mut self, loc: Location) -> Self {
71        self.context.push(loc);
72        Error {
73            context: self.context,
74            kind: self.kind,
75        }
76    }
77    /// Note which sequence index the error occurred in.
78    pub fn at_idx(mut self, idx: usize) -> Self {
79        self.context.push(Location::idx(idx));
80        Error {
81            context: self.context,
82            kind: self.kind,
83        }
84    }
85    /// Note which field the error occurred in.
86    pub fn at_field(mut self, field: impl Into<Cow<'static, str>>) -> Self {
87        self.context.push(Location::field(field));
88        Error {
89            context: self.context,
90            kind: self.kind,
91        }
92    }
93    /// Note which variant the error occurred in.
94    pub fn at_variant(mut self, variant: impl Into<Cow<'static, str>>) -> Self {
95        self.context.push(Location::variant(variant));
96        Error {
97            context: self.context,
98            kind: self.kind,
99        }
100    }
101}
102
103impl Display for Error {
104    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
105        let path = self.context.path();
106        let kind = &self.kind;
107        write!(f, "Error at {path}: {kind}")
108    }
109}
110
111/// The underlying nature of the error.
112#[derive(Debug, thiserror::Error)]
113pub enum ErrorKind {
114    /// There was an error resolving the type via the given [`crate::TypeResolver`].
115    #[error("Failed to resolve type: {0}")]
116    TypeResolvingError(String),
117    /// Cannot find a given type.
118    #[error("Cannot find type with identifier {0}")]
119    TypeNotFound(String),
120    /// Cannot encode the actual type given into the target type ID.
121    #[error("Cannot encode {actual:?} into type with ID {expected_id}")]
122    WrongShape {
123        /// The actual kind we have to encode
124        actual: Kind,
125        /// Identifier for the expected type
126        expected_id: String,
127    },
128    /// The types line up, but the expected length of the target type is different from the length of the input value.
129    #[error("Cannot encode to type; expected length {expected_len} but got length {actual_len}")]
130    WrongLength {
131        /// Length we have
132        actual_len: usize,
133        /// Length expected for type.
134        expected_len: usize,
135    },
136    /// We cannot encode the number given into the target type; it's out of range.
137    #[error("Number {value} is out of range for target type with identifier {expected_id}")]
138    NumberOutOfRange {
139        /// A string represenatation of the numeric value that was out of range.
140        value: String,
141        /// Identifier for the expected numeric type that we tried to encode it to.
142        expected_id: String,
143    },
144    /// Cannot find a variant with a matching name on the target type.
145    #[error("Variant {name} does not exist on type with identifier {expected_id}")]
146    CannotFindVariant {
147        /// Variant name we can't find in the expected type.
148        name: String,
149        /// Identifier for the expected type.
150        expected_id: String,
151    },
152    /// Cannot find a field on our source type that's needed for the target type.
153    #[error("Field {name} does not exist in our source struct")]
154    CannotFindField {
155        /// Name of the field which was not provided.
156        name: String,
157    },
158    /// A custom error.
159    #[error("Custom error: {0}")]
160    Custom(Box<dyn core::error::Error + Send + Sync + 'static>),
161}
162
163/// The kind of type that we're trying to encode.
164#[allow(missing_docs)]
165#[derive(Copy, Clone, PartialEq, Eq, Debug)]
166pub enum Kind {
167    Struct,
168    Tuple,
169    Variant,
170    Array,
171    BitSequence,
172    Bool,
173    Char,
174    Str,
175    Number,
176}