1use std::fmt;
4use std::marker::PhantomData;
5
6use serde::de::{Error as SerdeError, SeqAccess, Visitor};
7use serde::ser::SerializeSeq;
8use serde::{Deserialize, Serialize};
9use serde_bytes::{ByteBuf as SerdeByteBuf, Bytes as SerdeBytes};
10
11use crate::cursor::Cursor;
12use crate::hash::{Hash, HashError};
13use crate::identity::{Author, IdentityError, Signature, SigningKey, VerifyingKey};
14use crate::logs::{LogHeights, LogId, SeqNum};
15use crate::operation::{Body, Header, Version};
16use crate::topic::{Topic, TopicError};
17
18pub fn serialize_hex<S>(value: &[u8], serializer: S) -> Result<S::Ok, S::Error>
21where
22 S: serde::Serializer,
23{
24 if serializer.is_human_readable() {
25 hex::serde::serialize(value, serializer)
26 } else {
27 SerdeBytes::new(value).serialize(serializer)
28 }
29}
30
31pub fn deserialize_hex<'de, D>(deserializer: D) -> Result<Vec<u8>, D::Error>
34where
35 D: serde::Deserializer<'de>,
36{
37 if deserializer.is_human_readable() {
38 hex::serde::deserialize(deserializer)
39 } else {
40 let bytes = <SerdeByteBuf>::deserialize(deserializer)?;
41 Ok(bytes.to_vec())
42 }
43}
44
45impl Serialize for Hash {
46 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
47 where
48 S: serde::Serializer,
49 {
50 serialize_hex(self.as_bytes(), serializer)
51 }
52}
53
54impl<'de> Deserialize<'de> for Hash {
55 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
56 where
57 D: serde::Deserializer<'de>,
58 {
59 let bytes = deserialize_hex(deserializer)?;
60
61 bytes
62 .as_slice()
63 .try_into()
64 .map_err(|err: HashError| serde::de::Error::custom(err.to_string()))
65 }
66}
67
68impl Serialize for SigningKey {
69 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
70 where
71 S: serde::Serializer,
72 {
73 serialize_hex(self.as_bytes(), serializer)
74 }
75}
76
77impl<'de> Deserialize<'de> for SigningKey {
78 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
79 where
80 D: serde::Deserializer<'de>,
81 {
82 let bytes = deserialize_hex(deserializer)?;
83
84 bytes
85 .as_slice()
86 .try_into()
87 .map_err(|err: IdentityError| serde::de::Error::custom(err.to_string()))
88 }
89}
90
91impl Serialize for VerifyingKey {
92 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
93 where
94 S: serde::Serializer,
95 {
96 serialize_hex(self.as_bytes(), serializer)
97 }
98}
99
100impl<'de> Deserialize<'de> for VerifyingKey {
101 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
102 where
103 D: serde::Deserializer<'de>,
104 {
105 let bytes = deserialize_hex(deserializer)?;
106
107 bytes
108 .as_slice()
109 .try_into()
110 .map_err(|err: IdentityError| serde::de::Error::custom(err.to_string()))
111 }
112}
113
114impl Serialize for Signature {
115 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
116 where
117 S: serde::Serializer,
118 {
119 serialize_hex(&self.to_bytes(), serializer)
120 }
121}
122
123impl<'de> Deserialize<'de> for Signature {
124 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
125 where
126 D: serde::Deserializer<'de>,
127 {
128 let bytes = deserialize_hex(deserializer)?;
129
130 bytes
131 .as_slice()
132 .try_into()
133 .map_err(|err: IdentityError| serde::de::Error::custom(err.to_string()))
134 }
135}
136
137impl<E> Serialize for Header<E>
138where
139 E: Serialize,
140{
141 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
142 where
143 S: serde::Serializer,
144 {
145 let mut seq = serializer.serialize_seq(Some(self.field_count()))?;
146 seq.serialize_element(&self.version)?;
147 seq.serialize_element(&self.verifying_key)?;
148
149 if let Some(signature) = &self.signature {
150 seq.serialize_element(signature)?;
151 }
152
153 seq.serialize_element(&self.payload_size)?;
154 if let Some(hash) = &self.payload_hash {
155 seq.serialize_element(&hash)?;
156 }
157
158 seq.serialize_element(&self.seq_num)?;
159
160 if let Some(backlink) = &self.backlink {
161 seq.serialize_element(backlink)?;
162 }
163
164 if Self::has_non_zero_sized_extensions() {
165 seq.serialize_element(&self.extensions)?;
166 }
167
168 seq.end()
169 }
170}
171
172impl<'de, E> Deserialize<'de> for Header<E>
173where
174 E: Deserialize<'de>,
175{
176 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
177 where
178 D: serde::Deserializer<'de>,
179 {
180 struct HeaderVisitor<E> {
181 _marker: PhantomData<E>,
182 }
183
184 impl<'de, E> Visitor<'de> for HeaderVisitor<E>
185 where
186 E: Deserialize<'de>,
187 {
188 type Value = Header<E>;
189
190 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
191 formatter.write_str("Header encoded as a sequence")
192 }
193
194 fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
195 where
196 A: SeqAccess<'de>,
197 {
198 let version: Version = seq
199 .next_element()?
200 .ok_or(SerdeError::custom("version missing"))?;
201
202 let verifying_key: VerifyingKey = seq
203 .next_element()?
204 .ok_or(SerdeError::custom("public key missing"))?;
205
206 let signature: Signature = seq
207 .next_element()?
208 .ok_or(SerdeError::custom("signature missing"))?;
209
210 let payload_size: u32 = seq
211 .next_element()?
212 .ok_or(SerdeError::custom("payload size missing"))?;
213
214 let payload_hash: Option<Hash> = match payload_size {
215 0 => None,
216 _ => {
217 let hash: Hash = seq
218 .next_element()?
219 .ok_or(SerdeError::custom("payload hash missing"))?;
220 Some(hash)
221 }
222 };
223
224 let seq_num: SeqNum = seq
225 .next_element()?
226 .ok_or(SerdeError::custom("sequence number missing"))?;
227
228 let backlink: Option<Hash> = match seq_num {
229 0 => None,
230 _ => {
231 let hash: Hash = seq
232 .next_element()?
233 .ok_or(SerdeError::custom("backlink missing"))?;
234 Some(hash)
235 }
236 };
237
238 let extensions: E = if Header::<E>::has_non_zero_sized_extensions() {
239 seq.next_element()?
240 .ok_or(SerdeError::custom("extensions missing"))?
241 } else {
242 Header::<E>::zero_sized_extensions()
243 };
244
245 if let Some(remainder) = seq.size_hint()
246 && remainder > 0
247 {
248 return Err(SerdeError::custom(format!(
249 "unexpected {remainder} excessive field(s) in header"
250 )));
251 }
252
253 Ok(Header {
254 version,
255 verifying_key,
256 signature: Some(signature),
257 payload_hash,
258 payload_size,
259 seq_num,
260 backlink,
261 extensions,
262 })
263 }
264 }
265
266 deserializer.deserialize_seq(HeaderVisitor::<E> {
267 _marker: PhantomData,
268 })
269 }
270}
271
272impl Serialize for Body {
273 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
274 where
275 S: serde::Serializer,
276 {
277 serialize_hex(&self.0, serializer)
278 }
279}
280
281impl<'de> Deserialize<'de> for Body {
282 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
283 where
284 D: serde::Deserializer<'de>,
285 {
286 let bytes = deserialize_hex(deserializer)?;
287 Ok(Body(bytes.to_vec()))
288 }
289}
290
291impl Serialize for Topic {
292 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
293 where
294 S: serde::Serializer,
295 {
296 serialize_hex(&self.0, serializer)
297 }
298}
299
300impl<'de> Deserialize<'de> for Topic {
301 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
302 where
303 D: serde::Deserializer<'de>,
304 {
305 let bytes = deserialize_hex(deserializer)?;
306
307 bytes
308 .as_slice()
309 .try_into()
310 .map_err(|err: TopicError| serde::de::Error::custom(err.to_string()))
311 }
312}
313
314impl<A, L> Serialize for Cursor<A, L>
315where
316 A: Author,
317 L: LogId,
318{
319 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
320 where
321 S: serde::Serializer,
322 {
323 let mut seq = serializer.serialize_seq(Some(2))?;
324 seq.serialize_element(self.name())?;
325 seq.serialize_element(self.state())?;
326 seq.end()
327 }
328}
329
330impl<'de, A, L> Deserialize<'de> for Cursor<A, L>
331where
332 A: Author,
333 L: LogId,
334{
335 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
336 where
337 D: serde::Deserializer<'de>,
338 {
339 struct CursorVisitor<A, L> {
340 _marker: PhantomData<(A, L)>,
341 }
342
343 impl<'de, A, L> Visitor<'de> for CursorVisitor<A, L>
344 where
345 A: Author,
346 L: LogId,
347 {
348 type Value = Cursor<A, L>;
349
350 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
351 formatter.write_str("Cursor encoded as a sequence")
352 }
353
354 fn visit_seq<T>(self, mut seq: T) -> Result<Self::Value, T::Error>
355 where
356 T: SeqAccess<'de>,
357 {
358 let name: String = seq
359 .next_element()?
360 .ok_or(SerdeError::custom("cursor id missing"))?;
361
362 let state: LogHeights<A, L> = seq
363 .next_element()?
364 .ok_or(SerdeError::custom("state vector missing"))?;
365
366 Ok(Cursor::new(name, state))
367 }
368 }
369
370 deserializer.deserialize_seq(CursorVisitor::<A, L> {
371 _marker: PhantomData,
372 })
373 }
374}
375
376#[cfg(test)]
377mod tests {
378 use serde::de::DeserializeOwned;
379 use serde::{Deserialize, Serialize};
380
381 use crate::Body;
382 use crate::hash::Hash;
383 use crate::identity::{SigningKey, VerifyingKey};
384 use crate::operation::Header;
385
386 use super::{deserialize_hex, serialize_hex};
387
388 #[derive(Debug, Serialize, Deserialize)]
389 struct Test(
390 #[serde(serialize_with = "serialize_hex", deserialize_with = "deserialize_hex")] Vec<u8>,
391 );
392
393 #[test]
394 fn serialize() {
395 let mut bytes: Vec<u8> = Vec::new();
396 let test = Test(vec![1, 2, 3]);
397
398 ciborium::ser::into_writer(&test, &mut bytes).unwrap();
401 assert_eq!(vec![67, 1, 2, 3], bytes);
402 }
403
404 #[test]
405 fn deserialize() {
406 let bytes: Vec<u8> = vec![67, 1, 2, 3];
407
408 let test: Test = ciborium::de::from_reader(&bytes[..]).unwrap();
411 assert_eq!(test.0, vec![1, 2, 3]);
412 }
413
414 #[test]
415 fn serialize_hash() {
416 let mut bytes: Vec<u8> = Vec::new();
418 let hash = Hash::digest([1, 2, 3]);
419 ciborium::ser::into_writer(&hash, &mut bytes).unwrap();
420 assert_eq!(
421 bytes,
422 vec![
423 88, 32, 177, 119, 236, 27, 242, 109, 251, 59, 112, 16, 212, 115, 230, 212, 71, 19,
424 178, 155, 118, 91, 153, 198, 230, 14, 203, 250, 231, 66, 222, 73, 101, 67
425 ]
426 );
427
428 let json = serde_json::to_string(&hash).unwrap();
430 assert_eq!(
431 json,
432 "\"b177ec1bf26dfb3b7010d473e6d44713b29b765b99c6e60ecbfae742de496543\""
433 );
434 }
435
436 #[test]
437 fn deserialize_hash() {
438 let bytes = [
440 88, 32, 177, 119, 236, 27, 242, 109, 251, 59, 112, 16, 212, 115, 230, 212, 71, 19, 178,
441 155, 118, 91, 153, 198, 230, 14, 203, 250, 231, 66, 222, 73, 101, 67,
442 ];
443 let hash: Hash = ciborium::de::from_reader(&bytes[..]).unwrap();
444 assert_eq!(hash, Hash::digest([1, 2, 3]));
445
446 let json = "\"b177ec1bf26dfb3b7010d473e6d44713b29b765b99c6e60ecbfae742de496543\"";
448 let hash: Hash = serde_json::from_str(json).unwrap();
449 assert_eq!(hash, Hash::digest([1, 2, 3]));
450 }
451
452 #[test]
453 fn serialize_verifying_key() {
454 let mut bytes: Vec<u8> = Vec::new();
456 let verifying_key = VerifyingKey::from_bytes(&[
457 215, 90, 152, 1, 130, 177, 10, 183, 213, 75, 254, 211, 201, 100, 7, 58, 14, 225, 114,
458 243, 218, 166, 35, 37, 175, 2, 26, 104, 247, 7, 81, 26,
459 ])
460 .unwrap();
461 ciborium::ser::into_writer(&verifying_key, &mut bytes).unwrap();
462 assert_eq!(
463 bytes,
464 vec![
465 88, 32, 215, 90, 152, 1, 130, 177, 10, 183, 213, 75, 254, 211, 201, 100, 7, 58, 14,
466 225, 114, 243, 218, 166, 35, 37, 175, 2, 26, 104, 247, 7, 81, 26,
467 ]
468 );
469
470 let json = serde_json::to_string(&verifying_key).unwrap();
472 assert_eq!(
473 json,
474 "\"d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a\""
475 );
476 }
477
478 fn assert_serde_roundtrip<
479 E: Clone + std::fmt::Debug + PartialEq + Serialize + DeserializeOwned,
480 >(
481 mut header: Header<E>,
482 signing_key: &SigningKey,
483 ) {
484 header.sign(signing_key);
485
486 let mut bytes = Vec::new();
487 ciborium::ser::into_writer(&header, &mut bytes).unwrap();
488 let header_again: Header<E> = ciborium::de::from_reader(&bytes[..]).unwrap();
489 assert_eq!(header, header_again);
490 }
491
492 #[test]
493 fn serde_roundtrip_operations() {
494 #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
495 struct CustomExtensions {
496 custom_field: u64,
497 }
498
499 let extensions = CustomExtensions { custom_field: 12 };
500 let signing_key = SigningKey::generate();
501
502 assert_serde_roundtrip(
503 Header::<CustomExtensions> {
504 version: 1,
505 verifying_key: signing_key.verifying_key(),
506 payload_size: 123,
507 payload_hash: Some(Hash::digest(vec![1, 2, 3])),
508 seq_num: 0,
509 backlink: None,
510 extensions: extensions.clone(),
511 signature: None,
512 },
513 &signing_key,
514 );
515
516 assert_serde_roundtrip(
517 Header::<CustomExtensions> {
518 version: 1,
519 verifying_key: signing_key.verifying_key(),
520 payload_size: 0,
521 payload_hash: None,
522 seq_num: 0,
523 backlink: None,
524 extensions: extensions,
525 signature: None,
526 },
527 &signing_key,
528 );
529 }
530
531 #[test]
532 fn expected_de_error() {
533 let signing_key = SigningKey::generate();
534
535 let mut header = Header::<()> {
537 version: 1,
538 verifying_key: signing_key.verifying_key(),
539 signature: None,
540 payload_size: 2829099,
541 payload_hash: None,
542 seq_num: 0,
543 backlink: None,
544 extensions: (),
545 };
546 header.sign(&signing_key);
547
548 let result = ciborium::de::from_reader::<Header<()>, _>(&header.to_bytes()[..]);
549 assert!(result.is_err());
550
551 let mut header = Header::<()> {
553 version: 1,
554 verifying_key: signing_key.verifying_key(),
555 signature: None,
556 payload_size: 0,
557 payload_hash: Some(Hash::digest([0, 1, 2])),
558 seq_num: 0,
559 backlink: None,
560 extensions: (),
561 };
562 header.sign(&signing_key);
563
564 let result = ciborium::de::from_reader::<Header<()>, _>(&header.to_bytes()[..]);
565 assert!(result.is_err());
566
567 let mut header = Header::<()> {
569 version: 1,
570 verifying_key: signing_key.verifying_key(),
571 signature: None,
572 payload_size: 0,
573 payload_hash: None,
574 seq_num: 0,
575 backlink: Some(Hash::digest([0, 1, 2])),
576 extensions: (),
577 };
578 header.sign(&signing_key);
579
580 let result = ciborium::de::from_reader::<Header<()>, _>(&header.to_bytes()[..]);
581 assert!(result.is_err());
582
583 let mut header = Header::<()> {
585 version: 1,
586 verifying_key: signing_key.verifying_key(),
587 signature: None,
588 payload_size: 0,
589 payload_hash: None,
590 seq_num: 10,
591 backlink: None,
592 extensions: (),
593 };
594 header.sign(&signing_key);
595
596 let result = ciborium::de::from_reader::<Header<()>, _>(&header.to_bytes()[..]);
597 assert!(result.is_err());
598 }
599
600 #[test]
601 fn serde_header_with_other_types() {
602 let signing_key = SigningKey::generate();
603
604 #[derive(Debug, PartialEq, Serialize, Deserialize)]
605 struct Message {
606 header: Header<()>,
607 body: Body,
608 }
609
610 let body = Body::new(b"hello");
611 let mut header = Header::<()> {
612 version: 1,
613 verifying_key: signing_key.verifying_key(),
614 signature: None,
615 payload_size: body.size(),
616 payload_hash: Some(body.hash()),
617 seq_num: 0,
618 backlink: None,
619 extensions: (),
620 };
621 header.sign(&signing_key);
622
623 let message = Message { header, body };
624
625 let mut bytes = Vec::new();
626 ciborium::ser::into_writer(&message, &mut bytes).unwrap();
627
628 let message_again: Message = ciborium::de::from_reader(&bytes[..]).unwrap();
629 assert_eq!(message_again, message);
630 }
631
632 #[test]
633 fn fixtures() {
634 let signing_key = SigningKey::from_bytes(&[
635 244, 123, 85, 215, 161, 204, 94, 227, 239, 253, 128, 164, 228, 160, 195, 49, 18, 49,
636 125, 4, 50, 218, 157, 230, 174, 1, 154, 231, 231, 142, 22, 170,
637 ]);
638
639 let mut header = Header::<()> {
641 version: 1,
642 verifying_key: signing_key.verifying_key(),
643 signature: None,
644 payload_size: 0,
645 payload_hash: None,
646 seq_num: 0,
647 backlink: None,
648 extensions: (),
649 };
650 header.sign(&signing_key);
651
652 let bytes = [
653 133, 1, 88, 32, 228, 21, 196, 25, 12, 199, 241, 100, 122, 89, 46, 191, 142, 95, 144,
654 92, 42, 222, 249, 148, 139, 23, 91, 43, 92, 17, 225, 69, 17, 181, 22, 32, 88, 64, 17,
655 129, 90, 32, 212, 224, 74, 141, 219, 82, 160, 35, 19, 205, 82, 55, 247, 204, 121, 153,
656 128, 203, 123, 102, 108, 90, 60, 23, 223, 176, 251, 154, 243, 131, 177, 54, 142, 210,
657 0, 231, 125, 90, 206, 28, 240, 37, 179, 88, 200, 246, 185, 49, 246, 135, 242, 133, 128,
658 127, 22, 118, 23, 102, 22, 2, 0, 0,
659 ];
660
661 let header_again: Header<()> = ciborium::de::from_reader(&bytes[..]).unwrap();
662 assert_eq!(header, header_again);
663
664 let body = Body::new("Hello, Sloth!".as_bytes());
666 let mut header = Header::<()> {
667 version: 1,
668 verifying_key: signing_key.verifying_key(),
669 signature: None,
670 payload_size: body.size(),
671 payload_hash: Some(body.hash()),
672 seq_num: 0,
673 backlink: None,
674 extensions: (),
675 };
676 header.sign(&signing_key);
677
678 let bytes = [
679 134, 1, 88, 32, 228, 21, 196, 25, 12, 199, 241, 100, 122, 89, 46, 191, 142, 95, 144,
680 92, 42, 222, 249, 148, 139, 23, 91, 43, 92, 17, 225, 69, 17, 181, 22, 32, 88, 64, 187,
681 89, 157, 165, 197, 22, 79, 145, 227, 116, 226, 203, 231, 213, 225, 253, 197, 253, 240,
682 147, 16, 224, 186, 146, 94, 126, 79, 185, 150, 84, 102, 16, 109, 56, 241, 228, 164,
683 191, 153, 47, 142, 189, 12, 71, 159, 143, 81, 204, 108, 124, 22, 39, 222, 122, 88, 198,
684 123, 125, 2, 211, 28, 196, 90, 0, 13, 88, 32, 191, 127, 68, 13, 227, 43, 252, 155, 49,
685 148, 176, 2, 162, 217, 175, 171, 49, 44, 181, 215, 71, 113, 211, 195, 29, 128, 192,
686 169, 5, 138, 160, 142, 0,
687 ];
688
689 let header_again: Header<()> = ciborium::de::from_reader(&bytes[..]).unwrap();
690 assert_eq!(header, header_again);
691
692 let mut header = Header::<()> {
694 version: 1,
695 verifying_key: signing_key.verifying_key(),
696 signature: None,
697 payload_size: 0,
698 payload_hash: None,
699 seq_num: 1,
700 backlink: Some(header.hash()),
701 extensions: (),
702 };
703 header.sign(&signing_key);
704
705 let bytes = [
706 134, 1, 88, 32, 228, 21, 196, 25, 12, 199, 241, 100, 122, 89, 46, 191, 142, 95, 144,
707 92, 42, 222, 249, 148, 139, 23, 91, 43, 92, 17, 225, 69, 17, 181, 22, 32, 88, 64, 90,
708 241, 219, 179, 113, 96, 207, 245, 193, 3, 115, 166, 84, 177, 236, 191, 194, 134, 34,
709 214, 117, 182, 130, 121, 97, 9, 110, 170, 35, 44, 155, 205, 147, 180, 234, 188, 17, 39,
710 109, 146, 142, 68, 181, 186, 119, 197, 71, 45, 245, 246, 32, 139, 46, 197, 150, 12,
711 255, 110, 134, 99, 5, 139, 223, 13, 0, 1, 88, 32, 68, 43, 250, 251, 47, 151, 121, 58,
712 30, 144, 24, 129, 171, 35, 89, 56, 161, 112, 75, 91, 168, 201, 195, 121, 169, 155, 85,
713 104, 129, 60, 141, 161,
714 ];
715
716 let header_again: Header<()> = ciborium::de::from_reader(&bytes[..]).unwrap();
717 assert_eq!(header, header_again);
718 }
719
720 #[test]
721 fn decode_non_map_extensions() {
722 let signing_key = SigningKey::generate();
723
724 let mut header = Header::<()> {
725 version: 1,
726 verifying_key: signing_key.verifying_key(),
727 signature: None,
728 payload_size: 0,
729 payload_hash: None,
730 seq_num: 0,
731 backlink: None,
732 extensions: (),
733 };
734 header.sign(&signing_key);
735
736 let result = ciborium::de::from_reader::<Header<()>, _>(&header.to_bytes()[..]);
737 assert!(result.is_ok());
738 }
739
740 #[test]
741 fn unexpected_eof_when_incomplete() {
742 let incomplete = [
745 137, 1, 88, 32, 228, 21, 196, 25, 12, 199, 241, 100, 122, 89, 46, 191, 142, 95, 144,
746 ];
747
748 let result: Result<Header<()>, _> = ciborium::de::from_reader(&incomplete[..]);
749 std::assert_matches!(result, Err(ciborium::de::Error::Io(_)));
750 }
751
752 #[test]
753 fn zero_sized_extensions() {
754 #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
755 struct Zilch;
756
757 #[derive(Clone, Debug, Serialize, Deserialize)]
758 struct ZeroSizedExtension {
759 field_a: [u8; 0],
760 field_b: (),
761 field_c: Zilch,
762 }
763
764 let signing_key = SigningKey::generate();
765 let body = Body::new(b"look, no bytes!");
766
767 let mut header = Header::<ZeroSizedExtension> {
768 version: 1,
769 verifying_key: signing_key.verifying_key(),
770 signature: None,
771 payload_size: body.size(),
772 payload_hash: Some(body.hash()),
773 seq_num: 0,
774 backlink: None,
775 extensions: ZeroSizedExtension {
776 field_a: [],
777 field_b: (),
778 field_c: Zilch,
779 },
780 };
781 header.sign(&signing_key);
782
783 let bytes = header.to_bytes();
784
785 assert!(bytes[0] == 134);
790
791 let result: Header<ZeroSizedExtension> = ciborium::de::from_reader(&bytes[..]).unwrap();
793 assert_eq!(result.extensions.field_a.len(), 0);
794 assert_eq!(result.extensions.field_b, ());
795 assert_eq!(result.extensions.field_c, Zilch);
796 }
797}