amaru_kernel/utils/cbor/
serialised_as_millis.rs1use std::time::Duration;
16
17use num::BigUint;
18
19use crate::cbor;
20
21#[derive(Debug)]
28#[repr(transparent)]
29pub struct SerialisedAsMillis(Duration);
30
31impl From<SerialisedAsMillis> for Duration {
32 fn from(t: SerialisedAsMillis) -> Self {
33 t.0
34 }
35}
36
37impl From<Duration> for SerialisedAsMillis {
38 fn from(d: Duration) -> Self {
39 Self(d)
40 }
41}
42
43impl SerialisedAsMillis {
44 pub fn deserialize<'de, D>(deserializer: D) -> Result<Duration, D::Error>
45 where
46 D: serde::Deserializer<'de>,
47 {
48 Ok(Duration::from_millis(serde::Deserialize::deserialize(deserializer)?))
49 }
50
51 pub fn serialize<S>(duration: &Duration, serializer: S) -> Result<S::Ok, S::Error>
52 where
53 S: serde::Serializer,
54 {
55 serde::Serialize::serialize(&duration.as_millis(), serializer)
56 }
57}
58
59impl<C> cbor::Encode<C> for SerialisedAsMillis {
60 fn encode<W: cbor::encode::Write>(
61 &self,
62 e: &mut cbor::Encoder<W>,
63 ctx: &mut C,
64 ) -> Result<(), cbor::encode::Error<W::Error>> {
65 let ms = self.0.as_millis();
66 match u64::try_from(ms).ok() {
67 Some(t) => t.encode(e, ctx),
68 None => {
69 let bytes = BigUint::from(ms).to_bytes_be();
70 e.tag(cbor::IanaTag::PosBignum)?;
71 e.bytes(&bytes)?;
72 Ok(())
73 }
74 }
75 }
76}
77
78impl<'b, C> cbor::Decode<'b, C> for SerialisedAsMillis {
79 #[allow(clippy::wildcard_enum_match_arm)]
80 fn decode(d: &mut cbor::Decoder<'b>, _ctx: &mut C) -> Result<Self, cbor::decode::Error> {
81 use cbor::Type::*;
82 match d.datatype()? {
83 Tag => {
84 cbor::expect_tag(d, cbor::IanaTag::PosBignum)?;
85 let millis = BigUint::from_bytes_be(d.bytes()?);
86 match u128::try_from(millis) {
87 Ok(millis) => {
88 if let Some(nanos) = millis.checked_mul(1_000_000)
89 && nanos < (u64::MAX as u128) * 1_000_000_000
90 {
91 Ok(Self(Duration::from_nanos_u128(nanos)))
92 } else {
93 Err(cbor::decode::Error::message(format!(
94 "cannot convert to Duration, too large: {millis}ms"
95 )))
96 }
97 }
98 Err(millis) => Err(cbor::decode::Error::message(format!(
99 "cannot convert to Duration, too large: {}ms",
100 millis.into_original()
101 ))),
102 }
103 }
104 U64 | U32 | U16 | U8 => Ok(Self(Duration::from_millis(d.u64()?))),
105 t => Err(cbor::decode::Error::message(format!("Unhandled type decoding SerialisedAsMillis: {t}"))),
106 }
107 }
108}