1use alloc::vec::Vec;
15
16use zerodds_cdr::{BufferReader, BufferWriter, EncodeError};
17
18use crate::error::TypeCodecError;
19use crate::type_identifier::TypeIdentifier;
20use crate::type_information::TypeIdentifierWithSize;
21use crate::type_object::common::{decode_seq, encode_seq};
22use crate::type_object::{CompleteTypeObject, MinimalTypeObject};
23
24#[derive(Debug, Clone, Default, PartialEq, Eq)]
30pub struct GetTypesRequest {
31 pub type_ids: Vec<TypeIdentifier>,
33}
34
35impl GetTypesRequest {
36 pub fn encode_into(&self, w: &mut BufferWriter) -> Result<(), EncodeError> {
41 encode_seq(w, &self.type_ids, |w, t| t.encode_into(w))
42 }
43
44 pub fn decode_from(r: &mut BufferReader<'_>) -> Result<Self, TypeCodecError> {
49 let type_ids = decode_seq(r, |rr| {
50 TypeIdentifier::decode_from(rr).map_err(|e| zerodds_cdr::DecodeError::InvalidString {
51 offset: 0,
52 reason: match e {
53 zerodds_cdr::DecodeError::UnexpectedEof { .. } => "eof",
54 _ => "decode",
55 },
56 })
57 })?;
58 Ok(Self { type_ids })
59 }
60}
61
62#[allow(clippy::large_enum_variant)]
71#[derive(Debug, Clone, PartialEq, Eq)]
72pub enum ReplyTypeObject {
73 Minimal(MinimalTypeObject),
75 Complete(CompleteTypeObject),
77}
78
79#[derive(Debug, Clone, Default, PartialEq, Eq)]
81pub struct GetTypesReply {
82 pub types: Vec<ReplyTypeObject>,
86}
87
88impl GetTypesReply {
89 pub fn encode_into(&self, w: &mut BufferWriter) -> Result<(), EncodeError> {
94 encode_seq(w, &self.types, |w, t| match t {
95 ReplyTypeObject::Minimal(m) => {
96 crate::type_object::TypeObject::Minimal(m.clone()).encode_into(w)
97 }
98 ReplyTypeObject::Complete(c) => {
99 crate::type_object::TypeObject::Complete(c.clone()).encode_into(w)
100 }
101 })
102 }
103
104 pub fn decode_from(r: &mut BufferReader<'_>) -> Result<Self, TypeCodecError> {
109 let n = r.read_u32()? as usize;
110 let cap = crate::type_object::common::safe_capacity(n, 1, r.remaining());
116 let mut types = Vec::with_capacity(cap);
117 for _ in 0..n {
118 let to = crate::type_object::TypeObject::decode_from(r)?;
119 types.push(match to {
120 crate::type_object::TypeObject::Minimal(m) => ReplyTypeObject::Minimal(m),
121 crate::type_object::TypeObject::Complete(c) => ReplyTypeObject::Complete(c),
122 });
123 }
124 Ok(Self { types })
125 }
126}
127
128#[derive(Debug, Clone, Default, PartialEq, Eq)]
135pub struct ContinuationPoint(pub Vec<u8>);
136
137impl ContinuationPoint {
138 pub const MAX_LEN: usize = 32;
140
141 pub fn encode_into(&self, w: &mut BufferWriter) -> Result<(), EncodeError> {
148 let len = u32::try_from(self.0.len().min(Self::MAX_LEN)).unwrap_or(Self::MAX_LEN as u32);
149 w.write_u32(len)?;
150 w.write_bytes(&self.0[..len as usize])
151 }
152
153 pub fn decode_from(r: &mut BufferReader<'_>) -> Result<Self, TypeCodecError> {
158 let len = r.read_u32()? as usize;
159 if len > Self::MAX_LEN {
160 return Err(TypeCodecError::UnknownTypeKind { kind: 0 });
161 }
162 Ok(Self(r.read_bytes(len)?.to_vec()))
163 }
164}
165
166#[derive(Debug, Clone, Default, PartialEq, Eq)]
168pub struct GetTypeDependenciesRequest {
169 pub type_ids: Vec<TypeIdentifier>,
171 pub continuation_point: ContinuationPoint,
173}
174
175impl GetTypeDependenciesRequest {
176 pub fn encode_into(&self, w: &mut BufferWriter) -> Result<(), EncodeError> {
181 encode_seq(w, &self.type_ids, |w, t| t.encode_into(w))?;
182 self.continuation_point.encode_into(w)
183 }
184
185 pub fn decode_from(r: &mut BufferReader<'_>) -> Result<Self, TypeCodecError> {
190 let type_ids = decode_seq(r, |rr| {
191 TypeIdentifier::decode_from(rr).map_err(|_| zerodds_cdr::DecodeError::InvalidString {
192 offset: 0,
193 reason: "type_id",
194 })
195 })?;
196 let continuation_point = ContinuationPoint::decode_from(r)?;
197 Ok(Self {
198 type_ids,
199 continuation_point,
200 })
201 }
202}
203
204#[derive(Debug, Clone, Default, PartialEq, Eq)]
206pub struct GetTypeDependenciesReply {
207 pub dependent_typeids: Vec<TypeIdentifierWithSize>,
209 pub continuation_point: ContinuationPoint,
211}
212
213impl GetTypeDependenciesReply {
214 pub fn encode_into(&self, w: &mut BufferWriter) -> Result<(), EncodeError> {
219 encode_seq(w, &self.dependent_typeids, |w, t| t.encode_into(w))?;
220 self.continuation_point.encode_into(w)
221 }
222
223 pub fn decode_from(r: &mut BufferReader<'_>) -> Result<Self, TypeCodecError> {
228 let dependent_typeids = decode_seq(r, |rr| {
229 TypeIdentifierWithSize::decode_from(rr).map_err(|_| {
230 zerodds_cdr::DecodeError::InvalidString {
231 offset: 0,
232 reason: "ti_size",
233 }
234 })
235 })?;
236 let continuation_point = ContinuationPoint::decode_from(r)?;
237 Ok(Self {
238 dependent_typeids,
239 continuation_point,
240 })
241 }
242}
243
244#[cfg(test)]
249#[allow(clippy::unwrap_used)]
250mod tests {
251 use super::*;
252 use crate::type_identifier::{EquivalenceHash, PrimitiveKind};
253 use zerodds_cdr::{BufferReader, BufferWriter, Endianness};
254
255 fn roundtrip_get_types_request(req: GetTypesRequest) {
256 let mut w = BufferWriter::new(Endianness::Little);
257 req.encode_into(&mut w).unwrap();
258 let bytes = w.into_bytes();
259 let mut r = BufferReader::new(&bytes, Endianness::Little);
260 let decoded = GetTypesRequest::decode_from(&mut r).unwrap();
261 assert_eq!(decoded, req);
262 }
263
264 #[test]
265 fn get_types_request_roundtrips() {
266 roundtrip_get_types_request(GetTypesRequest {
267 type_ids: alloc::vec![
268 TypeIdentifier::EquivalenceHashMinimal(EquivalenceHash([0x01; 14])),
269 TypeIdentifier::Primitive(PrimitiveKind::Int64),
270 ],
271 });
272 }
273
274 #[test]
275 fn continuation_point_roundtrip_and_max_len() {
276 let cp = ContinuationPoint(alloc::vec![0x11, 0x22, 0x33]);
277 let mut w = BufferWriter::new(Endianness::Little);
278 cp.encode_into(&mut w).unwrap();
279 let bytes = w.into_bytes();
280 let mut r = BufferReader::new(&bytes, Endianness::Little);
281 assert_eq!(ContinuationPoint::decode_from(&mut r).unwrap(), cp);
282
283 let oversized = ContinuationPoint(alloc::vec![0xFF; 64]);
285 let mut w = BufferWriter::new(Endianness::Little);
286 oversized.encode_into(&mut w).unwrap();
287 let bytes = w.into_bytes();
288 let mut r = BufferReader::new(&bytes, Endianness::Little);
289 let decoded = ContinuationPoint::decode_from(&mut r).unwrap();
290 assert_eq!(decoded.0.len(), ContinuationPoint::MAX_LEN);
291 }
292
293 #[test]
294 fn get_types_reply_roundtrip_with_mixed_minimal_and_complete() {
295 use crate::builder::TypeObjectBuilder;
296 let min = MinimalTypeObject::Struct(
297 TypeObjectBuilder::struct_type("::X")
298 .member("a", TypeIdentifier::Primitive(PrimitiveKind::Int32), |m| m)
299 .build_minimal(),
300 );
301 let com = CompleteTypeObject::Struct(
302 TypeObjectBuilder::struct_type("::X")
303 .member("a", TypeIdentifier::Primitive(PrimitiveKind::Int32), |m| m)
304 .build_complete(),
305 );
306 let reply = GetTypesReply {
307 types: alloc::vec![
308 ReplyTypeObject::Minimal(min),
309 ReplyTypeObject::Complete(com),
310 ],
311 };
312 let mut w = BufferWriter::new(Endianness::Little);
313 reply.encode_into(&mut w).unwrap();
314 let bytes = w.into_bytes();
315 let mut r = BufferReader::new(&bytes, Endianness::Little);
316 let decoded = GetTypesReply::decode_from(&mut r).unwrap();
317 assert_eq!(decoded.types.len(), 2);
318 }
319
320 #[test]
321 fn continuation_point_too_large_on_decode_rejected() {
322 let mut w = BufferWriter::new(Endianness::Little);
325 w.write_u32(100).unwrap(); w.write_bytes(&[0u8; 100]).unwrap();
327 let bytes = w.into_bytes();
328 let mut r = BufferReader::new(&bytes, Endianness::Little);
329 let err = ContinuationPoint::decode_from(&mut r).unwrap_err();
330 assert!(matches!(err, TypeCodecError::UnknownTypeKind { .. }));
331 }
332
333 #[test]
334 fn get_type_dependencies_request_reply_roundtrip() {
335 let req = GetTypeDependenciesRequest {
336 type_ids: alloc::vec![TypeIdentifier::EquivalenceHashMinimal(EquivalenceHash(
337 [0xAA; 14]
338 ))],
339 continuation_point: ContinuationPoint::default(),
340 };
341 let mut w = BufferWriter::new(Endianness::Little);
342 req.encode_into(&mut w).unwrap();
343 let bytes = w.into_bytes();
344 let mut r = BufferReader::new(&bytes, Endianness::Little);
345 assert_eq!(
346 GetTypeDependenciesRequest::decode_from(&mut r).unwrap(),
347 req
348 );
349
350 let reply = GetTypeDependenciesReply {
351 dependent_typeids: alloc::vec![TypeIdentifierWithSize {
352 type_id: TypeIdentifier::EquivalenceHashMinimal(EquivalenceHash([0xBB; 14])),
353 typeobject_serialized_size: 128,
354 }],
355 continuation_point: ContinuationPoint(alloc::vec![0x42]),
356 };
357 let mut w = BufferWriter::new(Endianness::Little);
358 reply.encode_into(&mut w).unwrap();
359 let bytes = w.into_bytes();
360 let mut r = BufferReader::new(&bytes, Endianness::Little);
361 assert_eq!(
362 GetTypeDependenciesReply::decode_from(&mut r).unwrap(),
363 reply
364 );
365 }
366
367 #[test]
370 fn empty_get_types_request_roundtrips() {
371 roundtrip_get_types_request(GetTypesRequest::default());
372 }
373
374 #[test]
375 fn empty_get_types_reply_roundtrips() {
376 let reply = GetTypesReply::default();
377 let mut w = BufferWriter::new(Endianness::Little);
378 reply.encode_into(&mut w).unwrap();
379 let bytes = w.into_bytes();
380 let mut r = BufferReader::new(&bytes, Endianness::Little);
381 let decoded = GetTypesReply::decode_from(&mut r).unwrap();
382 assert_eq!(decoded.types.len(), 0);
383 assert_eq!(decoded, reply);
384 }
385
386 #[test]
387 fn empty_get_type_dependencies_request_roundtrips() {
388 let req = GetTypeDependenciesRequest::default();
389 let mut w = BufferWriter::new(Endianness::Little);
390 req.encode_into(&mut w).unwrap();
391 let bytes = w.into_bytes();
392 let mut r = BufferReader::new(&bytes, Endianness::Little);
393 let decoded = GetTypeDependenciesRequest::decode_from(&mut r).unwrap();
394 assert!(decoded.type_ids.is_empty());
395 assert!(decoded.continuation_point.0.is_empty());
396 assert_eq!(decoded, req);
397 }
398
399 #[test]
400 fn empty_get_type_dependencies_reply_roundtrips() {
401 let reply = GetTypeDependenciesReply::default();
402 let mut w = BufferWriter::new(Endianness::Little);
403 reply.encode_into(&mut w).unwrap();
404 let bytes = w.into_bytes();
405 let mut r = BufferReader::new(&bytes, Endianness::Little);
406 let decoded = GetTypeDependenciesReply::decode_from(&mut r).unwrap();
407 assert!(decoded.dependent_typeids.is_empty());
408 assert!(decoded.continuation_point.0.is_empty());
409 }
410
411 #[test]
412 fn continuation_point_len_zero_encodes_to_four_zero_bytes() {
413 let cp = ContinuationPoint(alloc::vec![]);
414 let mut w = BufferWriter::new(Endianness::Little);
415 cp.encode_into(&mut w).unwrap();
416 let bytes = w.into_bytes();
417 assert_eq!(bytes, alloc::vec![0, 0, 0, 0]);
419 let mut r = BufferReader::new(&bytes, Endianness::Little);
420 assert_eq!(ContinuationPoint::decode_from(&mut r).unwrap(), cp);
421 }
422
423 #[test]
424 fn continuation_point_len_max_roundtrips() {
425 let cp = ContinuationPoint(alloc::vec![0xAB; ContinuationPoint::MAX_LEN]);
426 let mut w = BufferWriter::new(Endianness::Little);
427 cp.encode_into(&mut w).unwrap();
428 let bytes = w.into_bytes();
429 let mut r = BufferReader::new(&bytes, Endianness::Little);
430 let decoded = ContinuationPoint::decode_from(&mut r).unwrap();
431 assert_eq!(decoded.0.len(), ContinuationPoint::MAX_LEN);
432 assert_eq!(decoded, cp);
433 }
434
435 #[test]
436 fn continuation_point_len_max_plus_one_on_decode_rejected() {
437 let mut w = BufferWriter::new(Endianness::Little);
438 w.write_u32((ContinuationPoint::MAX_LEN + 1) as u32)
439 .unwrap();
440 w.write_bytes(&[0u8; 33]).unwrap();
441 let bytes = w.into_bytes();
442 let mut r = BufferReader::new(&bytes, Endianness::Little);
443 let err = ContinuationPoint::decode_from(&mut r).unwrap_err();
444 assert!(matches!(err, TypeCodecError::UnknownTypeKind { .. }));
445 }
446
447 #[test]
448 fn reply_type_object_minimal_and_complete_serialize_distinct_discriminator() {
449 use crate::builder::TypeObjectBuilder;
450 let min = ReplyTypeObject::Minimal(MinimalTypeObject::Struct(
451 TypeObjectBuilder::struct_type("::X")
452 .member("a", TypeIdentifier::Primitive(PrimitiveKind::Int32), |m| m)
453 .build_minimal(),
454 ));
455 let com = ReplyTypeObject::Complete(crate::type_object::CompleteTypeObject::Struct(
456 TypeObjectBuilder::struct_type("::X")
457 .member("a", TypeIdentifier::Primitive(PrimitiveKind::Int32), |m| m)
458 .build_complete(),
459 ));
460 let reply = GetTypesReply {
461 types: alloc::vec![min, com],
462 };
463 let mut w = BufferWriter::new(Endianness::Little);
464 reply.encode_into(&mut w).unwrap();
465 let bytes = w.into_bytes();
466 assert_eq!(&bytes[..4], &[2, 0, 0, 0]);
468 let mut r = BufferReader::new(&bytes, Endianness::Little);
470 let decoded = GetTypesReply::decode_from(&mut r).unwrap();
471 assert!(matches!(decoded.types[0], ReplyTypeObject::Minimal(_)));
472 assert!(matches!(decoded.types[1], ReplyTypeObject::Complete(_)));
473 }
474
475 #[test]
476 fn get_type_dependencies_request_with_continuation_payload_roundtrips() {
477 let req = GetTypeDependenciesRequest {
478 type_ids: alloc::vec![],
479 continuation_point: ContinuationPoint(alloc::vec![0xDE, 0xAD, 0xBE, 0xEF]),
480 };
481 let mut w = BufferWriter::new(Endianness::Little);
482 req.encode_into(&mut w).unwrap();
483 let bytes = w.into_bytes();
484 let mut r = BufferReader::new(&bytes, Endianness::Little);
485 assert_eq!(
486 GetTypeDependenciesRequest::decode_from(&mut r).unwrap(),
487 req
488 );
489 }
490}