#[cfg(test)]
mod tests;
use crate::{
serde_hex,
utils::{
deserialize_from_byte_str,
serialize_as_byte_str,
},
};
use derive_more::From;
use pro_prelude::collections::btree_map::BTreeMap;
use pro_primitives::Key;
use tetsy_scale_info::{
form::{
Form,
MetaForm,
PortableForm,
},
meta_type,
IntoPortable,
Registry,
TypeInfo,
};
use serde::{
de::DeserializeOwned,
Deserialize,
Serialize,
};
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, From, Serialize, Deserialize)]
#[serde(bound(
serialize = "F::Type: Serialize, F::String: Serialize",
deserialize = "F::Type: DeserializeOwned, F::String: DeserializeOwned"
))]
#[serde(rename_all = "camelCase")]
pub enum Layout<F: Form = MetaForm> {
Cell(CellLayout<F>),
Hash(HashLayout<F>),
Array(ArrayLayout<F>),
Struct(StructLayout<F>),
Enum(EnumLayout<F>),
}
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, From)]
pub struct LayoutKey {
key: [u8; 32],
}
impl serde::Serialize for LayoutKey {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serde_hex::serialize(&self.key, serializer)
}
}
impl<'de> serde::Deserialize<'de> for LayoutKey {
fn deserialize<D>(d: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let mut arr = [0; 32];
serde_hex::deserialize_check_len(d, serde_hex::ExpectedLen::Exact(&mut arr[..]))?;
Ok(arr.into())
}
}
impl<'a> From<&'a Key> for LayoutKey {
fn from(key: &'a Key) -> Self {
Self {
key: key.to_bytes(),
}
}
}
impl From<Key> for LayoutKey {
fn from(key: Key) -> Self {
Self {
key: key.to_bytes(),
}
}
}
impl LayoutKey {
pub fn to_bytes(&self) -> &[u8] {
&self.key
}
}
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, From, Serialize, Deserialize)]
#[serde(bound(
serialize = "F::Type: Serialize, F::String: Serialize",
deserialize = "F::Type: DeserializeOwned, F::String: DeserializeOwned"
))]
pub struct CellLayout<F: Form = MetaForm> {
key: LayoutKey,
ty: <F as Form>::Type,
}
impl CellLayout {
pub fn new<T>(key: LayoutKey) -> Self
where
T: TypeInfo + 'static,
{
Self {
key,
ty: meta_type::<T>(),
}
}
}
impl IntoPortable for CellLayout {
type Output = CellLayout<PortableForm>;
fn into_portable(self, registry: &mut Registry) -> Self::Output {
CellLayout {
key: self.key,
ty: registry.register_type(&self.ty),
}
}
}
impl IntoPortable for Layout {
type Output = Layout<PortableForm>;
fn into_portable(self, registry: &mut Registry) -> Self::Output {
match self {
Layout::Cell(encoded_cell) => {
Layout::Cell(encoded_cell.into_portable(registry))
}
Layout::Hash(hash_layout) => {
Layout::Hash(hash_layout.into_portable(registry))
}
Layout::Array(array_layout) => {
Layout::Array(array_layout.into_portable(registry))
}
Layout::Struct(struct_layout) => {
Layout::Struct(struct_layout.into_portable(registry))
}
Layout::Enum(enum_layout) => {
Layout::Enum(enum_layout.into_portable(registry))
}
}
}
}
impl<F> CellLayout<F>
where
F: Form,
{
pub fn key(&self) -> &LayoutKey {
&self.key
}
pub fn ty(&self) -> &F::Type {
&self.ty
}
}
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
#[serde(bound(
serialize = "F::Type: Serialize, F::String: Serialize",
deserialize = "F::Type: DeserializeOwned, F::String: DeserializeOwned"
))]
pub struct HashLayout<F: Form = MetaForm> {
offset: LayoutKey,
strategy: HashingStrategy,
layout: Box<Layout<F>>,
}
impl IntoPortable for HashLayout {
type Output = HashLayout<PortableForm>;
fn into_portable(self, registry: &mut Registry) -> Self::Output {
HashLayout {
offset: self.offset,
strategy: self.strategy,
layout: Box::new(self.layout.into_portable(registry)),
}
}
}
impl HashLayout {
pub fn new<K, L>(offset: K, strategy: HashingStrategy, layout: L) -> Self
where
K: Into<LayoutKey>,
L: Into<Layout>,
{
Self {
offset: offset.into(),
strategy,
layout: Box::new(layout.into()),
}
}
}
impl<F> HashLayout<F>
where
F: Form,
{
pub fn offset(&self) -> &LayoutKey {
&self.offset
}
pub fn strategy(&self) -> &HashingStrategy {
&self.strategy
}
pub fn layout(&self) -> &Layout<F> {
&self.layout
}
}
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
pub struct HashingStrategy {
hasher: CryptoHasher,
#[serde(
serialize_with = "serialize_as_byte_str",
deserialize_with = "deserialize_from_byte_str"
)]
prefix: Vec<u8>,
#[serde(
serialize_with = "serialize_as_byte_str",
deserialize_with = "deserialize_from_byte_str"
)]
postfix: Vec<u8>,
}
impl HashingStrategy {
pub fn new(hasher: CryptoHasher, prefix: Vec<u8>, postfix: Vec<u8>) -> Self {
Self {
hasher,
prefix,
postfix,
}
}
pub fn hasher(&self) -> &CryptoHasher {
&self.hasher
}
pub fn prefix(&self) -> &[u8] {
&self.prefix
}
pub fn postfix(&self) -> &[u8] {
&self.postfix
}
}
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
pub enum CryptoHasher {
Blake2x256,
Sha2x256,
Keccak256,
}
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
#[serde(bound(
serialize = "F::Type: Serialize, F::String: Serialize",
deserialize = "F::Type: DeserializeOwned, F::String: DeserializeOwned"
))]
#[serde(rename_all = "camelCase")]
pub struct ArrayLayout<F: Form = MetaForm> {
offset: LayoutKey,
len: u32,
cells_per_elem: u64,
layout: Box<Layout<F>>,
}
impl ArrayLayout {
pub fn new<K, L>(at: K, len: u32, cells_per_elem: u64, layout: L) -> Self
where
K: Into<LayoutKey>,
L: Into<Layout>,
{
Self {
offset: at.into(),
len,
cells_per_elem,
layout: Box::new(layout.into()),
}
}
}
#[allow(clippy::len_without_is_empty)]
impl<F> ArrayLayout<F>
where
F: Form,
{
pub fn offset(&self) -> &LayoutKey {
&self.offset
}
pub fn len(&self) -> u32 {
self.len
}
pub fn cells_per_elem(&self) -> u64 {
self.cells_per_elem
}
pub fn layout(&self) -> &Layout<F> {
&self.layout
}
}
impl IntoPortable for ArrayLayout {
type Output = ArrayLayout<PortableForm>;
fn into_portable(self, registry: &mut Registry) -> Self::Output {
ArrayLayout {
offset: self.offset,
len: self.len,
cells_per_elem: self.cells_per_elem,
layout: Box::new(self.layout.into_portable(registry)),
}
}
}
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
#[serde(bound(
serialize = "F::Type: Serialize, F::String: Serialize",
deserialize = "F::Type: DeserializeOwned, F::String: DeserializeOwned"
))]
pub struct StructLayout<F: Form = MetaForm> {
fields: Vec<FieldLayout<F>>,
}
impl StructLayout {
pub fn new<F>(fields: F) -> Self
where
F: IntoIterator<Item = FieldLayout>,
{
Self {
fields: fields.into_iter().collect(),
}
}
}
impl<F> StructLayout<F>
where
F: Form,
{
pub fn fields(&self) -> &[FieldLayout<F>] {
&self.fields
}
}
impl IntoPortable for StructLayout {
type Output = StructLayout<PortableForm>;
fn into_portable(self, registry: &mut Registry) -> Self::Output {
StructLayout {
fields: self
.fields
.into_iter()
.map(|field| field.into_portable(registry))
.collect::<Vec<_>>(),
}
}
}
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
#[serde(bound(
serialize = "F::Type: Serialize, F::String: Serialize",
deserialize = "F::Type: DeserializeOwned, F::String: DeserializeOwned"
))]
pub struct FieldLayout<F: Form = MetaForm> {
name: Option<F::String>,
layout: Layout<F>,
}
impl FieldLayout {
pub fn new<N, L>(name: N, layout: L) -> Self
where
N: Into<Option<&'static str>>,
L: Into<Layout>,
{
Self {
name: name.into(),
layout: layout.into(),
}
}
}
impl<F> FieldLayout<F>
where
F: Form,
{
pub fn name(&self) -> Option<&F::String> {
self.name.as_ref()
}
pub fn layout(&self) -> &Layout<F> {
&self.layout
}
}
impl IntoPortable for FieldLayout {
type Output = FieldLayout<PortableForm>;
fn into_portable(self, registry: &mut Registry) -> Self::Output {
FieldLayout {
name: self.name.map(|name| name.into_portable(registry)),
layout: self.layout.into_portable(registry),
}
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
pub struct Discriminant(usize);
impl From<usize> for Discriminant {
fn from(value: usize) -> Self {
Self(value)
}
}
impl Discriminant {
pub fn value(&self) -> usize {
self.0
}
}
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
#[serde(bound(
serialize = "F::Type: Serialize, F::String: Serialize",
deserialize = "F::Type: DeserializeOwned, F::String: DeserializeOwned"
))]
#[serde(rename_all = "camelCase")]
pub struct EnumLayout<F: Form = MetaForm> {
dispatch_key: LayoutKey,
variants: BTreeMap<Discriminant, StructLayout<F>>,
}
impl EnumLayout {
pub fn new<K, V>(dispatch_key: K, variants: V) -> Self
where
K: Into<LayoutKey>,
V: IntoIterator<Item = (Discriminant, StructLayout)>,
{
Self {
dispatch_key: dispatch_key.into(),
variants: variants.into_iter().collect(),
}
}
}
impl<F> EnumLayout<F>
where
F: Form,
{
pub fn dispatch_key(&self) -> &LayoutKey {
&self.dispatch_key
}
pub fn variants(&self) -> &BTreeMap<Discriminant, StructLayout<F>> {
&self.variants
}
}
impl IntoPortable for EnumLayout {
type Output = EnumLayout<PortableForm>;
fn into_portable(self, registry: &mut Registry) -> Self::Output {
EnumLayout {
dispatch_key: self.dispatch_key,
variants: self
.variants
.into_iter()
.map(|(discriminant, layout)| {
(discriminant, layout.into_portable(registry))
})
.collect(),
}
}
}