1extern crate alloc;
45
46use alloc::string::String;
47use alloc::vec::Vec;
48
49use zerodds_cdr::buffer::{BufferReader, BufferWriter};
50use zerodds_cdr::endianness::Endianness;
51
52use crate::dds_type::{DdsType, DecodeError, EncodeError};
53
54#[derive(Debug, Clone, PartialEq, Eq)]
59pub struct ShapeType {
60 pub color: String,
65 pub x: i32,
67 pub y: i32,
69 pub shapesize: i32,
71}
72
73impl ShapeType {
74 #[must_use]
76 pub fn new(color: impl Into<String>, x: i32, y: i32, shapesize: i32) -> Self {
77 Self {
78 color: color.into(),
79 x,
80 y,
81 shapesize,
82 }
83 }
84}
85
86impl DdsType for ShapeType {
87 const TYPE_NAME: &'static str = "ShapeType";
90 const HAS_KEY: bool = true;
94
95 fn encode_key_holder_be(&self, holder: &mut crate::dds_type::PlainCdr2BeKeyHolder) {
96 holder.write_string(&self.color);
97 }
98
99 fn encode(&self, out: &mut Vec<u8>) -> core::result::Result<(), EncodeError> {
100 let mut w = BufferWriter::new(Endianness::Little);
101 w.write_string(&self.color)
102 .map_err(|_| EncodeError::Invalid {
103 what: "ShapeType.color encoding",
104 })?;
105 w.write_u32(self.x as u32)
106 .map_err(|_| EncodeError::Invalid {
107 what: "ShapeType.x encoding",
108 })?;
109 w.write_u32(self.y as u32)
110 .map_err(|_| EncodeError::Invalid {
111 what: "ShapeType.y encoding",
112 })?;
113 w.write_u32(self.shapesize as u32)
114 .map_err(|_| EncodeError::Invalid {
115 what: "ShapeType.shapesize encoding",
116 })?;
117 out.extend_from_slice(w.as_bytes());
118 Ok(())
119 }
120
121 fn decode(bytes: &[u8]) -> core::result::Result<Self, DecodeError> {
122 let mut r = BufferReader::new(bytes, Endianness::Little);
123 let color = r.read_string().map_err(|_| DecodeError::Invalid {
124 what: "ShapeType.color decoding",
125 })?;
126 let x = r.read_u32().map_err(|_| DecodeError::Invalid {
127 what: "ShapeType.x decoding",
128 })? as i32;
129 let y = r.read_u32().map_err(|_| DecodeError::Invalid {
130 what: "ShapeType.y decoding",
131 })? as i32;
132 let shapesize = r.read_u32().map_err(|_| DecodeError::Invalid {
133 what: "ShapeType.shapesize decoding",
134 })? as i32;
135 Ok(Self {
136 color,
137 x,
138 y,
139 shapesize,
140 })
141 }
142}
143
144#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
154#[repr(i32)]
155pub enum ShapeFillKind {
156 #[default]
158 SolidFill = 0,
159 TransparentFill = 1,
161 HorizontalHatch = 2,
163 VerticalHatch = 3,
165}
166
167impl ShapeFillKind {
168 #[must_use]
170 pub const fn to_i32(self) -> i32 {
171 self as i32
172 }
173
174 #[must_use]
177 pub const fn from_i32(v: i32) -> Self {
178 match v {
179 1 => Self::TransparentFill,
180 2 => Self::HorizontalHatch,
181 3 => Self::VerticalHatch,
182 _ => Self::SolidFill,
183 }
184 }
185}
186
187#[derive(Debug, Clone, PartialEq)]
207pub struct ShapeExtendedType {
208 pub color: String,
210 pub x: i32,
212 pub y: i32,
214 pub shapesize: i32,
216 pub fill_kind: ShapeFillKind,
218 pub angle: f32,
220}
221
222impl ShapeExtendedType {
223 #[must_use]
225 pub fn new(
226 color: impl Into<String>,
227 x: i32,
228 y: i32,
229 shapesize: i32,
230 fill_kind: ShapeFillKind,
231 angle: f32,
232 ) -> Self {
233 Self {
234 color: color.into(),
235 x,
236 y,
237 shapesize,
238 fill_kind,
239 angle,
240 }
241 }
242}
243
244impl DdsType for ShapeExtendedType {
245 const TYPE_NAME: &'static str = "ShapeExtendedType";
247 const HAS_KEY: bool = true;
249
250 fn encode_key_holder_be(&self, holder: &mut crate::dds_type::PlainCdr2BeKeyHolder) {
251 holder.write_string(&self.color);
252 }
253
254 fn encode(&self, out: &mut Vec<u8>) -> core::result::Result<(), EncodeError> {
255 let mut w = BufferWriter::new(Endianness::Little);
256 w.write_string(&self.color)
257 .map_err(|_| EncodeError::Invalid {
258 what: "ShapeExtendedType.color encoding",
259 })?;
260 w.write_u32(self.x as u32)
261 .map_err(|_| EncodeError::Invalid {
262 what: "ShapeExtendedType.x encoding",
263 })?;
264 w.write_u32(self.y as u32)
265 .map_err(|_| EncodeError::Invalid {
266 what: "ShapeExtendedType.y encoding",
267 })?;
268 w.write_u32(self.shapesize as u32)
269 .map_err(|_| EncodeError::Invalid {
270 what: "ShapeExtendedType.shapesize encoding",
271 })?;
272 w.write_u32(self.fill_kind.to_i32() as u32)
273 .map_err(|_| EncodeError::Invalid {
274 what: "ShapeExtendedType.fillKind encoding",
275 })?;
276 w.write_u32(self.angle.to_bits())
279 .map_err(|_| EncodeError::Invalid {
280 what: "ShapeExtendedType.angle encoding",
281 })?;
282 out.extend_from_slice(w.as_bytes());
283 Ok(())
284 }
285
286 fn decode(bytes: &[u8]) -> core::result::Result<Self, DecodeError> {
287 let mut r = BufferReader::new(bytes, Endianness::Little);
288 let color = r.read_string().map_err(|_| DecodeError::Invalid {
289 what: "ShapeExtendedType.color decoding",
290 })?;
291 let x = r.read_u32().map_err(|_| DecodeError::Invalid {
292 what: "ShapeExtendedType.x decoding",
293 })? as i32;
294 let y = r.read_u32().map_err(|_| DecodeError::Invalid {
295 what: "ShapeExtendedType.y decoding",
296 })? as i32;
297 let shapesize = r.read_u32().map_err(|_| DecodeError::Invalid {
298 what: "ShapeExtendedType.shapesize decoding",
299 })? as i32;
300 let fill_kind = ShapeFillKind::from_i32(r.read_u32().map_err(|_| DecodeError::Invalid {
301 what: "ShapeExtendedType.fillKind decoding",
302 })? as i32);
303 let angle = f32::from_bits(r.read_u32().map_err(|_| DecodeError::Invalid {
304 what: "ShapeExtendedType.angle decoding",
305 })?);
306 Ok(Self {
307 color,
308 x,
309 y,
310 shapesize,
311 fill_kind,
312 angle,
313 })
314 }
315}
316
317#[cfg(test)]
318#[allow(clippy::unwrap_used, clippy::float_cmp)]
319mod tests {
320 use super::*;
321
322 #[test]
323 fn shape_extended_round_trip() {
324 let s = ShapeExtendedType::new("BLUE", 100, 150, 30, ShapeFillKind::HorizontalHatch, 45.5);
325 let mut bytes = Vec::new();
326 s.encode(&mut bytes).unwrap();
327 let back = ShapeExtendedType::decode(&bytes).unwrap();
328 assert_eq!(back, s);
329 }
330
331 #[test]
332 fn shape_extended_type_name_distinct_from_shape() {
333 assert_eq!(ShapeExtendedType::TYPE_NAME, "ShapeExtendedType");
334 assert_ne!(ShapeExtendedType::TYPE_NAME, ShapeType::TYPE_NAME);
335 #[allow(clippy::assertions_on_constants)]
339 {
340 assert!(ShapeExtendedType::HAS_KEY);
341 }
342 }
343
344 #[test]
345 fn shape_extended_wire_layout() {
346 let s = ShapeExtendedType::new("RED", 1, 2, 30, ShapeFillKind::SolidFill, 0.0);
349 let mut bytes = Vec::new();
350 s.encode(&mut bytes).unwrap();
351 assert_eq!(bytes.len(), 4 + 4 + 16 + 4);
353 assert_eq!(&bytes[0..4], &[4, 0, 0, 0]);
355 assert_eq!(&bytes[4..8], b"RED\0");
356 assert_eq!(&bytes[20..24], &[0, 0, 0, 0]); assert_eq!(&bytes[24..28], &[0, 0, 0, 0]); }
360
361 #[test]
362 fn fill_kind_round_trips_and_clamps() {
363 for k in [
364 ShapeFillKind::SolidFill,
365 ShapeFillKind::TransparentFill,
366 ShapeFillKind::HorizontalHatch,
367 ShapeFillKind::VerticalHatch,
368 ] {
369 assert_eq!(ShapeFillKind::from_i32(k.to_i32()), k);
370 }
371 assert_eq!(ShapeFillKind::from_i32(99), ShapeFillKind::SolidFill);
373 }
374}