1use alloc::vec::Vec;
35
36use zerodds_cdr::{BufferReader, BufferWriter, EncodeError, Endianness};
37
38use crate::error::TypeCodecError;
39use crate::hash::{compute_complete_hash, compute_minimal_hash};
40use crate::type_identifier::TypeIdentifier;
41use crate::type_object::common::{decode_seq, encode_seq};
42use crate::type_object::{CompleteTypeObject, MinimalTypeObject};
43
44#[derive(Debug, Clone, PartialEq, Eq)]
46pub struct TypeIdentifierWithSize {
47 pub type_id: TypeIdentifier,
49 pub typeobject_serialized_size: u32,
54}
55
56impl TypeIdentifierWithSize {
57 pub fn encode_into(&self, w: &mut BufferWriter) -> Result<(), EncodeError> {
62 self.type_id.encode_into(w)?;
63 w.write_u32(self.typeobject_serialized_size)
64 }
65
66 pub fn decode_from(r: &mut BufferReader<'_>) -> Result<Self, TypeCodecError> {
71 let type_id = TypeIdentifier::decode_from(r)?;
72 let typeobject_serialized_size = r.read_u32()?;
73 Ok(Self {
74 type_id,
75 typeobject_serialized_size,
76 })
77 }
78}
79
80#[derive(Debug, Clone, PartialEq, Eq)]
82pub struct TypeIdentifierWithDependencies {
83 pub typeid_with_size: TypeIdentifierWithSize,
85 pub dependent_typeid_count: i32,
91 pub dependent_typeids: Vec<TypeIdentifierWithSize>,
93}
94
95impl TypeIdentifierWithDependencies {
96 #[must_use]
98 pub fn without_dependencies(typeid_with_size: TypeIdentifierWithSize) -> Self {
99 Self {
100 typeid_with_size,
101 dependent_typeid_count: 0,
102 dependent_typeids: Vec::new(),
103 }
104 }
105
106 pub fn encode_into(&self, w: &mut BufferWriter) -> Result<(), EncodeError> {
111 self.typeid_with_size.encode_into(w)?;
112 w.write_u32(self.dependent_typeid_count as u32)?;
113 encode_seq(w, &self.dependent_typeids, |w, t| t.encode_into(w))
114 }
115
116 pub fn decode_from(r: &mut BufferReader<'_>) -> Result<Self, TypeCodecError> {
121 let typeid_with_size = TypeIdentifierWithSize::decode_from(r)?;
122 let dependent_typeid_count = r.read_u32()? as i32;
123 let dependent_typeids = decode_seq(r, |rr| {
124 TypeIdentifierWithSize::decode_from(rr).map_err(|e| match e {
125 TypeCodecError::Encode(_) => zerodds_cdr::DecodeError::UnexpectedEof {
126 needed: 0,
127 offset: 0,
128 },
129 TypeCodecError::Decode(d) => d,
130 TypeCodecError::UnknownTypeKind { .. } => zerodds_cdr::DecodeError::UnexpectedEof {
131 needed: 0,
132 offset: 0,
133 },
134 })
135 })?;
136 Ok(Self {
137 typeid_with_size,
138 dependent_typeid_count,
139 dependent_typeids,
140 })
141 }
142}
143
144#[derive(Debug, Clone, PartialEq, Eq)]
148pub struct TypeInformation {
149 pub minimal: TypeIdentifierWithDependencies,
151 pub complete: TypeIdentifierWithDependencies,
153}
154
155impl TypeInformation {
156 pub fn from_minimal_and_complete(
163 minimal: &MinimalTypeObject,
164 complete: &CompleteTypeObject,
165 ) -> Result<Self, EncodeError> {
166 let min_hash = compute_minimal_hash(minimal)?;
167 let com_hash = compute_complete_hash(complete)?;
168 let min_size = u32::try_from(
173 crate::type_object::TypeObject::Minimal(minimal.clone())
174 .to_bytes_le()?
175 .len(),
176 )
177 .map_err(|_| EncodeError::ValueOutOfRange {
178 message: "minimal TypeObject serialized size exceeds u32::MAX",
179 })?;
180 let com_size = u32::try_from(
181 crate::type_object::TypeObject::Complete(complete.clone())
182 .to_bytes_le()?
183 .len(),
184 )
185 .map_err(|_| EncodeError::ValueOutOfRange {
186 message: "complete TypeObject serialized size exceeds u32::MAX",
187 })?;
188 Ok(Self {
189 minimal: TypeIdentifierWithDependencies::without_dependencies(TypeIdentifierWithSize {
190 type_id: TypeIdentifier::EquivalenceHashMinimal(min_hash),
191 typeobject_serialized_size: min_size,
192 }),
193 complete: TypeIdentifierWithDependencies::without_dependencies(
194 TypeIdentifierWithSize {
195 type_id: TypeIdentifier::EquivalenceHashComplete(com_hash),
196 typeobject_serialized_size: com_size,
197 },
198 ),
199 })
200 }
201
202 pub fn add_dependency(
204 &mut self,
205 minimal_dep: TypeIdentifierWithSize,
206 complete_dep: TypeIdentifierWithSize,
207 ) {
208 self.minimal.dependent_typeids.push(minimal_dep);
209 self.minimal.dependent_typeid_count =
210 self.minimal.dependent_typeids.len().min(i32::MAX as usize) as i32;
211 self.complete.dependent_typeids.push(complete_dep);
212 self.complete.dependent_typeid_count =
213 self.complete.dependent_typeids.len().min(i32::MAX as usize) as i32;
214 }
215
216 pub fn encode_into(&self, w: &mut BufferWriter) -> Result<(), EncodeError> {
222 self.minimal.encode_into(w)?;
223 self.complete.encode_into(w)
224 }
225
226 pub fn decode_from(r: &mut BufferReader<'_>) -> Result<Self, TypeCodecError> {
231 let minimal = TypeIdentifierWithDependencies::decode_from(r)?;
232 let complete = TypeIdentifierWithDependencies::decode_from(r)?;
233 Ok(Self { minimal, complete })
234 }
235
236 pub fn to_bytes_le(&self) -> Result<Vec<u8>, EncodeError> {
241 let mut w = BufferWriter::new(Endianness::Little);
242 self.encode_into(&mut w)?;
243 Ok(w.into_bytes())
244 }
245
246 pub fn from_bytes_le(bytes: &[u8]) -> Result<Self, TypeCodecError> {
251 let mut r = BufferReader::new(bytes, Endianness::Little);
252 Self::decode_from(&mut r)
253 }
254}
255
256#[cfg(test)]
261#[allow(clippy::unwrap_used)]
262mod tests {
263 use super::*;
264 use crate::builder::{Extensibility, TypeObjectBuilder};
265 use crate::type_identifier::{EquivalenceHash, PrimitiveKind};
266
267 fn sample_type_info() -> TypeInformation {
268 let b = TypeObjectBuilder::struct_type("::chat::Chatter")
269 .extensibility(Extensibility::Appendable)
270 .member("id", TypeIdentifier::Primitive(PrimitiveKind::Int64), |m| {
271 m.key()
272 })
273 .member("text", TypeIdentifier::String8Small { bound: 255 }, |m| m);
274 let minimal = MinimalTypeObject::Struct(b.build_minimal());
275 let complete = CompleteTypeObject::Struct(b.build_complete());
276 TypeInformation::from_minimal_and_complete(&minimal, &complete).unwrap()
277 }
278
279 #[test]
280 fn typeinformation_roundtrips() {
281 let ti = sample_type_info();
282 let bytes = ti.to_bytes_le().unwrap();
283 let decoded = TypeInformation::from_bytes_le(&bytes).unwrap();
284 assert_eq!(ti, decoded);
285 }
286
287 #[test]
288 fn typeinformation_minimal_has_minimal_discriminator() {
289 let ti = sample_type_info();
290 assert!(matches!(
291 ti.minimal.typeid_with_size.type_id,
292 TypeIdentifier::EquivalenceHashMinimal(_)
293 ));
294 assert!(matches!(
295 ti.complete.typeid_with_size.type_id,
296 TypeIdentifier::EquivalenceHashComplete(_)
297 ));
298 }
299
300 #[test]
301 fn typeinformation_size_matches_actual_typeobject_bytes() {
302 let b = TypeObjectBuilder::struct_type("::X").member(
303 "a",
304 TypeIdentifier::Primitive(PrimitiveKind::Int32),
305 |m| m,
306 );
307 let minimal = MinimalTypeObject::Struct(b.build_minimal());
308 let complete = CompleteTypeObject::Struct(b.build_complete());
309 let actual_min_size = crate::type_object::TypeObject::Minimal(minimal.clone())
310 .to_bytes_le()
311 .unwrap()
312 .len();
313 let actual_com_size = crate::type_object::TypeObject::Complete(complete.clone())
314 .to_bytes_le()
315 .unwrap()
316 .len();
317 let ti = TypeInformation::from_minimal_and_complete(&minimal, &complete).unwrap();
318 assert_eq!(
319 ti.minimal.typeid_with_size.typeobject_serialized_size,
320 actual_min_size as u32
321 );
322 assert_eq!(
323 ti.complete.typeid_with_size.typeobject_serialized_size,
324 actual_com_size as u32
325 );
326 }
327
328 #[test]
329 fn add_dependency_updates_count_and_list() {
330 let mut ti = sample_type_info();
331 assert_eq!(ti.minimal.dependent_typeid_count, 0);
332 let dep_min = TypeIdentifierWithSize {
333 type_id: TypeIdentifier::EquivalenceHashMinimal(EquivalenceHash([0xAA; 14])),
334 typeobject_serialized_size: 42,
335 };
336 let dep_com = TypeIdentifierWithSize {
337 type_id: TypeIdentifier::EquivalenceHashComplete(EquivalenceHash([0xBB; 14])),
338 typeobject_serialized_size: 84,
339 };
340 ti.add_dependency(dep_min.clone(), dep_com.clone());
341 assert_eq!(ti.minimal.dependent_typeid_count, 1);
342 assert_eq!(ti.minimal.dependent_typeids[0], dep_min);
343 assert_eq!(ti.complete.dependent_typeid_count, 1);
344 assert_eq!(ti.complete.dependent_typeids[0], dep_com);
345
346 let bytes = ti.to_bytes_le().unwrap();
348 assert_eq!(TypeInformation::from_bytes_le(&bytes).unwrap(), ti);
349 }
350
351 #[test]
352 fn negative_dependent_typeid_count_roundtrips() {
353 let mut ti = sample_type_info();
355 ti.minimal.dependent_typeid_count = -1;
356 ti.complete.dependent_typeid_count = -1;
357 let bytes = ti.to_bytes_le().unwrap();
358 let decoded = TypeInformation::from_bytes_le(&bytes).unwrap();
359 assert_eq!(decoded.minimal.dependent_typeid_count, -1);
360 assert_eq!(decoded.complete.dependent_typeid_count, -1);
361 }
362}