polymesh-api-client 1.0.0

Polymesh API client core
Documentation
use codec::{Compact, Decode, Input};
use serde_json::{json, Map, Value};

use crate::error::*;
use crate::schema::*;
use crate::type_def::*;

#[derive(Clone)]
pub struct TypeCodec {
  type_lookup: TypeLookup,
  ty: Type,
}

impl TypeCodec {
  pub fn new(type_lookup: &TypeLookup, type_ref: TypeRef) -> Option<Self> {
    type_ref.ty.map(|ty| Self {
      type_lookup: type_lookup.clone(),
      ty,
    })
  }

  pub fn decode_value<I: Input>(&self, input: &mut I, is_compact: bool) -> Result<Value> {
    self.ty.decode_value(&self.type_lookup, input, is_compact)
  }

  pub fn decode(&self, data: Vec<u8>) -> Result<Value> {
    self.decode_value(&mut &data[..], false)
  }
}

impl TypeLookup {
  pub fn decode_value<I: Input>(
    &self,
    type_id: TypeId,
    input: &mut I,
    is_compact: bool,
  ) -> Result<Value> {
    let ty = self
      .get_type(type_id)
      .ok_or_else(|| Error::DecodeTypeFailed(format!("Missing type_id: {type_id:?}")))?;
    if ty.path().is_empty() {
      log::trace!("decode type[{type_id:?}]");
    } else {
      log::trace!("decode type[{type_id:?}]: {}", ty.path());
    }
    ty.decode_value(self, input, is_compact)
  }
}

impl Type {
  pub fn decode_value<I: Input>(
    &self,
    type_lookup: &TypeLookup,
    input: &mut I,
    is_compact: bool,
  ) -> Result<Value> {
    if !self.path().is_empty() {
      log::trace!("decode type: {}", self.path());
    }
    self.type_def.decode_value(type_lookup, input, is_compact)
  }
}

impl TypeDef {
  pub fn decode_value<I: Input>(
    &self,
    type_lookup: &TypeLookup,
    input: &mut I,
    is_compact: bool,
  ) -> Result<Value> {
    match self {
      TypeDef::Composite(def) => def.decode_value(type_lookup, input, is_compact),
      TypeDef::Variant(def) => def.decode_value(type_lookup, input, is_compact),
      TypeDef::Sequence(def) => def.decode_value(type_lookup, input, is_compact),
      TypeDef::Array(def) => def.decode_value(type_lookup, input, is_compact),
      TypeDef::Tuple(def) => def.decode_value(type_lookup, input, is_compact),
      TypeDef::Primitive(prim) => {
        log::trace!("decode Primitive: {prim:?}, is_compact: {is_compact}");
        match prim {
          TypeDefPrimitive::Bool => match input.read_byte()? {
            0 => Ok(json!(false)),
            1 => Ok(json!(true)),
            num => Err(Error::DecodeTypeFailed(format!(
              "Invalid bool byte: {num:?}"
            ))),
          },
          TypeDefPrimitive::Char => {
            let ch = input.read_byte()? as char;
            Ok(json!(ch))
          }
          TypeDefPrimitive::Str => {
            let s = String::decode(input)?;
            Ok(json!(s))
          }
          TypeDefPrimitive::U8 => {
            let num = u8::decode(input)?;
            Ok(json!(num))
          }
          TypeDefPrimitive::U16 => {
            let num = if is_compact {
              Compact::<u16>::decode(input)?.0
            } else {
              u16::decode(input)?
            };
            Ok(json!(num))
          }
          TypeDefPrimitive::U32 => {
            let num = if is_compact {
              Compact::<u32>::decode(input)?.0
            } else {
              u32::decode(input)?
            };
            Ok(json!(num))
          }
          TypeDefPrimitive::U64 => {
            let num = if is_compact {
              Compact::<u64>::decode(input)?.0
            } else {
              u64::decode(input)?
            };
            Ok(json!(num))
          }
          TypeDefPrimitive::U128 => {
            let num = if is_compact {
              Compact::<u128>::decode(input)?.0
            } else {
              u128::decode(input)?
            };
            Ok(json!(num))
          }
          TypeDefPrimitive::U256 => {
            unimplemented!();
          }
          TypeDefPrimitive::I8 => {
            let num = i8::decode(input)?;
            Ok(json!(num))
          }
          TypeDefPrimitive::I16 => {
            let num = if is_compact {
              Compact::<u16>::decode(input)?.0 as i16
            } else {
              i16::decode(input)?
            };
            Ok(json!(num))
          }
          TypeDefPrimitive::I32 => {
            let num = if is_compact {
              Compact::<u32>::decode(input)?.0 as i32
            } else {
              i32::decode(input)?
            };
            Ok(json!(num))
          }
          TypeDefPrimitive::I64 => {
            let num = if is_compact {
              Compact::<u64>::decode(input)?.0 as i64
            } else {
              i64::decode(input)?
            };
            Ok(json!(num))
          }
          TypeDefPrimitive::I128 => {
            let num = if is_compact {
              Compact::<u128>::decode(input)?.0 as i128
            } else {
              i128::decode(input)?
            };
            Ok(json!(num))
          }
          TypeDefPrimitive::I256 => {
            unimplemented!();
          }
        }
      },
      TypeDef::Compact(def) => def.decode_value(type_lookup, input, is_compact),
    }
  }
}

