Skip to main content

amaru_kernel/utils/cbor/
serialised_as_millis.rs

1// Copyright 2026 PRAGMA
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use std::time::Duration;
16
17use num::BigUint;
18
19use crate::cbor;
20
21/// A newtype wrapper meant to facilitate encoding of time::Duration as integers with millis
22/// precision. This may seem odd, but is necessary to mimicks the encoding behavior of *some*
23/// Haskell types such as 'SlotLength'.
24///
25/// Note that there is a loss of precision coming from this type when durations are below
26/// milliseconds. In practice, this type is used to represent seconds or tenth of seconds.
27#[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}