1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128
//! `scale-encode` builds on `parity-scale-codec`. `parity-scale-codec` provides an `Encode` trait
//! which allows types to SCALE encode themselves with no external information. `scale-encode` provides an
//! [`EncodeAsType`] trait which allows types to decide how to encode themselves based on the desired
//! target type.
#![deny(missing_docs)]
mod impls;
mod context;
mod linkedlist;
use std::fmt::Display;
use scale_info::PortableRegistry;
pub use context::Context;
/// This trait signals that some static type can possibly be SCALE encoded given some
/// `type_id` and [`PortableRegistry`] which dictates the expected encoding. A [`Context`]
/// is also passed around, which is used internally to improve error reporting. Implementations
/// should use the [`Context::at`] method to indicate the current location if they would like
/// it to show up in error output.
pub trait EncodeAsType {
/// This is a helper function which internally calls [`EncodeAsType::encode_as_type_to`]. Prefer to
/// implement that instead.
fn encode_as_type(&self, type_id: u32, types: &PortableRegistry, context: Context) -> Result<Vec<u8>, Error> {
let mut out = Vec::new();
self.encode_as_type_to(type_id, types, context, &mut out)?;
Ok(out)
}
/// Given some `type_id`, `types`, a `context` and some output target for the SCALE encoded bytes,
/// attempt to SCALE encode the current value into the type given by `type_id`.
fn encode_as_type_to(&self, type_id: u32, types: &PortableRegistry, context: Context, out: &mut Vec<u8>) -> Result<(), Error>;
}
/// An error produced while attempting to encode some type.
#[derive(Debug, Clone, thiserror::Error)]
pub struct Error {
context: Context,
kind: ErrorKind,
}
impl Error {
/// construct a new error given some context and an error kind.
pub fn new(context: Context, kind: ErrorKind) -> Error {
Error {
context,
kind
}
}
/// Retrieve more information abotu what went wrong.
pub fn kind(&self) -> &ErrorKind {
&self.kind
}
/// Retrieve details about where the error occurred.
pub fn context(&self) -> &Context {
&self.context
}
}
impl Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::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, Clone, thiserror::Error, PartialEq, Eq)]
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 ID {expected}; expected length {expected_len} but got length {actual_len}")]
WrongLength {
/// Length we have
actual_len: usize,
/// Length expected for type.
expected_len: usize,
/// ID of the expected type.
expected: u32
},
/// 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
},
}
/// The kind of type that we're trying to encode.
#[allow(missing_docs)]
#[derive(Copy,Clone,PartialEq,Eq,Debug)]
pub enum Kind {
Struct,
Tuple,
Variant,
Array,
BitSequence,
Bool,
Char,
Str,
Number,
}
// TODO:
// - tests for current impls to verify against parity-scale-codec.
// - add derive crate to handle structs and variants. (make possible to impl for existing structs/enums in other crates too)