use super::scon::Value;
use crate::scon::Hex;
use anyhow::{
Context,
Result,
};
use scale::{
Decode,
Encode,
Output,
};
use scale_info::{
form::PortableForm,
IntoPortable,
Path,
TypeInfo,
};
use sp_core::crypto::{
AccountId32,
Ss58Codec,
};
use std::{
boxed::Box,
collections::HashMap,
convert::TryFrom,
str::FromStr,
};
#[derive(Default)]
pub struct EnvTypesTranscoder {
encoders: HashMap<u32, Box<dyn CustomTypeEncoder>>,
decoders: HashMap<u32, Box<dyn CustomTypeDecoder>>,
}
impl EnvTypesTranscoder {
pub fn new(
encoders: HashMap<u32, Box<dyn CustomTypeEncoder>>,
decoders: HashMap<u32, Box<dyn CustomTypeDecoder>>,
) -> Self {
Self { encoders, decoders }
}
pub fn try_encode<O>(
&self,
type_id: u32,
value: &Value,
output: &mut O,
) -> Result<bool>
where
O: Output,
{
match self.encoders.get(&type_id) {
Some(encoder) => {
tracing::debug!("Encoding type {:?} with custom encoder", type_id);
let encoded_env_type = encoder
.encode_value(value)
.context("Error encoding custom type")?;
output.write(&encoded_env_type);
Ok(true)
}
None => Ok(false),
}
}
pub fn try_decode(&self, type_id: u32, input: &mut &[u8]) -> Result<Option<Value>> {
match self.decoders.get(&type_id) {
Some(decoder) => {
tracing::debug!("Decoding type {:?} with custom decoder", type_id);
let decoded = decoder.decode_value(input)?;
Ok(Some(decoded))
}
None => {
tracing::debug!("No custom decoder found for type {:?}", type_id);
Ok(None)
}
}
}
}
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
pub struct PathKey(Vec<String>);
impl PathKey {
pub fn from_type<T>() -> Self
where
T: TypeInfo,
{
let type_info = T::type_info();
let path = type_info
.path()
.clone()
.into_portable(&mut Default::default());
PathKey::from(&path)
}
}
impl From<&Path<PortableForm>> for PathKey {
fn from(path: &Path<PortableForm>) -> Self {
PathKey(path.segments().to_vec())
}
}
pub type TypesByPath = HashMap<PathKey, u32>;
pub trait CustomTypeEncoder: Send + Sync {
fn encode_value(&self, value: &Value) -> Result<Vec<u8>>;
}
pub trait CustomTypeDecoder: Send + Sync {
fn decode_value(&self, input: &mut &[u8]) -> Result<Value>;
}
#[derive(Clone)]
pub struct AccountId;
impl CustomTypeEncoder for AccountId {
fn encode_value(&self, value: &Value) -> Result<Vec<u8>> {
let account_id = match value {
Value::Literal(literal) => {
AccountId32::from_str(literal).map_err(|e| {
anyhow::anyhow!(
"Error parsing AccountId from literal `{}`: {}",
literal,
e
)
})?
}
Value::String(string) => {
AccountId32::from_str(string).map_err(|e| {
anyhow::anyhow!(
"Error parsing AccountId from string '{}': {}",
string,
e
)
})?
}
Value::Hex(hex) => {
AccountId32::try_from(hex.bytes()).map_err(|_| {
anyhow::anyhow!(
"Error converting hex bytes `{:?}` to AccountId",
hex.bytes()
)
})?
}
_ => {
return Err(anyhow::anyhow!(
"Expected a string or a literal for an AccountId"
))
}
};
Ok(account_id.encode())
}
}
impl CustomTypeDecoder for AccountId {
fn decode_value(&self, input: &mut &[u8]) -> Result<Value> {
let account_id = AccountId32::decode(input)?;
Ok(Value::Literal(account_id.to_ss58check()))
}
}
pub struct Hash;
impl CustomTypeDecoder for Hash {
fn decode_value(&self, input: &mut &[u8]) -> Result<Value> {
let hash = sp_core::H256::decode(input)?;
Ok(Value::Hex(Hex::from_str(&format!("{:?}", hash))?))
}
}