Skip to content

Commit

Permalink
compiling
Browse files Browse the repository at this point in the history
  • Loading branch information
haerdib committed Jun 16, 2023
1 parent f11729e commit cdc946f
Show file tree
Hide file tree
Showing 19 changed files with 3,947 additions and 2 deletions.
13 changes: 12 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 5 additions & 1 deletion node-api/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,20 @@ log = { version = "0.4.14", default-features = false }
scale-info = { version = "2.0.1", features = ["derive", "decode", "bitvec"], default-features = false }
serde = { version = "1.0.136", features = ["derive"], default-features = false }
serde_json = { version = "1.0.79", default-features = false, features = ["alloc"] }
smallvec = "1.10.0"

# substrate
# parity
sp-core = { default-features = false, features = ["full_crypto", "serde"], git = "https://github.com/paritytech/substrate.git", branch = "master" }
sp-runtime = { default-features = false, features = ["serde"], git = "https://github.com/paritytech/substrate.git", branch = "master" }
sp-storage = { default-features = false, features = ["serde"], git = "https://github.com/paritytech/substrate.git", branch = "master" }
scale-bits = { default-features = false, features = ["scale-info"], git = "https://github.com/haerdib/scale-bits.git", branch = "bh/no-std" }

# need to add this for `no_std`
sp-application-crypto = { default-features = false, git = "https://github.com/paritytech/substrate.git", features = ["full_crypto"], branch = "master" }
sp-runtime-interface = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "master" }



[features]
default = ["std"]
# To support `no_std` builds in non-32 bit environments.
Expand Down
2 changes: 2 additions & 0 deletions node-api/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
//! Contains stuff to instantiate communication with a substrate node.

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

extern crate alloc;

Expand All @@ -28,6 +29,7 @@ pub mod decoder;
pub mod error;
pub mod events;
pub mod metadata;
pub mod scale_decode;
pub mod storage;

#[cfg(any(feature = "mocks", test))]
Expand Down
100 changes: 100 additions & 0 deletions node-api/src/scale_decode/error/context.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
// Copyright (C) 2023 Parity Technologies (UK) Ltd. ([email protected])
// This file is a part of the scale-decode crate.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! This module provides a [`Context`] type, which tracks the path
//! that we're attempting to encode to aid in error reporting.

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
/// error reporting.
#[derive(Clone, Default, Debug)]
pub struct Context {
path: Vec<Location>,
}

impl Context {
/// Construct a new, empty context.
pub fn new() -> Context {
Default::default()
}
/// Return a new context with the given location appended.
pub fn push(&mut self, loc: Location) {
self.path.push(loc);
}
/// Return the current path.
pub fn path(&self) -> Path<'_> {
Path(Cow::Borrowed(&self.path))
}
}

/// The current path that we're trying to encode.
pub struct Path<'a>(Cow<'a, Vec<Location>>);

impl<'a> Path<'a> {
/// Cheaply convert the path to an owned version.
pub fn into_owned(self) -> Path<'static> {
Path(Cow::Owned(self.0.into_owned()))
}
/// Return each location visited, oldest first
pub fn locations(&self) -> impl Iterator<Item = &Location> {
self.0.iter()
}
}

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(".")?;
}
match &loc.inner {
Loc::Field(name) => f.write_str(name)?,
Loc::Index(i) => write!(f, "[{i}]")?,
Loc::Variant(name) => write!(f, "({name})")?,
}
}
Ok(())
}
}

/// Some location, like a field, variant or index in an array.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Location {
inner: Loc,
}

#[derive(Debug, Clone, PartialEq, Eq)]
enum Loc {
Field(Cow<'static, str>),
Index(usize),
Variant(Cow<'static, str>),
}

impl Location {
/// This represents some struct field.
pub fn field(name: impl Into<Cow<'static, str>>) -> Self {
Location { inner: Loc::Field(name.into()) }
}
/// This represents some variant name.
pub fn variant(name: impl Into<Cow<'static, str>>) -> Self {
Location { inner: Loc::Variant(name.into()) }
}
/// This represents a tuple or array index.
pub fn idx(i: usize) -> Self {
Location { inner: Loc::Index(i) }
}
}
151 changes: 151 additions & 0 deletions node-api/src/scale_decode/error/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
// Copyright (C) 2023 Parity Technologies (UK) Ltd. ([email protected])
// This file is a part of the scale-decode crate.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! An error that is emitted whenever some decoding fails.
mod context;

use crate::scale_decode::visitor::DecodeError;
use alloc::{borrow::Cow, boxed::Box, string::String, vec::Vec};
use core::fmt::Display;

pub use context::{Context, Location};
use sp_core::RuntimeDebug;

/// An error produced while attempting to decode some type.
#[derive(Debug)]
pub struct Error {
context: Context,
kind: ErrorKind,
}

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 Into<CustomError>) -> Error {
Error::new(ErrorKind::Custom(error.into()))
}
/// Retrieve more information about what went wrong.
pub fn kind(&self) -> &ErrorKind {
&self.kind
}
/// Retrieve details about where the error occurred.
pub fn context(&self) -> &Context {
&self.context
}
/// Give some context to the error.
pub fn at(mut self, loc: Location) -> Self {
self.context.push(loc);
Error { context: self.context, kind: self.kind }
}
/// Note which sequence index the error occurred in.
pub fn at_idx(mut self, idx: usize) -> Self {
self.context.push(Location::idx(idx));
Error { context: self.context, kind: self.kind }
}
/// Note which field the error occurred in.
pub fn at_field(mut self, field: impl Into<Cow<'static, str>>) -> Self {
self.context.push(Location::field(field));
Error { context: self.context, kind: self.kind }
}
/// Note which variant the error occurred in.
pub fn at_variant(mut self, variant: impl Into<Cow<'static, str>>) -> Self {
self.context.push(Location::variant(variant));
Error { context: self.context, kind: self.kind }
}
}

impl Display for Error {
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:?}")
}
}

impl From<DecodeError> for Error {
fn from(err: DecodeError) -> Error {
Error::new(err.into())
}
}

/// The underlying nature of the error.
#[derive(RuntimeDebug)]
pub enum ErrorKind {
/// Something went wrong decoding the bytes based on the type
/// and type registry provided.
VisitorDecodeError(DecodeError),
/// We cannot decode the number seen into the target type; it's 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.
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.
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
CannotFindField {
/// Name of the field which was not provided.
name: String,
},
/// A custom error
Custom(CustomError),
}

impl From<DecodeError> for ErrorKind {
fn from(err: DecodeError) -> ErrorKind {
ErrorKind::VisitorDecodeError(err)
}
}

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

type CustomError = Box<dyn core::error::Error + Send + Sync + 'static>;

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

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

impl core::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);
}
}
Loading

0 comments on commit cdc946f

Please sign in to comment.