Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Test PR #3

Open
wants to merge 27 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,32 @@ jobs:
# we run tests using BitVec<u64,_> which doesn't.
args: --all-features --target wasm32-unknown-unknown

no_std:
name: Check no_std build
runs-on: ubuntu-latest
steps:
- name: Checkout sources
uses: actions/checkout@v2

- name: Install Rust stable toolchain
uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: stable
target: aarch64-unknown-none
override: true

- name: Rust Cache
uses: Swatinem/[email protected]

- name: Check no_std build
uses: actions-rs/[email protected]
with:
command: check
# The aarch64-unknown-none doesn't support `std`, so this
# will fail if the crate is not no_std compatible.
args: --no-default-features --target aarch64-unknown-none --features bits,primitive-types,derive

fmt:
name: Cargo fmt
runs-on: ubuntu-latest
Expand Down Expand Up @@ -138,6 +164,12 @@ jobs:
command: test
args: --all-targets --workspace

- name: Cargo test no_std
uses: actions-rs/[email protected]
with:
command: test
args: --all-targets --workspace --no-default-features --features bits,primitive-types

- name: Cargo test docs
uses: actions-rs/[email protected]
with:
Expand Down
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
[workspace]
members = [
"scale-encode",
"scale-encode-derive"
"scale-encode-derive",
"testing/no_std",
]

