Skip to content

Commit

Permalink
use core::error::Error and remove std feature (#67)
Browse files Browse the repository at this point in the history
* use `core::error::Error` and remove std feature

* make crate no-std

* Update scale-decode/src/visitor/decode.rs

* Update scale-decode/src/visitor/decode.rs

* Update scale-decode/src/visitor/decode.rs

* Update scale-decode/src/visitor/decode.rs

* Update scale-decode/src/visitor/decode.rs

* fix doc links
  • Loading branch information
niklasad1 authored Nov 7, 2024
1 parent dc7305d commit 8654c98
Show file tree
Hide file tree
Showing 6 changed files with 62 additions and 97 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ repository = "https://github.com/paritytech/scale-decode"
homepage = "https://www.parity.io/"
keywords = ["parity", "scale", "decoding"]
include = ["Cargo.toml", "src/**/*.rs", "README.md", "LICENSE"]
rust-version = "1.81.0"

[workspace.dependencies]
scale-decode = { version = "0.14.0", path = "scale-decode" }
Expand Down
1 change: 1 addition & 0 deletions scale-decode-derive/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ repository.workspace = true
homepage.workspace = true
keywords.workspace = true
include.workspace = true
rust-version.workspace = true

[lib]
proc-macro = true
Expand Down
8 changes: 3 additions & 5 deletions scale-decode/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,10 @@ repository.workspace = true
homepage.workspace = true
keywords.workspace = true
include.workspace = true
rust-version.workspace = true

[features]
default = ["std", "derive", "primitive-types"]

# Activates std feature.
std = []
default = ["derive", "primitive-types"]

# Impls for primitive-types.
primitive-types = ["dep:primitive-types"]
Expand All @@ -31,8 +29,8 @@ scale-bits = { version = "0.6.0", default-features = false }
scale-decode-derive = { workspace = true, optional = true }
primitive-types = { version = "0.13.1", optional = true, default-features = false }
smallvec = "1.10.0"
derive_more = { version = "1.0.0", default-features = false, features = ["from", "display"] }
scale-type-resolver = { version = "0.2.0", default-features = false }
thiserror = { version = "2.0.0", default-features = false }

[dev-dependencies]
scale-info = { version = "2.7.0", default-features = false, features = ["bit-vec", "derive"] }
Expand Down
68 changes: 15 additions & 53 deletions scale-decode/src/error/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,33 +29,30 @@ pub struct Error {
kind: ErrorKind,
}

#[cfg(feature = "std")]
impl std::error::Error for Error {}
impl core::error::Error for Error {}

impl Error {
/// Construct a new error given an error kind.
pub fn new(kind: ErrorKind) -> Error {
Error { context: Context::new(), kind }
}
/// Construct a new, custom error.
pub fn custom(error: impl CustomError) -> Error {
pub fn custom(error: impl core::error::Error + Send + Sync + 'static) -> Error {
Error::new(ErrorKind::Custom(Box::new(error)))
}
/// Construct a custom error from a static string.
pub fn custom_str(error: &'static str) -> Error {
#[derive(derive_more::Display, Debug)]
#[derive(Debug, thiserror::Error)]
#[error("{0}")]
pub struct StrError(pub &'static str);
#[cfg(feature = "std")]
impl std::error::Error for StrError {}

Error::new(ErrorKind::Custom(Box::new(StrError(error))))
}
/// Construct a custom error from an owned string.
pub fn custom_string(error: String) -> Error {
#[derive(derive_more::Display, Debug)]
#[derive(Debug, thiserror::Error)]
#[error("{0}")]
pub struct StringError(String);
#[cfg(feature = "std")]
impl std::error::Error for StringError {}

Error::new(ErrorKind::Custom(Box::new(StringError(error))))
}
Expand Down Expand Up @@ -111,76 +108,41 @@ impl From<codec::Error> for Error {
}

/// The underlying nature of the error.
#[derive(Debug, derive_more::From, derive_more::Display)]
#[derive(Debug, thiserror::Error)]
pub enum ErrorKind {
/// Something went wrong decoding the bytes based on the type
/// and type registry provided.
#[from]
#[display("Error decoding bytes given the type ID and registry provided: {_0}")]
VisitorDecodeError(DecodeError),
#[error("Error decoding bytes given the type ID and registry provided: {_0}")]
VisitorDecodeError(#[from] DecodeError),
/// We cannot decode the number seen into the target type; it's out of range.
#[display("Number {value} is out of range")]
#[error("Number {value} is out of range")]
NumberOutOfRange {
/// A string representation of the numeric value that was out of range.
value: String,
},
/// We cannot find the variant we're trying to decode from in the target type.
#[display("Cannot find variant {got}; expects one of {expected:?}")]
#[error("Cannot find variant {got}; expects one of {expected:?}")]
CannotFindVariant {
/// The variant that we are given back from the encoded bytes.
got: String,
/// The possible variants that we can decode into.
expected: Vec<&'static str>,
},
/// The types line up, but the expected length of the target type is different from the length of the input value.
#[display(
"Cannot decode from type; expected length {expected_len} but got length {actual_len}"
)]
#[error("Cannot decode from type; expected length {expected_len} but got length {actual_len}")]
WrongLength {
/// Length of the type we are trying to decode from
actual_len: usize,
/// Length fo the type we're trying to decode into
expected_len: usize,
},
/// Cannot find a field that we need to decode to our target type
#[display("Field {name} does not exist in our encoded data")]
#[error("Field {name} does not exist in our encoded data")]
CannotFindField {
/// Name of the field which was not provided.
name: String,
},
/// A custom error.
#[from]
#[display("Custom error: {_0}")]
Custom(Box<dyn CustomError>),
}

/// Anything implementing this trait can be used in [`ErrorKind::Custom`].
#[cfg(feature = "std")]
pub trait CustomError: std::error::Error + Send + Sync + 'static {}
#[cfg(feature = "std")]
impl<T: std::error::Error + Send + Sync + 'static> CustomError for T {}

/// Anything implementing this trait can be used in [`ErrorKind::Custom`].
#[cfg(not(feature = "std"))]
pub trait CustomError: core::fmt::Debug + core::fmt::Display + Send + Sync + 'static {}
#[cfg(not(feature = "std"))]
impl<T: core::fmt::Debug + core::fmt::Display + Send + Sync + 'static> CustomError for T {}

#[cfg(test)]
mod test {
use super::*;

#[derive(Debug, derive_more::Display)]
enum MyError {
Foo,
}

#[cfg(feature = "std")]
impl std::error::Error for MyError {}

#[test]
fn custom_error() {
// Just a compile-time check that we can ergonomically provide an arbitrary custom error:
Error::custom(MyError::Foo);
}
#[error("Custom error: {0}")]
Custom(Box<dyn core::error::Error + Send + Sync + 'static>),
}
4 changes: 2 additions & 2 deletions scale-decode/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.

#![cfg_attr(not(feature = "std"), no_std)]
#![no_std]

/*!
`parity-scale-codec` provides a `Decode` trait which allows bytes to be scale decoded into types based on the shape of those
Expand Down Expand Up @@ -315,6 +315,6 @@ pub trait IntoVisitor {
/// - `#[decode_as_type(skip)]` (or `#[codec(skip)]`):
/// Any fields annotated with this will be skipped when attempting to decode into the
/// type, and instead will be populated with their default value (and therefore must
/// implement [`std::default::Default`]).
/// implement [`core::default::Default`]).
#[cfg(feature = "derive")]
pub use scale_decode_derive::DecodeAsType;
77 changes: 40 additions & 37 deletions scale-decode/src/visitor/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -241,86 +241,90 @@ pub trait Visitor: Sized {
}

/// An error decoding SCALE bytes.
#[derive(Debug, Clone, PartialEq, Eq, derive_more::From, derive_more::Display)]
#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
pub enum DecodeError {
/// Type ID was not found
#[display("Could not find type with ID '{_0}'")]
#[error("Could not find type with ID '{0}'")]
TypeIdNotFound(String),
/// A low level error trying to resolve a type.
#[display("Failed to resolve type: {_0}")]
#[error("Failed to resolve type: {0}")]
TypeResolvingError(String),
/// The type we're trying to decode is supposed to be compact encoded, but that is not possible.
#[display("Could not decode compact encoded type: compact types can only have 1 field")]
#[error("Could not decode compact encoded type: compact types can only have 1 field")]
CannotDecodeCompactIntoType,
/// Failure to decode bytes into a string.
#[from]
#[display("Could not decode string: {_0}")]
#[error("Could not decode string: {0}")]
InvalidStr(alloc::str::Utf8Error),
/// We could not convert the [`u32`] that we found into a valid [`char`].
#[display("{_0} is expected to be a valid char, but is not")]
#[error("{_0} is expected to be a valid char, but is not")]
InvalidChar(u32),
/// We expected more bytes to finish decoding, but could not find them.
#[display("Ran out of data during decoding")]
#[error("Ran out of data during decoding")]
NotEnoughInput,
/// We found a variant that does not match with any in the type we're trying to decode from.
#[display("Could not find variant with index {_0}")]
#[error("Could not find variant with index {_0}")]
VariantNotFound(u8),
/// Some error emitted from a [`codec::Decode`] impl.
#[from]
#[error("Decode error: {0}")]
CodecError(codec::Error),
/// This is returned by default if a visitor function is not implemented.
#[display("Unexpected type {_0}")]
Unexpected(Unexpected),
#[error("Unexpected type {_0}")]
Unexpected(#[from] Unexpected),
}

#[cfg(feature = "std")]
impl std::error::Error for DecodeError {}
// TODO(niklasad1): when `codec::Error` implements `core::error::Error` we can remove this impl
// and use thiserror::Error #[from] instead.
impl From<codec::Error> for DecodeError {
fn from(e: codec::Error) -> Self {
DecodeError::CodecError(e)
}
}

/// This is returned by default when a visitor function isn't implemented.
#[derive(Debug, Clone, Copy, PartialEq, Eq, derive_more::Display)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error)]
#[allow(missing_docs)]
pub enum Unexpected {
#[display("bool")]
#[error("bool")]
Bool,
#[display("char")]
#[error("char")]
Char,
#[display("u8")]
#[error("u8")]
U8,
#[display("u16")]
#[error("u16")]
U16,
#[display("u32")]
#[error("u32")]
U32,
#[display("u64")]
#[error("u64")]
U64,
#[display("u128")]
#[error("u128")]
U128,
#[display("u256")]
#[error("u256")]
U256,
#[display("i8")]
#[error("i8")]
I8,
#[display("i16")]
#[error("i16")]
I16,
#[display("i32")]
#[error("i32")]
I32,
#[display("i64")]
#[error("i64")]
I64,
#[display("i128")]
#[error("i128")]
I128,
#[display("i256")]
#[error("i256")]
I256,
#[display("sequence")]
#[error("sequence")]
Sequence,
#[display("composite")]
#[error("composite")]
Composite,
#[display("tuple")]
#[error("tuple")]
Tuple,
#[display("str")]
#[error("str")]
Str,
#[display("variant")]
#[error("variant")]
Variant,
#[display("array")]
#[error("array")]
Array,
#[display("bitsequence")]
#[error("bitsequence")]
Bitsequence,
}

Expand Down Expand Up @@ -1209,7 +1213,6 @@ mod test {

// A couple of tests to check that invalid input doesn't lead to panics
// when we attempt to decode it to certain types.
#[cfg(feature = "std")]
mod proptests {
use super::*;
use proptest::prelude::*;
Expand Down

0 comments on commit 8654c98

Please sign in to comment.