use crate::{
error::{Error, ErrorKind, Kind, Location},
EncodeAsType, Field, FieldIter, TypeResolver,
};
use alloc::collections::BTreeMap;
use alloc::{format, string::ToString, vec::Vec};
use scale_type_resolver::visitor;
trait EncodeAsTypeWithResolver<R: TypeResolver> {
fn encode_as_type_with_resolver_to(
&self,
type_id: R::TypeId,
types: &R,
out: &mut Vec<u8>,
) -> Result<(), Error>;
}
impl<T: EncodeAsType, R: TypeResolver> EncodeAsTypeWithResolver<R> for T {
fn encode_as_type_with_resolver_to(
&self,
type_id: R::TypeId,
types: &R,
out: &mut Vec<u8>,
) -> Result<(), Error> {
self.encode_as_type_to(type_id, types, out)
}
}
pub struct CompositeField<'a, R> {
val: &'a dyn EncodeAsTypeWithResolver<R>,
}
impl<'a, R> Copy for CompositeField<'a, R> {}
impl<'a, R> Clone for CompositeField<'a, R> {
fn clone(&self) -> Self {
*self
}
}
impl<'a, R> core::fmt::Debug for CompositeField<'a, R> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.write_str("CompositeField")
}
}
impl<'a, R: TypeResolver> CompositeField<'a, R> {
pub fn new<T: EncodeAsType>(val: &'a T) -> Self {
CompositeField { val }
}
pub fn encode_composite_field_to(
&self,
type_id: R::TypeId,
types: &R,
out: &mut Vec<u8>,
) -> Result<(), Error> {
self.val
.encode_as_type_with_resolver_to(type_id, types, out)
}
}
pub struct Composite<R, Vals> {
vals: Vals,
marker: core::marker::PhantomData<R>,
}
impl<'a, R, Vals> Composite<R, Vals>
where
R: TypeResolver + 'a,
Vals: ExactSizeIterator<Item = (Option<&'a str>, CompositeField<'a, R>)> + Clone,
{
pub fn new(vals: Vals) -> Self {
Composite {
vals,
marker: core::marker::PhantomData,
}
}
pub fn encode_composite_as_type(
&self,
type_id: R::TypeId,
types: &R,
) -> Result<Vec<u8>, Error> {
let mut out = Vec::new();
self.encode_composite_as_type_to(type_id, types, &mut out)?;
Ok(out)
}
pub fn encode_composite_as_type_to(
&self,
type_id: R::TypeId,
types: &R,
out: &mut Vec<u8>,
) -> Result<(), Error> {
let vals_iter = self.vals.clone();
let vals_iter_len = vals_iter.len();
let type_id = skip_through_single_unnamed_fields(type_id, types);
let v = visitor::new(
(type_id.clone(), out, vals_iter),
|(type_id, out, mut vals_iter), _| {
if vals_iter_len == 1 {
return vals_iter
.next()
.expect("1 value expected")
.1
.encode_composite_field_to(type_id, types, out);
}
Err(Error::new(ErrorKind::WrongShape {
actual: Kind::Struct,
expected_id: format!("{type_id:?}"),
}))
},
)
.visit_not_found(|(type_id, _, _)| {
Err(Error::new(ErrorKind::TypeNotFound(format!("{type_id:?}"))))
})
.visit_composite(|(type_id, out, mut vals_iter), _, mut fields| {
let is_named_vals = vals_iter.clone().any(|(name, _)| name.is_some());
if !is_named_vals && vals_iter_len == 1 {
return vals_iter
.next()
.expect("1 value expected")
.1
.encode_composite_field_to(type_id, types, out);
}
self.encode_composite_fields_to(&mut fields, types, out)
})
.visit_tuple(|(type_id, out, mut vals_iter), type_ids| {
if vals_iter_len == 1 {
return vals_iter
.next()
.unwrap()
.1
.encode_composite_field_to(type_id, types, out);
}
let mut fields = type_ids.map(Field::unnamed);
self.encode_composite_fields_to(
&mut fields as &mut dyn FieldIter<'_, R::TypeId>,
types,
out,
)
});
super::resolve_type_and_encode(types, type_id, v)
}
pub fn encode_composite_fields(
&self,
fields: &mut dyn FieldIter<'_, R::TypeId>,
types: &R,
) -> Result<Vec<u8>, Error> {
let mut out = Vec::new();
self.encode_composite_fields_to(fields, types, &mut out)?;
Ok(out)
}
pub fn encode_composite_fields_to(
&self,
fields: &mut dyn FieldIter<'_, R::TypeId>,
types: &R,
out: &mut Vec<u8>,
) -> Result<(), Error> {
let vals_iter = self.vals.clone();
let fields = smallvec::SmallVec::<[_; 16]>::from_iter(fields);
let is_named = {
let is_target_named = fields.iter().any(|f| f.name.is_some());
let is_source_named = vals_iter.clone().any(|(name, _)| name.is_some());
is_target_named && is_source_named
};
if is_named {
let source_fields_by_name: BTreeMap<&str, CompositeField<'a, R>> = vals_iter
.map(|(name, val)| (name.unwrap_or(""), val))
.collect();
for field in fields {
let name = field.name.unwrap_or("");
let Some(value) = source_fields_by_name.get(name) else {
return Err(Error::new(ErrorKind::CannotFindField {
name: name.to_string(),
}));
};
value
.encode_composite_field_to(field.id, types, out)
.map_err(|e| e.at_field(name.to_string()))?;
}
Ok(())
} else {
let fields_len = fields.len();
if fields_len != vals_iter.len() {
return Err(Error::new(ErrorKind::WrongLength {
actual_len: vals_iter.len(),
expected_len: fields_len,
}));
}
for (idx, (field, (name, val))) in fields.iter().zip(vals_iter).enumerate() {
val.encode_composite_field_to(field.id.clone(), types, out)
.map_err(|e| {
let loc = if let Some(name) = name {
Location::field(name.to_string())
} else {
Location::idx(idx)
};
e.at(loc)
})?;
}
Ok(())
}
}
}
fn skip_through_single_unnamed_fields<R: TypeResolver>(type_id: R::TypeId, types: &R) -> R::TypeId {
let v = visitor::new(type_id.clone(), |type_id, _| type_id)
.visit_composite(|type_id, _, fields| {
let Some(f) = fields.next() else {
return type_id;
};
if fields.next().is_some() || f.name.is_some() {
return type_id;
};
skip_through_single_unnamed_fields(f.id, types)
})
.visit_tuple(|type_id, type_ids| {
let Some(new_type_id) = type_ids.next() else {
return type_id;
};
if type_ids.next().is_some() {
return type_id;
};
skip_through_single_unnamed_fields(new_type_id, types)
});
types.resolve_type(type_id.clone(), v).unwrap_or(type_id)
}