1use core::fmt;
9use iref::UriBuf;
10use serde::{Deserialize, Serialize};
11use std::{hash::Hash, str::FromStr, time::Duration};
12
13use crate::{Overflow, StatusMap, StatusSizeError};
14
15mod syntax;
16pub use syntax::*;
17
18#[derive(Clone, Debug, Serialize, Deserialize)]
19pub struct StatusMessage {
20 #[serde(with = "prefixed_hexadecimal")]
21 pub status: u8,
22 pub message: String,
23}
24
25impl StatusMessage {
26 pub fn new(status: u8, message: String) -> Self {
27 Self { status, message }
28 }
29}
30
31#[derive(Debug, thiserror::Error)]
32#[error("invalid status size `{0}`")]
33pub struct InvalidStatusSize(u8);
34
35impl From<InvalidStatusSize> for StatusSizeError {
36 fn from(_value: InvalidStatusSize) -> Self {
37 Self::Invalid
38 }
39}
40
41#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize)]
42pub struct StatusSize(u8);
43
44impl TryFrom<u8> for StatusSize {
45 type Error = InvalidStatusSize;
46
47 fn try_from(value: u8) -> Result<Self, Self::Error> {
48 if (1..=8).contains(&value) {
49 Ok(Self(value))
50 } else {
51 Err(InvalidStatusSize(value))
52 }
53 }
54}
55
56impl From<StatusSize> for u8 {
57 fn from(value: StatusSize) -> Self {
58 value.0
59 }
60}
61
62impl Default for StatusSize {
63 fn default() -> Self {
64 Self::DEFAULT
65 }
66}
67
68impl StatusSize {
69 pub const DEFAULT: Self = Self(1);
70
71 pub fn is_default(&self) -> bool {
72 *self == Self::DEFAULT
73 }
74
75 fn offset_of(&self, index: usize) -> Offset {
76 let bit_offset = self.0 as usize * index;
77 Offset {
78 byte: bit_offset / 8,
79 bit: bit_offset % 8,
80 }
81 }
82
83 fn last_of(&self, index: usize) -> Offset {
84 let bit_offset = self.0 as usize * index + self.0 as usize - 1;
85 Offset {
86 byte: bit_offset / 8,
87 bit: bit_offset % 8,
88 }
89 }
90
91 fn mask(&self) -> u8 {
92 if self.0 == 8 {
93 0xff
94 } else {
95 (1 << self.0) - 1
96 }
97 }
98}
99
100impl<'de> Deserialize<'de> for StatusSize {
101 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
102 where
103 D: serde::Deserializer<'de>,
104 {
105 u8::deserialize(deserializer)?
106 .try_into()
107 .map_err(serde::de::Error::custom)
108 }
109}
110
111#[derive(Debug)]
112struct Offset {
113 byte: usize,
114 bit: usize,
115}
116
117impl Offset {
118 fn left_shift(&self, status_size: StatusSize) -> (i32, Option<u32>) {
119 let high = (8 - status_size.0 as isize - self.bit as isize) as i32;
120 let low = if high < 0 {
121 Some((8 + high) as u32)
122 } else {
123 None
124 };
125
126 (high, low)
127 }
128}
129
130#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
135#[serde(transparent)]
136pub struct TimeToLive(pub u64);
137
138impl Default for TimeToLive {
139 fn default() -> Self {
140 Self::DEFAULT
141 }
142}
143
144impl TimeToLive {
145 pub const DEFAULT: Self = Self(300000);
146
147 pub fn is_default(&self) -> bool {
148 *self == Self::DEFAULT
149 }
150}
151
152impl From<TimeToLive> for Duration {
153 fn from(value: TimeToLive) -> Self {
154 Duration::from_millis(value.0)
155 }
156}
157
158#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
159#[serde(rename_all = "camelCase")]
160pub enum StatusPurpose {
161 Revocation,
165
166 Suspension,
170
171 Message,
177}
178
179impl StatusPurpose {
180 pub fn from_name(name: &str) -> Option<Self> {
182 match name {
183 "revocation" => Some(Self::Revocation),
184 "suspension" => Some(Self::Suspension),
185 "message" => Some(Self::Message),
186 _ => None,
187 }
188 }
189
190 pub fn name(&self) -> &'static str {
192 match self {
193 Self::Revocation => "revocation",
194 Self::Suspension => "suspension",
195 Self::Message => "message",
196 }
197 }
198
199 pub fn as_str(&self) -> &'static str {
203 self.name()
204 }
205
206 pub fn into_name(self) -> &'static str {
210 self.name()
211 }
212
213 pub fn into_str(self) -> &'static str {
217 self.name()
218 }
219}
220
221impl<'a> From<&'a StatusPurpose> for crate::StatusPurpose<&'a str> {
222 fn from(value: &'a StatusPurpose) -> Self {
223 match value {
224 StatusPurpose::Revocation => Self::Revocation,
225 StatusPurpose::Suspension => Self::Suspension,
226 StatusPurpose::Message => Self::Other("message"),
227 }
228 }
229}
230
231impl<'a> PartialEq<crate::StatusPurpose<&'a str>> for StatusPurpose {
232 fn eq(&self, other: &crate::StatusPurpose<&'a str>) -> bool {
233 matches!(
234 (self, other),
235 (Self::Revocation, crate::StatusPurpose::Revocation)
236 | (Self::Suspension, crate::StatusPurpose::Suspension)
237 | (Self::Message, crate::StatusPurpose::Other("message"))
238 )
239 }
240}
241
242impl fmt::Display for StatusPurpose {
243 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
244 self.name().fmt(f)
245 }
246}
247
248#[derive(Debug, Clone, thiserror::Error)]
250#[error("invalid status purpose: {0}")]
251pub struct InvalidStatusPurpose(pub String);
252
253impl FromStr for StatusPurpose {
254 type Err = InvalidStatusPurpose;
255
256 fn from_str(s: &str) -> Result<Self, Self::Err> {
257 Self::from_name(s).ok_or_else(|| InvalidStatusPurpose(s.to_owned()))
258 }
259}
260
261#[derive(Debug, Clone)]
268pub struct SizedBitString {
269 inner: BitString,
270 status_size: StatusSize,
271 len: usize,
272}
273
274impl SizedBitString {
275 pub fn new(status_size: StatusSize) -> Self {
277 Self {
278 inner: BitString::new(),
279 status_size,
280 len: 0,
281 }
282 }
283
284 pub fn new_with(
289 status_size: StatusSize,
290 len: usize,
291 mut f: impl FnMut(usize) -> u8,
292 ) -> Result<Self, Overflow> {
293 let mut result = Self::with_capacity(status_size, len);
294
295 for i in 0..len {
296 result.push(f(i))?;
297 }
298
299 Ok(result)
300 }
301
302 pub fn new_with_value(
305 status_size: StatusSize,
306 len: usize,
307 value: u8,
308 ) -> Result<Self, Overflow> {
309 Self::new_with(status_size, len, |_| value)
310 }
311
312 pub fn new_zeroed(status_size: StatusSize, len: usize) -> Self {
315 Self::new_with_value(status_size, len, 0).unwrap() }
317
318 pub fn with_capacity(status_size: StatusSize, capacity: usize) -> Self {
321 Self {
322 inner: BitString::with_capacity(status_size, capacity),
323 status_size,
324 len: 0,
325 }
326 }
327
328 pub fn from_bytes(status_size: StatusSize, bytes: Vec<u8>) -> Self {
330 let len = bytes.len() * 8usize / status_size.0 as usize;
331 Self {
332 inner: BitString::from_bytes(bytes),
333 status_size,
334 len,
335 }
336 }
337
338 pub fn status_size(&self) -> StatusSize {
339 self.status_size
340 }
341
342 pub fn is_empty(&self) -> bool {
344 self.len == 0
345 }
346
347 pub fn len(&self) -> usize {
349 self.len
350 }
351
352 pub fn get(&self, index: usize) -> Option<u8> {
354 if index >= self.len {
355 return None;
356 }
357
358 self.inner.get(self.status_size, index)
359 }
360
361 pub fn push(&mut self, value: u8) -> Result<usize, Overflow> {
366 let masked_value = value & self.status_size.mask();
367 if masked_value != value {
368 return Err(Overflow::Value(value));
369 }
370
371 let index = self.len;
372 let offset = self.status_size.offset_of(index);
373
374 let (high_shift, low_shift) = offset.left_shift(self.status_size);
375
376 if offset.byte == self.inner.0.len() {
377 self.inner
378 .0
379 .push(masked_value.overflowing_signed_shl(high_shift).0);
380 } else {
381 self.inner.0[offset.byte] |= masked_value.overflowing_signed_shl(high_shift).0
382 }
383
384 if let Some(low_shift) = low_shift {
385 self.inner.0.push(masked_value.overflowing_shl(low_shift).0);
386 }
387
388 self.len += 1;
389 Ok(index)
390 }
391
392 pub fn set(&mut self, index: usize, value: u8) -> Result<u8, Overflow> {
397 if index >= self.len {
398 return Err(Overflow::Index(index));
399 }
400
401 self.inner.set(self.status_size, index, value)
402 }
403
404 pub fn iter(&self) -> BitStringIter {
406 self.inner.iter(self.status_size)
407 }
408
409 pub fn encode(&self) -> EncodedList {
411 self.inner.encode()
412 }
413
414 pub fn into_unsized(self) -> BitString {
415 self.inner
416 }
417
418 pub fn into_bytes(self) -> Vec<u8> {
420 self.inner.into_bytes()
421 }
422}
423
424impl From<SizedBitString> for Vec<u8> {
425 fn from(value: SizedBitString) -> Self {
426 value.into_bytes()
427 }
428}
429
430#[derive(Debug, Default, Clone)]
444pub struct BitString(Vec<u8>);
445
446impl BitString {
447 pub fn new() -> Self {
449 Self::default()
450 }
451
452 pub fn new_with(
457 status_size: StatusSize,
458 len: usize,
459 f: impl FnMut(usize) -> u8,
460 ) -> Result<Self, Overflow> {
461 SizedBitString::new_with(status_size, len, f).map(SizedBitString::into_unsized)
462 }
463
464 pub fn new_with_value(
467 status_size: StatusSize,
468 len: usize,
469 value: u8,
470 ) -> Result<Self, Overflow> {
471 Self::new_with(status_size, len, |_| value)
472 }
473
474 pub fn new_zeroed(status_size: StatusSize, len: usize) -> Self {
477 Self::new_with_value(status_size, len, 0).unwrap() }
479
480 pub fn with_capacity(status_size: StatusSize, capacity: usize) -> Self {
483 Self(Vec::with_capacity(
484 (capacity * status_size.0 as usize).div_ceil(8),
485 ))
486 }
487
488 pub fn from_bytes(bytes: Vec<u8>) -> Self {
490 Self(bytes)
491 }
492
493 pub fn get(&self, status_size: StatusSize, index: usize) -> Option<u8> {
495 if status_size.last_of(index).byte >= self.0.len() {
496 return None;
497 }
498
499 let offset = status_size.offset_of(index);
500 let (high_shift, low_shift) = offset.left_shift(status_size);
501
502 Some(self.get_at(status_size, offset.byte, high_shift, low_shift))
503 }
504
505 fn get_at(
506 &self,
507 status_size: StatusSize,
508 byte_offset: usize,
509 high_shift: i32,
510 low_shift: Option<u32>,
511 ) -> u8 {
512 let high = self
513 .0
514 .get(byte_offset)
515 .unwrap()
516 .overflowing_signed_shr(high_shift)
517 .0;
518
519 let low = match low_shift {
520 Some(low_shift) => {
521 self.0
522 .get(byte_offset + 1)
523 .unwrap()
524 .overflowing_shr(low_shift)
525 .0
526 }
527 None => 0,
528 };
529
530 (high | low) & status_size.mask()
531 }
532
533 pub fn set(
538 &mut self,
539 status_size: StatusSize,
540 index: usize,
541 value: u8,
542 ) -> Result<u8, Overflow> {
543 if status_size.last_of(index).byte >= self.0.len() {
544 return Err(Overflow::Index(index));
545 }
546
547 let mask = status_size.mask();
548 let masked_value = value & mask;
549 if masked_value != value {
550 return Err(Overflow::Value(value));
551 }
552
553 let offset = status_size.offset_of(index);
554 let (high_shift, low_shift) = offset.left_shift(status_size);
555
556 let old_value = self.get_at(status_size, offset.byte, high_shift, low_shift);
557
558 self.0[offset.byte] &= !mask.overflowing_signed_shl(high_shift).0; self.0[offset.byte] |= masked_value.overflowing_signed_shl(high_shift).0; if let Some(low_shift) = low_shift {
561 self.0[offset.byte + 1] &= !mask.overflowing_shl(low_shift).0; self.0[offset.byte + 1] |= masked_value.overflowing_shl(low_shift).0;
563 }
565
566 Ok(old_value)
567 }
568
569 pub fn iter(&self, status_size: StatusSize) -> BitStringIter {
571 BitStringIter {
572 bit_string: self,
573 status_size,
574 index: 0,
575 }
576 }
577
578 pub fn encode(&self) -> EncodedList {
580 EncodedList::encode(&self.0)
581 }
582
583 pub fn into_bytes(self) -> Vec<u8> {
585 self.0
586 }
587}
588
589impl From<BitString> for Vec<u8> {
590 fn from(value: BitString) -> Self {
591 value.into_bytes()
592 }
593}
594
595trait OverflowingSignedShift: Sized {
596 fn overflowing_signed_shl(self, shift: i32) -> (Self, bool);
597
598 fn overflowing_signed_shr(self, shift: i32) -> (Self, bool);
599}
600
601impl OverflowingSignedShift for u8 {
602 fn overflowing_signed_shl(self, shift: i32) -> (u8, bool) {
603 if shift < 0 {
604 self.overflowing_shr(shift.unsigned_abs())
605 } else {
606 self.overflowing_shl(shift.unsigned_abs())
607 }
608 }
609
610 fn overflowing_signed_shr(self, shift: i32) -> (u8, bool) {
611 if shift < 0 {
612 self.overflowing_shl(shift.unsigned_abs())
613 } else {
614 self.overflowing_shr(shift.unsigned_abs())
615 }
616 }
617}
618
619#[derive(Debug, Clone)]
625pub struct StatusList {
626 bit_string: BitString,
627 ttl: TimeToLive,
628}
629
630impl StatusList {
631 pub fn new(ttl: TimeToLive) -> Self {
632 Self {
633 bit_string: BitString::new(),
634 ttl,
635 }
636 }
637
638 pub fn from_bytes(bytes: impl Into<Vec<u8>>, ttl: TimeToLive) -> Self {
639 Self {
640 bit_string: BitString::from_bytes(bytes.into()),
641 ttl,
642 }
643 }
644
645 pub fn get(&self, status_size: StatusSize, index: usize) -> Option<u8> {
646 self.bit_string.get(status_size, index)
647 }
648
649 pub fn set(
650 &mut self,
651 status_size: StatusSize,
652 index: usize,
653 value: u8,
654 ) -> Result<u8, Overflow> {
655 self.bit_string.set(status_size, index, value)
656 }
657
658 pub fn iter(&self, status_size: StatusSize) -> BitStringIter {
659 self.bit_string.iter(status_size)
660 }
661
662 pub fn to_credential_subject(
663 &self,
664 id: Option<UriBuf>,
665 status_purpose: StatusPurpose,
666 ) -> BitstringStatusList {
667 BitstringStatusList::new(id, status_purpose, self.bit_string.encode(), self.ttl)
668 }
669}
670
671#[derive(Debug, Clone)]
678pub struct SizedStatusList {
679 bit_string: SizedBitString,
680 ttl: TimeToLive,
681}
682
683impl SizedStatusList {
684 pub fn new(status_size: StatusSize, ttl: TimeToLive) -> Self {
685 Self {
686 bit_string: SizedBitString::new(status_size),
687 ttl,
688 }
689 }
690
691 pub fn from_bytes(status_size: StatusSize, bytes: Vec<u8>, ttl: TimeToLive) -> Self {
692 Self {
693 bit_string: SizedBitString::from_bytes(status_size, bytes),
694 ttl,
695 }
696 }
697
698 pub fn get(&self, index: usize) -> Option<u8> {
699 self.bit_string.get(index)
700 }
701
702 pub fn set(&mut self, index: usize, value: u8) -> Result<u8, Overflow> {
703 self.bit_string.set(index, value)
704 }
705
706 pub fn push(&mut self, value: u8) -> Result<usize, Overflow> {
707 self.bit_string.push(value)
708 }
709
710 pub fn iter(&self) -> BitStringIter {
711 self.bit_string.iter()
712 }
713
714 pub fn to_credential_subject(
715 &self,
716 id: Option<UriBuf>,
717 status_purpose: StatusPurpose,
718 ) -> BitstringStatusList {
719 BitstringStatusList::new(id, status_purpose, self.bit_string.encode(), self.ttl)
720 }
721
722 pub fn into_unsized(self) -> StatusList {
723 StatusList {
724 bit_string: self.bit_string.into_unsized(),
725 ttl: self.ttl,
726 }
727 }
728}
729
730pub struct BitStringIter<'a> {
731 bit_string: &'a BitString,
732 status_size: StatusSize,
733 index: usize,
734}
735
736impl Iterator for BitStringIter<'_> {
737 type Item = u8;
738
739 fn next(&mut self) -> Option<Self::Item> {
740 self.bit_string
741 .get(self.status_size, self.index)
742 .inspect(|_| {
743 self.index += 1;
744 })
745 }
746}
747
748impl StatusMap for StatusList {
749 type Key = usize;
750 type StatusSize = StatusSize;
751 type Status = u8;
752
753 fn time_to_live(&self) -> Option<Duration> {
754 Some(self.ttl.into())
755 }
756
757 fn get_by_key(
758 &self,
759 status_size: Option<StatusSize>,
760 key: Self::Key,
761 ) -> Result<Option<u8>, StatusSizeError> {
762 Ok(self
763 .bit_string
764 .get(status_size.ok_or(StatusSizeError::Missing)?, key))
765 }
766}
767
768mod prefixed_hexadecimal {
769 use serde::{Deserialize, Deserializer, Serialize, Serializer};
770
771 pub fn serialize<S>(value: &u8, serializer: S) -> Result<S::Ok, S::Error>
772 where
773 S: Serializer,
774 {
775 format!("{value:#x}").serialize(serializer)
776 }
777
778 pub fn deserialize<'de, D>(deserializer: D) -> Result<u8, D::Error>
779 where
780 D: Deserializer<'de>,
781 {
782 let string = String::deserialize(deserializer)?;
783 let number = string
784 .strip_prefix("0x")
785 .ok_or_else(|| serde::de::Error::custom("missing `0x` prefix"))?;
786 u8::from_str_radix(number, 16).map_err(serde::de::Error::custom)
787 }
788}
789
790#[cfg(test)]
791mod tests {
792 use rand::{rngs::StdRng, RngCore, SeedableRng};
793
794 use crate::Overflow;
795
796 use super::{SizedBitString, StatusSize};
797
798 fn random_bit_string(
799 rng: &mut StdRng,
800 status_size: StatusSize,
801 len: usize,
802 ) -> (Vec<u8>, SizedBitString) {
803 let mut values = Vec::with_capacity(len);
804
805 for _ in 0..len {
806 values.push((rng.next_u32() & 0xff) as u8 & status_size.mask())
807 }
808
809 let mut bit_string = SizedBitString::new(status_size);
810 for &s in &values {
811 bit_string.push(s).unwrap();
812 }
813
814 (values, bit_string)
815 }
816
817 fn randomized_roundtrip(seed: u64, status_size: StatusSize, len: usize) {
818 let mut rng = StdRng::seed_from_u64(seed);
819 let (values, bit_string) = random_bit_string(&mut rng, status_size, len);
820
821 let encoded = bit_string.encode();
822 let decoded = SizedBitString::from_bytes(status_size, encoded.decode(None).unwrap());
823
824 assert!(decoded.len() >= len);
825
826 for (i, item) in values.into_iter().enumerate().take(len) {
827 assert_eq!(decoded.get(i), Some(item))
828 }
829 }
830
831 fn randomized_write(seed: u64, status_size: StatusSize, len: usize) {
832 let mut rng = StdRng::seed_from_u64(seed);
833 let (mut values, mut bit_string) = random_bit_string(&mut rng, status_size, len);
834
835 for _ in 0..len {
836 let i = (rng.next_u32() as usize) % len;
837 let value = (rng.next_u32() & 0xff) as u8 & status_size.mask();
838 bit_string.set(i, value).unwrap();
839 values[i] = value;
840 }
841
842 for (i, item) in values.into_iter().enumerate().take(len) {
843 assert_eq!(bit_string.get(i), Some(item))
844 }
845 }
846
847 #[test]
848 fn randomized_roundtrip_1bit() {
849 for i in 0..10 {
850 randomized_roundtrip(i, 1u8.try_into().unwrap(), 10);
851 }
852
853 for i in 0..10 {
854 randomized_roundtrip(i, 1u8.try_into().unwrap(), 100);
855 }
856
857 for i in 0..10 {
858 randomized_roundtrip(i, 1u8.try_into().unwrap(), 1000);
859 }
860 }
861
862 #[test]
863 fn randomized_write_1bits() {
864 for i in 0..10 {
865 randomized_write(i, 1u8.try_into().unwrap(), 10);
866 }
867
868 for i in 0..10 {
869 randomized_write(i, 1u8.try_into().unwrap(), 100);
870 }
871
872 for i in 0..10 {
873 randomized_write(i, 1u8.try_into().unwrap(), 1000);
874 }
875 }
876
877 #[test]
878 fn randomized_roundtrip_3bits() {
879 for i in 0..10 {
880 randomized_roundtrip(i, 3u8.try_into().unwrap(), 10);
881 }
882
883 for i in 0..10 {
884 randomized_roundtrip(i, 3u8.try_into().unwrap(), 100);
885 }
886
887 for i in 0..10 {
888 randomized_roundtrip(i, 3u8.try_into().unwrap(), 1000);
889 }
890 }
891
892 #[test]
893 fn randomized_write_3bits() {
894 for i in 0..10 {
895 randomized_write(i, 3u8.try_into().unwrap(), 10);
896 }
897
898 for i in 0..10 {
899 randomized_write(i, 3u8.try_into().unwrap(), 100);
900 }
901
902 for i in 0..10 {
903 randomized_write(i, 3u8.try_into().unwrap(), 1000);
904 }
905 }
906
907 #[test]
908 fn randomized_roundtrip_7bits() {
909 for i in 0..10 {
910 randomized_roundtrip(i, 7u8.try_into().unwrap(), 10);
911 }
912
913 for i in 0..10 {
914 randomized_roundtrip(i, 7u8.try_into().unwrap(), 100);
915 }
916
917 for i in 0..10 {
918 randomized_roundtrip(i, 7u8.try_into().unwrap(), 1000);
919 }
920 }
921
922 #[test]
923 fn randomized_write_7bits() {
924 for i in 0..10 {
925 randomized_write(i, 7u8.try_into().unwrap(), 10);
926 }
927
928 for i in 0..10 {
929 randomized_write(i, 7u8.try_into().unwrap(), 100);
930 }
931
932 for i in 0..10 {
933 randomized_write(i, 7u8.try_into().unwrap(), 1000);
934 }
935 }
936
937 #[test]
938 fn overflows() {
939 let mut rng = StdRng::seed_from_u64(0);
940 let (_, mut bitstring) = random_bit_string(&mut rng, 1u8.try_into().unwrap(), 15);
941
942 assert!(bitstring.get(15).is_none());
944
945 assert_eq!(bitstring.set(15, 0), Err(Overflow::Index(15)));
947
948 assert_eq!(bitstring.set(14, 2), Err(Overflow::Value(2)));
950 }
951
952 #[test]
953 fn deserialize_status_size_1() {
954 assert!(serde_json::from_str::<StatusSize>("1").is_ok())
955 }
956
957 #[test]
958 fn deserialize_status_size_2() {
959 assert!(serde_json::from_str::<StatusSize>("2").is_ok())
960 }
961
962 #[test]
963 fn deserialize_status_size_3() {
964 assert!(serde_json::from_str::<StatusSize>("3").is_ok())
965 }
966
967 #[test]
968 fn deserialize_status_size_negative() {
969 assert!(serde_json::from_str::<StatusSize>("-1").is_err())
970 }
971
972 #[test]
973 fn deserialize_status_size_overflow() {
974 assert!(serde_json::from_str::<StatusSize>("9").is_err())
975 }
976}