embedded_semver/
version.rs

1use bitvec::prelude::*;
2
3use crate::{helpers::num_as_bv, sizes, Error, Magic};
4
5/// Represents a version number conforming to the semantic versioning scheme
6///
7/// Conversions:
8/// * To an integer: [`Semver::to_i32`], [`Semver::to_u32`], [`Semver::to_i64`]
9///   and [`Semver::to_u64`]
10/// * From an integer: [`Semver::from_i32`], [`Semver::from_u32`], [`Semver::from_i64`]
11///   and [`Semver::from_u64`]
12///
13/// [Wikipedia](https://en.wikipedia.org/wiki/Software_versioning#Degree_of_compatibility)
14/// explains semantic versioning and the fields in detail.
15#[derive(Debug, PartialEq)]
16pub struct Semver {
17    /// The semantic versioning major version (high risk)
18    pub major: usize,
19    /// The semantic versioning minor version (medium risk)
20    pub minor: usize,
21    /// The semantic versioning patch version (lowest risk)
22    pub patch: usize,
23
24    /// Magic with what the Semver has been or will be packed
25    magic: Magic,
26}
27
28impl Semver {
29    /// Helper for constructing new instance
30    pub fn new(major: usize, minor: usize, patch: usize) -> Self {
31        Self {
32            major,
33            minor,
34            patch,
35            magic: Default::default(),
36        }
37    }
38
39    /// Construct from an i64
40    pub fn from_i64(n: i64) -> Result<Self, Error> {
41        let bytes = n.to_le_bytes();
42        let bv = bytes.view_bits::<Msb0>();
43        let sizes = sizes::size_iterator(&sizes::I64_SIZES);
44        Ok(Self::from_size_iterator(&bv, sizes)?)
45    }
46
47    /// Construct from an u64
48    pub fn from_u64(n: u64) -> Result<Self, Error> {
49        let i64 = i64::from_le_bytes(n.to_le_bytes());
50        Self::from_i64(i64)
51    }
52
53    /// Construct from an i32
54    pub fn from_i32(n: i32) -> Result<Self, Error> {
55        let bytes = n.to_le_bytes();
56        let bv = bytes.view_bits::<Msb0>();
57        let sizes = sizes::size_iterator(&sizes::I32_SIZES);
58        Ok(Self::from_size_iterator(bv, sizes)?)
59    }
60
61    /// Construct from an u32
62    pub fn from_u32(n: u32) -> Result<Self, Error> {
63        let i32 = i32::from_le_bytes(n.to_le_bytes());
64        Self::from_i32(i32)
65    }
66
67    /// Convert to an i32. Errs if any of the fields overflow
68    pub fn to_i32(&self) -> Result<i32, Error> {
69        let mut bv: BitArray<[u8; 4], Msb0> = BitArray::ZERO;
70        let sizes = sizes::size_iterator(&sizes::I32_SIZES);
71        self.append_with_size_iterator(&mut bv, sizes)?;
72        Ok(i32::from_le_bytes(bv.data))
73    }
74
75    /// Convert to an u32. Errs if any of the fields overflow
76    pub fn to_u32(&self) -> Result<u32, Error> {
77        let val = self.to_i32()?;
78        Ok(u32::from_le_bytes(val.to_le_bytes()))
79    }
80
81    /// Convert to an u64. Errs if any of the fields overflow
82    pub fn to_i64(&self) -> Result<i64, Error> {
83        let mut bv: BitArray<[u8; 8], Msb0> = BitArray::ZERO;
84        let sizes = sizes::size_iterator(&sizes::I64_SIZES);
85        self.append_with_size_iterator(&mut bv, sizes)?;
86        Ok(i64::from_le_bytes(bv.data))
87    }
88
89    /// Convert to an u64. Errs if any of the fields overflow
90    pub fn to_u64(&self) -> Result<u64, Error> {
91        let val = self.to_i64()?;
92        Ok(u64::from_le_bytes(val.to_le_bytes()))
93    }
94
95    fn from_size_iterator<const SIZE: usize>(
96        bv: &BitSlice<u8, Msb0>,
97        mut sizes: sizes::SizeIterator<SIZE>,
98    ) -> Result<Self, Error> {
99        Ok(Self {
100            magic: convert_api_version(bv[sizes.next().unwrap()].load::<u64>())?,
101            major: bv[sizes.next().unwrap()].load::<usize>(),
102            minor: bv[sizes.next().unwrap()].load::<usize>(),
103            patch: bv[sizes.next().unwrap()].load::<usize>(),
104        })
105    }
106
107    fn append_with_size_iterator<const SIZE: usize, const ITER_SIZE: usize>(
108        &self,
109        bv: &mut BitArray<[u8; SIZE], Msb0>,
110        mut sizes: sizes::SizeIterator<ITER_SIZE>,
111    ) -> Result<(), Error> {
112        num_as_bv(bv, &mut sizes, Magic::default() as u64)?; // for now, can be extended in future
113        num_as_bv(bv, &mut sizes, self.major as u64)?;
114        num_as_bv(bv, &mut sizes, self.minor as u64)?;
115        num_as_bv(bv, &mut sizes, self.patch as u64)?;
116        Ok(())
117    }
118}
119
120fn convert_api_version(n: u64) -> Result<Magic, Error> {
121    let api_version = match n {
122        0 => Magic::V0,
123        1 => Magic::V1,
124        2 => Magic::V2,
125        3 => Magic::V3,
126        _ => return Err(Error::UnknownMagic(n)),
127    };
128
129    match api_version {
130        Magic::V0 => (),
131        _ => return Err(Error::UnsupportedMagic(api_version)),
132    }
133
134    Ok(api_version)
135}
136
137#[cfg(test)]
138mod tests {
139    use super::*;
140
141    #[test]
142    fn test_roundtrips() {
143        assert_roundtrip_i32(Semver::new(254, 500, 498));
144        assert_roundtrip_i32(Semver::new(0, 0, 0));
145
146        assert_roundtrip_u32(Semver::new(254, 500, 498));
147        assert_roundtrip_u32(Semver::new(0, 0, 0));
148
149        assert_roundtrip_i64(Semver::new(65343, 64000, 65310));
150        assert_roundtrip_i64(Semver::new(0, 0, 0));
151
152        assert_roundtrip_u64(Semver::new(65343, 64000, 65310));
153        assert_roundtrip_u64(Semver::new(0, 0, 0));
154
155        let val = Semver::new(5, 230, 150).to_i32().unwrap() as u32;
156        let version = Semver::from_u32(val).unwrap();
157        assert_eq!(version, Semver::new(5, 230, 150));
158    }
159
160    #[test]
161    fn test_unsupported_api_version() {
162        let mut bv: BitArray<[u8; 4], Msb0> = BitArray::ZERO;
163        let mut iter = sizes::size_iterator(&sizes::I32_SIZES);
164        num_as_bv(&mut bv, &mut iter, Magic::V2 as u64).unwrap();
165        let val = i32::from_le_bytes(bv.data);
166
167        assert_eq!(
168            Semver::from_i32(val).unwrap_err(),
169            Error::UnsupportedMagic(Magic::V2)
170        );
171    }
172
173    #[test]
174    fn test_unknown_api_version() {
175        let mut bv: BitArray<[u8; 8], Msb0> = BitArray::ZERO;
176        let mut iter = sizes::size_iterator(&sizes::I64_SIZES);
177        num_as_bv(&mut bv, &mut iter, 13).unwrap();
178        let val = i64::from_le_bytes(bv.data);
179
180        assert_eq!(Semver::from_i64(val).unwrap_err(), Error::UnknownMagic(13));
181    }
182
183    #[test]
184    fn test_from_i32() {
185        assert_eq!(Semver::from_i32(16843009).unwrap(), test_version())
186    }
187
188    #[test]
189    fn test_from_u32() {
190        let u32 = u32::from_le_bytes(16843009i32.to_le_bytes());
191        assert_eq!(Semver::from_u32(u32).unwrap(), test_version())
192    }
193
194    #[test]
195    fn test_from_i64() {
196        assert_eq!(Semver::from_i64(21474902017).unwrap(), test_version())
197    }
198
199    #[test]
200    fn test_from_u64() {
201        let u64 = u64::from_le_bytes(21474902017i64.to_le_bytes());
202        assert_eq!(Semver::from_u64(u64).unwrap(), test_version())
203    }
204
205    #[test]
206    fn test_to_i32() {
207        let val = test_version().to_i32().unwrap();
208        assert_eq!(val, 16843009);
209        assert_eq!(&val.to_le_bytes()[..], &[0b1, 0b1, 0b1, 0b1]);
210    }
211
212    #[test]
213    fn test_to_i64() {
214        let val = test_version().to_i64().unwrap();
215        assert_eq!(
216            &val.to_le_bytes()[..],
217            &[0b1, 0b0, 0b1, 0b0, 0b101, 0b0, 0b0, 0b0]
218        );
219
220        assert_eq!(val, 21474902017);
221    }
222
223    #[test]
224    fn test_overflow_i32() {
225        assert!(Semver::new(2usize.pow(10), 2usize.pow(10), 2usize.pow(10))
226            .to_i32()
227            .is_ok());
228
229        // for overflows, see sizes::I32_SIZES
230        assert_eq!(
231            Semver::new(2usize.pow(10) + 1, 0, 0).to_i32().unwrap_err(),
232            Error::Overflow
233        );
234
235        assert_eq!(
236            Semver::new(0, 2usize.pow(10) + 1, 0).to_i32().unwrap_err(),
237            Error::Overflow
238        );
239
240        assert_eq!(
241            Semver::new(0, 0, 2usize.pow(10) + 1).to_i32().unwrap_err(),
242            Error::Overflow
243        );
244    }
245
246    #[test]
247    fn test_overflow_i64() {
248        assert!(Semver::new(2usize.pow(16), 2usize.pow(16), 2usize.pow(16))
249            .to_i64()
250            .is_ok());
251
252        // see sizes::I64_SIZES
253        let overflow = 2usize.pow(16) + 1;
254
255        assert_eq!(
256            Semver::new(overflow, 0, 0).to_i64().unwrap_err(),
257            Error::Overflow
258        );
259
260        assert_eq!(
261            Semver::new(0, overflow, 0).to_i64().unwrap_err(),
262            Error::Overflow
263        );
264
265        assert_eq!(
266            Semver::new(0, 0, overflow).to_i64().unwrap_err(),
267            Error::Overflow
268        );
269    }
270
271    fn test_version() -> Semver {
272        Semver {
273            major: 1,
274            minor: 1,
275            patch: 5,
276            magic: Magic::V0,
277        }
278    }
279
280    fn assert_roundtrip_i32(ver: Semver) {
281        assert_eq!(Semver::from_i32(ver.to_i32().unwrap()).unwrap(), ver);
282    }
283
284    fn assert_roundtrip_u32(ver: Semver) {
285        assert_eq!(Semver::from_u32(ver.to_u32().unwrap()).unwrap(), ver);
286    }
287
288    fn assert_roundtrip_i64(ver: Semver) {
289        assert_eq!(Semver::from_i64(ver.to_i64().unwrap()).unwrap(), ver);
290    }
291
292    fn assert_roundtrip_u64(ver: Semver) {
293        assert_eq!(Semver::from_u64(ver.to_u64().unwrap()).unwrap(), ver);
294    }
295}