fn decode_fields<I: Input>(
  fields: &Vec<Field>,
  type_lookup: &TypeLookup,
  input: &mut I,
  is_compact: bool,
) -> Result<Value> {
  let len = fields.len();
  if len == 0 {
    return Ok(Value::Null);
  }
  match fields.len() {
    0 => Ok(Value::Null),
    1 if fields[0].name.is_none() => {
      Ok(type_lookup.decode_value(fields[0].ty, input, is_compact)?)
    }
    len => {
      let mut m = Map::with_capacity(len);
      for (idx, field) in fields.iter().enumerate() {
        let name = field
          .name
          .as_ref()
          .cloned()
          .unwrap_or_else(|| format!("{idx}"));
        log::trace!("decode Composite field: {name}");
        m.insert(name, type_lookup.decode_value(field.ty, input, is_compact)?);
      }
      Ok(m.into())
    }
  }
}

impl TypeDefComposite {
  pub fn decode_value<I: Input>(
    &self,
    type_lookup: &TypeLookup,
    input: &mut I,
    is_compact: bool,
  ) -> Result<Value> {
    decode_fields(&self.fields, type_lookup, input, is_compact)
  }
}

impl TypeDefVariant {
  pub fn decode_value<I: Input>(
    &self,
    type_lookup: &TypeLookup,
    input: &mut I,
    is_compact: bool,
  ) -> Result<Value> {
    let val = input.read_byte()?;
    match self.get_by_idx(val) {
      Some(variant) if variant.fields.len() == 0 => Ok(json!(variant.name)),
      Some(variant) => {
        let mut m = Map::new();
        let name = variant.name.clone();
        m.insert(
          name,
          decode_fields(&variant.fields, type_lookup, input, is_compact)?,
        );
        Ok(m.into())
      }
      None if val == 0 => Ok(Value::Null),
      None => {
        log::debug!(
          "Invalid variant: {}, bytes remaining: {:?}, variants: {:?}",
          val,
          input.remaining_len()?,
          self.variants
        );
        Err(Error::DecodeTypeFailed(format!("Invalid variant: {val}")))
      }
    }
  }
}

impl TypeDefSequence {
  pub fn decode_value<I: Input>(
    &self,
    type_lookup: &TypeLookup,
    input: &mut I,
    is_compact: bool,
  ) -> Result<Value> {
    let len = Compact::<u64>::decode(input)?.0 as usize;
    let mut vec = Vec::with_capacity(len.max(256));
    for _ in 0..len {
      vec.push(type_lookup.decode_value(self.type_param, input, is_compact)?);
    }
    Ok(vec.into())
  }
}

impl TypeDefArray {
  pub fn decode_value<I: Input>(
    &self,
    type_lookup: &TypeLookup,
    input: &mut I,
    is_compact: bool,
  ) -> Result<Value> {
    let len = self.len as usize;
    let mut vec = Vec::with_capacity(len);
    for _ in 0..len {
      vec.push(type_lookup.decode_value(self.type_param, input, is_compact)?);
    }
    Ok(vec.into())
  }
}

impl TypeDefTuple {
  pub fn decode_value<I: Input>(
    &self,
    type_lookup: &TypeLookup,
    input: &mut I,
    is_compact: bool,
  ) -> Result<Value> {
    match self.fields.len() {
      0 => Ok(Value::Null),
      1 => Ok(type_lookup.decode_value(self.fields[0], input, is_compact)?),
      len => {
        let mut vec = Vec::with_capacity(len);
        for field in &self.fields {
          vec.push(type_lookup.decode_value(*field, input, is_compact)?);
        }
        Ok(vec.into())
      }
    }
  }
}

impl TypeDefCompact {
  pub fn decode_value<I: Input>(
    &self,
    type_lookup: &TypeLookup,
    input: &mut I,
    _is_compact: bool,
  ) -> Result<Value> {
    type_lookup.decode_value(self.type_param, input, true)
  }
}