[workspace.package]
Expand Down
14 changes: 6 additions & 8 deletions scale-encode-derive/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ fn generate_enum_impl(
// long variable names to prevent conflict with struct field names:
__encode_as_type_type_id: u32,
__encode_as_type_types: &#path_to_scale_encode::PortableRegistry,
__encode_as_type_out: &mut Vec<u8>
__encode_as_type_out: &mut #path_to_scale_encode::Vec<u8>
) -> Result<(), #path_to_scale_encode::Error> {
match self {
#( #match_arms, )*
Expand Down Expand Up @@ -113,7 +113,7 @@ fn generate_struct_impl(
// long variable names to prevent conflict with struct field names:
__encode_as_type_type_id: u32,
__encode_as_type_types: &#path_to_scale_encode::PortableRegistry,
__encode_as_type_out: &mut Vec<u8>
__encode_as_type_out: &mut #path_to_scale_encode::Vec<u8>
) -> Result<(), #path_to_scale_encode::Error> {
let #path_to_type #matcher = self;
#composite.encode_as_type_to(
Expand All @@ -129,7 +129,7 @@ fn generate_struct_impl(
// long variable names to prevent conflict with struct field names:
__encode_as_type_fields: &mut dyn #path_to_scale_encode::FieldIter<'_>,
__encode_as_type_types: &#path_to_scale_encode::PortableRegistry,
__encode_as_type_out: &mut Vec<u8>
__encode_as_type_out: &mut #path_to_scale_encode::Vec<u8>
) -> Result<(), #path_to_scale_encode::Error> {
let #path_to_type #matcher = self;
#composite.encode_as_fields_to(
Expand Down Expand Up @@ -193,15 +193,13 @@ fn fields_to_matcher_and_composite(
)
}
syn::Fields::Unnamed(fields) => {
let field_idents: Vec<syn::Ident> = fields
let field_idents = fields
.unnamed
.iter()
.enumerate()
.map(|(idx, _)| format_ident!("_{idx}"))
.collect();
let match_body = field_idents.iter().map(|i| quote!(#i));
.map(|(idx, _)| format_ident!("_{idx}"));
let match_body = field_idents.clone().map(|i| quote!(#i));
let tuple_body = field_idents
.iter()
.map(|i| quote!((None as Option<&'static str>, #i as &dyn #path_to_scale_encode::EncodeAsType)));

(
Expand Down
22 changes: 12 additions & 10 deletions scale-encode/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,31 +14,33 @@ keywords.workspace = true
include.workspace = true

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

# Include the derive proc macro
# Activates std feature.
std = ["scale-info/std"]

# Include the derive proc macro.
derive = ["dep:scale-encode-derive"]

# impls for key primitive-types
# impls for key primitive-types.
primitive-types = ["dep:primitive-types"]

# impls for Bits.
bits = ["dep:scale-bits"]

[dependencies]
scale-info = { version = "2.7.0", features = ["bit-vec"] }
thiserror = "1.0.37"
codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive", "full"] }
scale-bits = { version = "0.3.0", default-features = false, features = ["scale-info"], optional = true }
scale-info = { version = "2.7.0", default-features = false, features = ["bit-vec"] }
codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] }
scale-bits = { version = "0.4.0", default-features = false, features = ["scale-info"], optional = true }
scale-encode-derive = { workspace = true, optional = true }
primitive-types = { version = "0.12.0", optional = true, default-features = false }
smallvec = "1.10.0"

[dev-dependencies]
bitvec = "1.0.1"
scale-info = { version = "2.3.0", features = ["bit-vec", "derive"] }
bitvec = { version = "1.0.1", default-features = false }
scale-info = { version = "2.3.0", features = ["bit-vec", "derive"], default-features = false }
scale-encode-derive = { workspace = true }
codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive", "full", "bit-vec"] }
codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive", "bit-vec"] }
trybuild = "1.0.72"
# enable scale-info feature for testing:
primitive-types = { version = "0.12.0", default-features = false, features = ["scale-info"] }
6 changes: 3 additions & 3 deletions scale-encode/src/error/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
//! This module provides a [`Context`] type, which tracks the path
//! that we're attempting to encode to aid in error reporting.

use std::borrow::Cow;
use alloc::{borrow::Cow, vec::Vec};

/// A cheaply clonable opaque context which allows us to track the current
/// location into a type that we're trying to encode, to aid in
Expand Down Expand Up @@ -55,8 +55,8 @@ impl<'a> Path<'a> {
}
}

impl<'a> std::fmt::Display for Path<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
impl<'a> core::fmt::Display for Path<'a> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
for (idx, loc) in self.0.iter().enumerate() {
if idx != 0 {
f.write_str(".")?;
Expand Down
81 changes: 67 additions & 14 deletions scale-encode/src/error/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,13 @@
//! An error that is emitted whenever some encoding fails.
mod context;

use std::borrow::Cow;
use std::fmt::Display;
use alloc::{borrow::Cow, boxed::Box, string::String};
use core::fmt::Display;

pub use context::{Context, Location};

/// An error produced while attempting to encode some type.
#[derive(Debug, thiserror::Error)]
#[derive(Debug)]
pub struct Error {
context: Context,
kind: ErrorKind,
Expand Down Expand Up @@ -83,64 +83,102 @@ impl Error {
}

impl Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
let path = self.context.path();
let kind = &self.kind;
write!(f, "Error at {path}: {kind}")
}
}

/// The underlying nature of the error.
#[derive(Debug, thiserror::Error)]
#[derive(Debug)]
pub enum ErrorKind {
/// Cannot find a given type.
#[error("Cannot find type with ID {0}")]
TypeNotFound(u32),
/// Cannot encode the actual type given into the target type ID.
#[error("Cannot encode {actual:?} into type with ID {expected}")]
WrongShape {
/// The actual kind we have to encode
actual: Kind,
/// ID of the expected type.
expected: u32,
},
/// The types line up, but the expected length of the target type is different from the length of the input value.
#[error("Cannot encode to type; expected length {expected_len} but got length {actual_len}")]
WrongLength {
/// Length we have
actual_len: usize,
/// Length expected for type.
expected_len: usize,
},
/// We cannot encode the number given into the target type; it's out of range.
#[error("Number {value} is out of range for target type {expected}")]
NumberOutOfRange {
/// A string represenatation of the numeric value that was out of range.
value: String,
/// Id of the expected numeric type that we tried to encode it to.
expected: u32,
},
/// Cannot find a variant with a matching name on the target type.
#[error("Variant {name} does not exist on type with ID {expected}")]
CannotFindVariant {
/// Variant name we can't find in the expected type.
name: String,
/// ID of the expected type.
expected: u32,
},
/// Cannot find a field on our source type that's needed for the target type.
#[error("Field {name} does not exist in our source struct")]
CannotFindField {
/// Name of the field which was not provided.
name: String,
},
/// A custom error.
#[error("Custom error: {0}")]
Custom(CustomError),
}

impl From<CustomError> for ErrorKind {
fn from(err: CustomError) -> ErrorKind {
ErrorKind::Custom(err)
}
}

impl Display for ErrorKind {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
ErrorKind::TypeNotFound(id) => write!(f, "Cannot find type with ID {id}"),
ErrorKind::WrongShape { actual, expected } => {
write!(f, "Cannot encode {actual:?} into type with ID {expected}")
}
ErrorKind::WrongLength {
actual_len,
expected_len,
} => {
write!(f, "Cannot encode to type; expected length {expected_len} but got length {actual_len}")
}
ErrorKind::NumberOutOfRange { value, expected } => {
write!(
f,
"Number {value} is out of range for target type {expected}"
)
}
ErrorKind::CannotFindVariant { name, expected } => {
write!(
f,
"Variant {name} does not exist on type with ID {expected}"
)
}
ErrorKind::CannotFindField { name } => {
write!(f, "Field {name} does not exist in our source struct")
}
ErrorKind::Custom(custom_error) => {
write!(f, "Custom error: {custom_error:?}")
}
}
}
}

#[cfg(feature = "std")]
type CustomError = Box<dyn std::error::Error + Send + Sync + 'static>;

#[cfg(not(feature = "std"))]
type CustomError = Box<dyn core::fmt::Debug + Send + Sync + 'static>;

/// The kind of type that we're trying to encode.
#[allow(missing_docs)]
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
Expand All @@ -160,12 +198,27 @@ pub enum Kind {
mod test {
use super::*;

#[derive(thiserror::Error, Debug)]
#[derive(Debug)]
enum MyError {
#[error("Foo!")]
Foo,
}

impl Display for MyError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "{self:?}")
}
}

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

#[cfg(not(feature = "std"))]
impl Into<CustomError> for MyError {
fn into(self) -> CustomError {
Box::new(self)
}
}

#[test]
fn custom_error() {
// Just a compile-time check that we can ergonomically provide an arbitrary custom error:
Expand Down
1 change: 1 addition & 0 deletions scale-encode/src/impls/bits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ use crate::{
error::{Error, ErrorKind, Kind},
EncodeAsType,
};
use alloc::vec::Vec;
use scale_info::TypeDef;

impl EncodeAsType for scale_bits::Bits {
Expand Down
5 changes: 3 additions & 2 deletions scale-encode/src/impls/composite.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,9 @@ use crate::{
error::{Error, ErrorKind, Kind, Location},
EncodeAsFields, EncodeAsType, Field, FieldIter,
};
use alloc::collections::BTreeMap;
use alloc::{string::ToString, vec::Vec};
use scale_info::{PortableRegistry, TypeDef};
use std::collections::HashMap;

/// This type represents named or unnamed composite values, and can be used
/// to help generate `EncodeAsType` impls. It's primarily used by the exported
Expand Down Expand Up @@ -156,7 +157,7 @@ where
// then encode to the target type by matching the names. If fields are
// named, we don't even mind if the number of fields doesn't line up;
// we just ignore any fields we provided that aren't needed.
let source_fields_by_name: HashMap<&str, &dyn EncodeAsType> = vals_iter
let source_fields_by_name: BTreeMap<&str, &dyn EncodeAsType> = vals_iter
.map(|(name, val)| (name.unwrap_or(""), val))
.collect();

Expand Down
Loading