apache_avro/
duration.rs

1// Licensed to the Apache Software Foundation (ASF) under one
2// or more contributor license agreements.  See the NOTICE file
3// distributed with this work for additional information
4// regarding copyright ownership.  The ASF licenses this file
5// to you under the Apache License, Version 2.0 (the
6// "License"); you may not use this file except in compliance
7// with the License.  You may obtain a copy of the License at
8//
9//   http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing,
12// software distributed under the License is distributed on an
13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14// KIND, either express or implied.  See the License for the
15// specific language governing permissions and limitations
16// under the License.
17
18/// A struct representing duration that hides the details of endianness and conversion between
19/// platform-native u32 and byte arrays.
20use serde::{Deserialize, Serialize, de};
21
22#[derive(Debug, Copy, Clone, Eq, PartialEq)]
23pub struct Duration {
24    months: Months,
25    days: Days,
26    millis: Millis,
27}
28
29#[derive(Debug, Copy, Clone, Eq, PartialEq)]
30pub struct Months(u32);
31
32impl Months {
33    pub fn new(months: u32) -> Self {
34        Self(months)
35    }
36
37    fn as_bytes(&self) -> [u8; 4] {
38        self.0.to_le_bytes()
39    }
40}
41
42impl From<Months> for u32 {
43    fn from(days: Months) -> Self {
44        days.0
45    }
46}
47
48impl From<[u8; 4]> for Months {
49    fn from(bytes: [u8; 4]) -> Self {
50        Self(u32::from_le_bytes(bytes))
51    }
52}
53
54#[derive(Debug, Copy, Clone, Eq, PartialEq)]
55pub struct Days(u32);
56
57impl Days {
58    pub fn new(days: u32) -> Self {
59        Self(days)
60    }
61
62    fn as_bytes(&self) -> [u8; 4] {
63        self.0.to_le_bytes()
64    }
65}
66
67impl From<Days> for u32 {
68    fn from(days: Days) -> Self {
69        days.0
70    }
71}
72
73impl From<[u8; 4]> for Days {
74    fn from(bytes: [u8; 4]) -> Self {
75        Self(u32::from_le_bytes(bytes))
76    }
77}
78
79#[derive(Debug, Copy, Clone, Eq, PartialEq)]
80pub struct Millis(u32);
81
82impl Millis {
83    pub fn new(millis: u32) -> Self {
84        Self(millis)
85    }
86
87    fn as_bytes(&self) -> [u8; 4] {
88        self.0.to_le_bytes()
89    }
90}
91
92impl From<Millis> for u32 {
93    fn from(days: Millis) -> Self {
94        days.0
95    }
96}
97
98impl From<[u8; 4]> for Millis {
99    fn from(bytes: [u8; 4]) -> Self {
100        Self(u32::from_le_bytes(bytes))
101    }
102}
103
104impl Duration {
105    /// Construct a new `Duration`.
106    pub fn new(months: Months, days: Days, millis: Millis) -> Self {
107        Self {
108            months,
109            days,
110            millis,
111        }
112    }
113
114    /// Return the number of months in this duration.
115    pub fn months(&self) -> Months {
116        self.months
117    }
118
119    /// Return the number of days in this duration.
120    pub fn days(&self) -> Days {
121        self.days
122    }
123
124    /// Return the number of milliseconds in this duration.
125    pub fn millis(&self) -> Millis {
126        self.millis
127    }
128}
129
130impl From<&Duration> for [u8; 12] {
131    fn from(duration: &Duration) -> Self {
132        let mut bytes = [0u8; 12];
133        bytes[0..4].copy_from_slice(&duration.months.as_bytes());
134        bytes[4..8].copy_from_slice(&duration.days.as_bytes());
135        bytes[8..12].copy_from_slice(&duration.millis.as_bytes());
136        bytes
137    }
138}
139
140impl From<Duration> for [u8; 12] {
141    fn from(duration: Duration) -> Self {
142        (&duration).into()
143    }
144}
145
146impl From<&[u8; 12]> for Duration {
147    fn from(bytes: &[u8; 12]) -> Self {
148        Self {
149            months: Months::from([bytes[0], bytes[1], bytes[2], bytes[3]]),
150            days: Days::from([bytes[4], bytes[5], bytes[6], bytes[7]]),
151            millis: Millis::from([bytes[8], bytes[9], bytes[10], bytes[11]]),
152        }
153    }
154}
155
156impl From<[u8; 12]> for Duration {
157    fn from(bytes: [u8; 12]) -> Duration {
158        (&bytes).into()
159    }
160}
161
162impl Serialize for Duration {
163    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
164    where
165        S: serde::Serializer,
166    {
167        let value_bytes: [u8; 12] = self.into();
168        serializer.serialize_bytes(&value_bytes)
169    }
170}
171
172impl<'de> Deserialize<'de> for Duration {
173    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
174    where
175        D: serde::Deserializer<'de>,
176    {
177        struct DurationVisitor;
178
179        impl de::Visitor<'_> for DurationVisitor {
180            type Value = Duration;
181
182            fn expecting(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
183                write!(f, "a byte array with size 12")
184            }
185
186            fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E>
187            where
188                E: de::Error,
189            {
190                if v.len() != 12 {
191                    Err(E::custom(format!(
192                        "Expected byte array of length 12, but length is {}",
193                        v.len()
194                    )))
195                } else {
196                    let v_slice: [u8; 12] = v[..12].try_into().unwrap();
197                    Ok(Duration::from(v_slice))
198                }
199            }
200        }
201
202        deserializer.deserialize_bytes(DurationVisitor)
203    }
204}
205
206#[cfg(test)]
207mod tests {
208    use super::*;
209    use crate::types::Value;
210    use apache_avro_test_helper::TestResult;
211
212    #[test]
213    fn avro_rs_382_duration_from_value() -> TestResult {
214        let val = Value::Duration(Duration::new(Months::new(7), Days::new(4), Millis::new(45)));
215        let de_val: Duration = crate::from_value(&val)?;
216        assert_eq!(de_val.months(), Months::new(7));
217        assert_eq!(de_val.days(), Days::new(4));
218        assert_eq!(de_val.millis(), Millis::new(45));
219        Ok(())
220    }
221
222    #[test]
223    fn avro_rs_382_duration_to_value() -> TestResult {
224        let duration = Duration::new(Months::new(7), Days::new(4), Millis::new(45));
225        let ser_val = crate::to_value(duration)?;
226        match ser_val {
227            // Without a schema, we expect Duration to serialize to bytes
228            Value::Bytes(b) => {
229                assert_eq!(b, vec![7, 0, 0, 0, 4, 0, 0, 0, 45, 0, 0, 0]);
230            }
231            _ => {
232                Err(format!("Expected a Bytes value but got {ser_val:?}"))?;
233            }
234        }
235        Ok(())
236    }
237}