use canadensis_dsdl_frontend::types::{
ImplicitField, PrimitiveType, ResolvedScalarType, ResolvedType,
};
use std::fmt::{Display, Formatter, Result};
use crate::{
GeneratedEnum, GeneratedField, GeneratedStruct, GeneratedType, GeneratedTypeKind, RustTypeName,
};
pub(crate) struct ImplementDeserialize<'t, 'c> {
pub ty: &'t GeneratedType<'c>,
pub zero_copy: bool,
}
impl Display for ImplementDeserialize<'_, '_> {
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
writeln!(
f,
"impl ::canadensis_encoding::Deserialize for {} {{",
self.ty.name.type_name
)?;
writeln!(f, "fn deserialize(cursor: &mut ::canadensis_encoding::ReadCursor<'_>) -> ::core::result::Result<Self, ::canadensis_encoding::DeserializeError> where Self: Sized {{")?;
match &self.ty.kind {
GeneratedTypeKind::Struct(gstruct) => {
deserialize_struct(f, &self.ty.name, gstruct, self.zero_copy)?
}
GeneratedTypeKind::Enum(genum) => deserialize_enum(f, &self.ty.name, genum)?,
}
writeln!(f, "}}")?;
writeln!(f, "}}")
}
}
fn deserialize_struct(
f: &mut Formatter,
name: &RustTypeName,
gstruct: &GeneratedStruct,
zero_copy: bool,
) -> Result {
if zero_copy {
writeln!(f, "Ok(Self::deserialize_zero_copy(cursor))")
} else {
writeln!(f, "Ok( {} {{", name.type_name)?;
let mut padding_before_data: Vec<u8> = Vec::new();
for field in &gstruct.fields {
match field {
GeneratedField::Data(field) => {
write!(f, "{}: {{", field.name)?;
for padding in padding_before_data.drain(..) {
writeln!(f, "cursor.skip_{}();", padding)?;
}
writeln!(
f,
"{} }},",
ReadUnalignedField {
ty: field.cyphal_ty
}
)?;
}
GeneratedField::Padding(bits) => {
padding_before_data.push(*bits);
}
}
}
writeln!(f, "}} )")?;
Ok(())
}
}
fn deserialize_enum(f: &mut Formatter<'_>, name: &RustTypeName, genum: &GeneratedEnum) -> Result {
writeln!(
f,
"match {} {{",
CallReadAligned {
bits: genum.discriminant_bits
}
)?;
for variant in genum.variants.iter() {
writeln!(f, "{} => {{", variant.discriminant)?;
if let Some(ty) = &variant.ty {
writeln!(
f,
"Ok({}::{}({{ {} }}))",
name.type_name,
variant.name,
ReadUnalignedField { ty: &ty.cyphal_ty }
)?;
} else {
writeln!(f, "Ok({}::{})", name.type_name, variant.name)?;
}
writeln!(f, "}}")?;
}
writeln!(
f,
"_ => Err(::canadensis_encoding::DeserializeError::UnionTag),"
)?;
writeln!(f, "}}")?;
Ok(())
}
struct ReadUnalignedField<'t> {
ty: &'t ResolvedType,
}
impl Display for ReadUnalignedField<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
match &self.ty {
ResolvedType::Scalar(scalar) => Display::fmt(&ReadUnalignedScalar { ty: scalar }, f)?,
ResolvedType::FixedArray {
inner: ResolvedScalarType::Primitive(PrimitiveType::Boolean),
len,
} => {
writeln!(
f,
"::canadensis_encoding::bits::BitArray::deserialize({}_usize, cursor)",
*len
)?;
}
ResolvedType::VariableArray {
inner: ResolvedScalarType::Primitive(PrimitiveType::Boolean),
..
} => {
let length_bits = match &self.ty.implicit_field() {
Some(ImplicitField::ArrayLength { bits }) => *bits,
_ => unreachable!("Variable-length array does not have a length field"),
};
writeln!(f, "{{ let length = {};", CallRead { bits: length_bits })?;
writeln!(
f,
"::canadensis_encoding::bits::BitArray::deserialize(length, cursor) }}"
)?;
}
ResolvedType::FixedArray { inner, len } => {
writeln!(f, "[")?;
for _ in 0..*len {
writeln!(f, "{},", ReadUnalignedScalar { ty: inner })?;
}
writeln!(f, "]")?;
}
ResolvedType::VariableArray { inner, max_len } => {
let length_bits = match &self.ty.implicit_field() {
Some(ImplicitField::ArrayLength { bits }) => *bits,
_ => unreachable!("Variable-length array does not have a length field"),
};
writeln!(f, "let length = {};", CallRead { bits: length_bits })?;
writeln!(f, "if length <= {} {{", *max_len)?;
writeln!(f, "let mut elements = ::heapless::Vec::new();")?;
writeln!(f, "for _ in 0..length {{")?;
writeln!(
f,
"let _ = elements.push({});",
ReadUnalignedScalar { ty: inner }
)?;
writeln!(f, "}}")?;
writeln!(f, "elements")?;
writeln!(f, "}} else {{")?;
writeln!(
f,
"return Err(::canadensis_encoding::DeserializeError::ArrayLength)"
)?;
writeln!(f, "}}")?;
}
}
Ok(())
}
}
struct ReadUnalignedScalar<'t> {
ty: &'t ResolvedScalarType,
}
impl Display for ReadUnalignedScalar<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
match &self.ty {
ResolvedScalarType::Composite { .. } => {
write!(f, "cursor.read_composite()?")?;
}
ResolvedScalarType::Primitive(primitive) => match primitive {
PrimitiveType::Boolean => write!(f, "cursor.read_bool()")?,
PrimitiveType::Utf8 | PrimitiveType::Byte => {
Display::fmt(&CallRead { bits: 8 }, f)?
}
PrimitiveType::Int { bits } => Display::fmt(&CallRead { bits: *bits }, f)?,
PrimitiveType::UInt { bits, .. } => Display::fmt(&CallRead { bits: *bits }, f)?,
PrimitiveType::Float16 { .. } => write!(f, "cursor.read_f16()")?,
PrimitiveType::Float32 { .. } => write!(f, "cursor.read_f32()")?,
PrimitiveType::Float64 { .. } => write!(f, "cursor.read_f64()")?,
},
ResolvedScalarType::Void { bits } => write!(f, "cursor.skip_{}()", *bits)?,
}
Ok(())
}
}
struct CallReadAligned {
bits: u8,
}
impl Display for CallReadAligned {
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
let function_name = match self.bits {
0 => unreachable!("Can't have a 0-bit integer"),
8 => "read_aligned_u8".to_owned(),
16 => "read_aligned_u16".to_owned(),
32 => "read_aligned_u32".to_owned(),
64 => "read_aligned_u64".to_owned(),
1..=7 | 9..=15 | 17..=31 | 33..=63 => format!("read_u{}", self.bits),
65..=u8::MAX => panic!("Integer too large"),
};
write!(f, "cursor.{}() as _", function_name,)
}
}
struct CallRead {
bits: u8,
}
impl Display for CallRead {
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
let function_name = format!("read_u{}", self.bits);
write!(f, "cursor.{}() as _", function_name,)
}
}