use std::convert::TryInto;
use std::io;
use crate::vlq_encode;
use crate::vlq_encode::*;
use bounded_vec::{BoundedVec, BoundedVecOutOfBounds};
use thiserror::Error;
#[derive(Error, Eq, PartialEq, Debug, Clone)]
pub enum ScorexSerializationError {
#[error("IO error: {0}")]
Io(String),
#[error("serialization not yet implemented: {0}")]
NotImplementedYet(&'static str),
#[error("serialization not supported: {0}")]
NotSupported(&'static str),
#[error("Bounds check error: {0}")]
TryFrom(#[from] std::num::TryFromIntError),
#[error("error: {0}")]
Misc(&'static str),
}
impl From<io::Error> for ScorexSerializationError {
fn from(error: io::Error) -> Self {
ScorexSerializationError::Io(error.to_string())
}
}
impl From<ScorexSerializationError> for io::Error {
fn from(e: ScorexSerializationError) -> Self {
io::Error::new(io::ErrorKind::InvalidInput, e.to_string())
}
}
#[derive(Error, Eq, PartialEq, Debug, Clone)]
pub enum ScorexParsingError {
#[error("invalid op code: {0}")]
InvalidOpCode(u8),
#[error("not implemented op error")]
NotImplementedOpCode(String),
#[error("type parsing error")]
InvalidTypeCode(u8),
#[error("vlq encode error: {0}")]
VlqEncode(#[from] vlq_encode::VlqEncodingError),
#[error("IO error: {0}")]
Io(String),
#[error("misc error")]
Misc(String),
#[error("parsing not yet implemented: {0}")]
NotImplementedYet(String),
#[error("Value out of bounds: {0}")]
ValueOutOfBounds(String),
#[error("Tuple items out of bounds: {0}")]
TupleItemsOutOfBounds(usize),
#[error("parsing not supported: {0}")]
NotSupported(&'static str),
#[error("serialization error: {0}")]
SerializationError(#[from] ScorexSerializationError),
#[error("Invalid item quantity for BoundedVec: {0}")]
BoundedVecOutOfBounds(#[from] BoundedVecOutOfBounds),
#[error("Bounds check error: {0}")]
TryFrom(#[from] std::num::TryFromIntError),
}
impl From<io::Error> for ScorexParsingError {
fn from(error: io::Error) -> Self {
ScorexParsingError::Io(error.to_string())
}
}
impl From<&io::Error> for ScorexParsingError {
fn from(error: &io::Error) -> Self {
ScorexParsingError::Io(error.to_string())
}
}
pub type ScorexSerializeResult = Result<(), ScorexSerializationError>;
pub trait ScorexSerializable: Sized {
fn scorex_serialize<W: WriteSigmaVlqExt>(&self, w: &mut W) -> ScorexSerializeResult;
fn scorex_parse<R: ReadSigmaVlqExt>(r: &mut R) -> Result<Self, ScorexParsingError>;
fn scorex_serialize_bytes(&self) -> Result<Vec<u8>, ScorexSerializationError> {
let mut w = vec![];
self.scorex_serialize(&mut w)?;
Ok(w)
}
fn scorex_parse_bytes(mut bytes: &[u8]) -> Result<Self, ScorexParsingError> {
Self::scorex_parse(&mut bytes)
}
}
impl<T: ScorexSerializable> ScorexSerializable for Vec<T> {
fn scorex_serialize<W: WriteSigmaVlqExt>(&self, w: &mut W) -> ScorexSerializeResult {
w.put_u32(self.len() as u32)?;
self.iter().try_for_each(|i| i.scorex_serialize(w))
}
fn scorex_parse<R: ReadSigmaVlqExt>(r: &mut R) -> Result<Self, ScorexParsingError> {
let items_count = r.get_u32()?;
let mut items = Vec::with_capacity(items_count as usize);
for _ in 0..items_count {
items.push(T::scorex_parse(r)?);
}
Ok(items)
}
}
impl<T: ScorexSerializable, const L: usize, const U: usize> ScorexSerializable
for BoundedVec<T, L, U>
{
fn scorex_serialize<W: WriteSigmaVlqExt>(&self, w: &mut W) -> ScorexSerializeResult {
self.as_vec().scorex_serialize(w)
}
fn scorex_parse<R: ReadSigmaVlqExt>(r: &mut R) -> Result<Self, ScorexParsingError> {
Ok(Vec::<T>::scorex_parse(r)?.try_into()?)
}
}
impl ScorexSerializable for u32 {
fn scorex_serialize<W: WriteSigmaVlqExt>(&self, w: &mut W) -> ScorexSerializeResult {
w.put_u32(*self)?;
Ok(())
}
fn scorex_parse<R: ReadSigmaVlqExt>(r: &mut R) -> Result<Self, ScorexParsingError> {
let v = r.get_u32()?;
Ok(v)
}
}
impl<T: ScorexSerializable> ScorexSerializable for Option<Box<T>> {
fn scorex_serialize<W: WriteSigmaVlqExt>(&self, w: &mut W) -> ScorexSerializeResult {
match self {
Some(v) => {
w.put_u8(1)?;
v.scorex_serialize(w)
}
None => Ok(w.put_u8(0)?),
}
}
fn scorex_parse<R: ReadSigmaVlqExt>(r: &mut R) -> Result<Self, ScorexParsingError> {
let tag = r.get_u8()?;
Ok(if tag != 0 {
Some(T::scorex_parse(r)?.into())
} else {
None
})
}
}
#[allow(clippy::expect_used)]
pub fn scorex_serialize_roundtrip<T: ScorexSerializable>(v: &T) -> T {
let mut data = Vec::new();
v.scorex_serialize(&mut data).expect("serialization failed");
let reader = &mut &data[..];
T::scorex_parse(reader).expect("parse failed")
}
#[allow(clippy::unwrap_used)]
#[cfg(test)]
#[allow(clippy::panic)]
mod test {
use super::*;
use proptest::collection::vec;
use proptest::prelude::*;
proptest! {
#[test]
fn u32_roundtrip(val in any::<u32>()) {
assert_eq!(scorex_serialize_roundtrip(&val), val);
}
#[test]
fn vec_roundtrip(val in vec(any::<u32>(), 0..255)) {
assert_eq!(scorex_serialize_roundtrip(&val), val);
}
#[test]
fn box_roundtrip(val in any::<Option<Box<u32>>>()) {
assert_eq!(scorex_serialize_roundtrip(&val), val);
}
}
}