extern crate alloc;
use alloc::string::String;
use alloc::vec::Vec;
use zerodds_cdr::buffer::{BufferReader, BufferWriter};
use zerodds_cdr::endianness::Endianness;
use crate::dds_type::{DdsType, DecodeError, EncodeError};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ShapeType {
pub color: String,
pub x: i32,
pub y: i32,
pub shapesize: i32,
}
impl ShapeType {
#[must_use]
pub fn new(color: impl Into<String>, x: i32, y: i32, shapesize: i32) -> Self {
Self {
color: color.into(),
x,
y,
shapesize,
}
}
}
impl DdsType for ShapeType {
const TYPE_NAME: &'static str = "ShapeType";
const HAS_KEY: bool = true;
fn encode_key_holder_be(&self, holder: &mut crate::dds_type::PlainCdr2BeKeyHolder) {
holder.write_string(&self.color);
}
fn encode(&self, out: &mut Vec<u8>) -> core::result::Result<(), EncodeError> {
let mut w = BufferWriter::new(Endianness::Little);
w.write_string(&self.color)
.map_err(|_| EncodeError::Invalid {
what: "ShapeType.color encoding",
})?;
w.write_u32(self.x as u32)
.map_err(|_| EncodeError::Invalid {
what: "ShapeType.x encoding",
})?;
w.write_u32(self.y as u32)
.map_err(|_| EncodeError::Invalid {
what: "ShapeType.y encoding",
})?;
w.write_u32(self.shapesize as u32)
.map_err(|_| EncodeError::Invalid {
what: "ShapeType.shapesize encoding",
})?;
out.extend_from_slice(w.as_bytes());
Ok(())
}
fn decode(bytes: &[u8]) -> core::result::Result<Self, DecodeError> {
let mut r = BufferReader::new(bytes, Endianness::Little);
let color = r.read_string().map_err(|_| DecodeError::Invalid {
what: "ShapeType.color decoding",
})?;
let x = r.read_u32().map_err(|_| DecodeError::Invalid {
what: "ShapeType.x decoding",
})? as i32;
let y = r.read_u32().map_err(|_| DecodeError::Invalid {
what: "ShapeType.y decoding",
})? as i32;
let shapesize = r.read_u32().map_err(|_| DecodeError::Invalid {
what: "ShapeType.shapesize decoding",
})? as i32;
Ok(Self {
color,
x,
y,
shapesize,
})
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[repr(i32)]
pub enum ShapeFillKind {
#[default]
SolidFill = 0,
TransparentFill = 1,
HorizontalHatch = 2,
VerticalHatch = 3,
}
impl ShapeFillKind {
#[must_use]
pub const fn to_i32(self) -> i32 {
self as i32
}
#[must_use]
pub const fn from_i32(v: i32) -> Self {
match v {
1 => Self::TransparentFill,
2 => Self::HorizontalHatch,
3 => Self::VerticalHatch,
_ => Self::SolidFill,
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct ShapeExtendedType {
pub color: String,
pub x: i32,
pub y: i32,
pub shapesize: i32,
pub fill_kind: ShapeFillKind,
pub angle: f32,
}
impl ShapeExtendedType {
#[must_use]
pub fn new(
color: impl Into<String>,
x: i32,
y: i32,
shapesize: i32,
fill_kind: ShapeFillKind,
angle: f32,
) -> Self {
Self {
color: color.into(),
x,
y,
shapesize,
fill_kind,
angle,
}
}
}
impl DdsType for ShapeExtendedType {
const TYPE_NAME: &'static str = "ShapeExtendedType";
const HAS_KEY: bool = true;
fn encode_key_holder_be(&self, holder: &mut crate::dds_type::PlainCdr2BeKeyHolder) {
holder.write_string(&self.color);
}
fn encode(&self, out: &mut Vec<u8>) -> core::result::Result<(), EncodeError> {
let mut w = BufferWriter::new(Endianness::Little);
w.write_string(&self.color)
.map_err(|_| EncodeError::Invalid {
what: "ShapeExtendedType.color encoding",
})?;
w.write_u32(self.x as u32)
.map_err(|_| EncodeError::Invalid {
what: "ShapeExtendedType.x encoding",
})?;
w.write_u32(self.y as u32)
.map_err(|_| EncodeError::Invalid {
what: "ShapeExtendedType.y encoding",
})?;
w.write_u32(self.shapesize as u32)
.map_err(|_| EncodeError::Invalid {
what: "ShapeExtendedType.shapesize encoding",
})?;
w.write_u32(self.fill_kind.to_i32() as u32)
.map_err(|_| EncodeError::Invalid {
what: "ShapeExtendedType.fillKind encoding",
})?;
w.write_u32(self.angle.to_bits())
.map_err(|_| EncodeError::Invalid {
what: "ShapeExtendedType.angle encoding",
})?;
out.extend_from_slice(w.as_bytes());
Ok(())
}
fn decode(bytes: &[u8]) -> core::result::Result<Self, DecodeError> {
let mut r = BufferReader::new(bytes, Endianness::Little);
let color = r.read_string().map_err(|_| DecodeError::Invalid {
what: "ShapeExtendedType.color decoding",
})?;
let x = r.read_u32().map_err(|_| DecodeError::Invalid {
what: "ShapeExtendedType.x decoding",
})? as i32;
let y = r.read_u32().map_err(|_| DecodeError::Invalid {
what: "ShapeExtendedType.y decoding",
})? as i32;
let shapesize = r.read_u32().map_err(|_| DecodeError::Invalid {
what: "ShapeExtendedType.shapesize decoding",
})? as i32;
let fill_kind = ShapeFillKind::from_i32(r.read_u32().map_err(|_| DecodeError::Invalid {
what: "ShapeExtendedType.fillKind decoding",
})? as i32);
let angle = f32::from_bits(r.read_u32().map_err(|_| DecodeError::Invalid {
what: "ShapeExtendedType.angle decoding",
})?);
Ok(Self {
color,
x,
y,
shapesize,
fill_kind,
angle,
})
}
}
#[cfg(test)]
#[allow(clippy::unwrap_used, clippy::float_cmp)]
mod tests {
use super::*;
#[test]
fn shape_extended_round_trip() {
let s = ShapeExtendedType::new("BLUE", 100, 150, 30, ShapeFillKind::HorizontalHatch, 45.5);
let mut bytes = Vec::new();
s.encode(&mut bytes).unwrap();
let back = ShapeExtendedType::decode(&bytes).unwrap();
assert_eq!(back, s);
}
#[test]
fn shape_extended_type_name_distinct_from_shape() {
assert_eq!(ShapeExtendedType::TYPE_NAME, "ShapeExtendedType");
assert_ne!(ShapeExtendedType::TYPE_NAME, ShapeType::TYPE_NAME);
#[allow(clippy::assertions_on_constants)]
{
assert!(ShapeExtendedType::HAS_KEY);
}
}
#[test]
fn shape_extended_wire_layout() {
let s = ShapeExtendedType::new("RED", 1, 2, 30, ShapeFillKind::SolidFill, 0.0);
let mut bytes = Vec::new();
s.encode(&mut bytes).unwrap();
assert_eq!(bytes.len(), 4 + 4 + 16 + 4);
assert_eq!(&bytes[0..4], &[4, 0, 0, 0]);
assert_eq!(&bytes[4..8], b"RED\0");
assert_eq!(&bytes[20..24], &[0, 0, 0, 0]); assert_eq!(&bytes[24..28], &[0, 0, 0, 0]); }
#[test]
fn fill_kind_round_trips_and_clamps() {
for k in [
ShapeFillKind::SolidFill,
ShapeFillKind::TransparentFill,
ShapeFillKind::HorizontalHatch,
ShapeFillKind::VerticalHatch,
] {
assert_eq!(ShapeFillKind::from_i32(k.to_i32()), k);
}
assert_eq!(ShapeFillKind::from_i32(99), ShapeFillKind::SolidFill);
}
}