massa_models/
version.rs

1// Copyright (c) 2022 MASSA LABS <info@massa.net>
2
3use crate::error::ModelsError;
4use massa_serialization::{Deserializer, Serializer, U32VarIntDeserializer, U32VarIntSerializer};
5use nom::bytes::complete::take;
6use nom::error::context;
7use nom::sequence::tuple;
8use nom::Parser;
9use nom::{
10    error::{ContextError, ParseError},
11    IResult,
12};
13use serde::de::Unexpected;
14use std::ops::Bound::Included;
15use std::{convert::TryInto, fmt, str::FromStr};
16
17const INSTANCE_LEN: usize = 4;
18
19/// Application version, checked during handshakes
20#[derive(Debug, Clone, Copy, PartialEq, Eq)]
21pub struct Version {
22    /// ASCII uppercase alpha
23    instance: [char; INSTANCE_LEN],
24    major: u32,
25    minor: u32,
26}
27
28struct VersionVisitor;
29
30impl<'de> serde::de::Visitor<'de> for VersionVisitor {
31    type Value = Version;
32
33    fn visit_str<E>(self, value: &str) -> Result<Version, E>
34    where
35        E: serde::de::Error,
36    {
37        Version::from_str(value).map_err(|_| E::invalid_value(Unexpected::Str(value), &self))
38    }
39
40    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
41        write!(
42            formatter,
43            "an Version type representing a version identifier"
44        )
45    }
46}
47
48impl<'de> serde::Deserialize<'de> for Version {
49    fn deserialize<D>(deserializer: D) -> Result<Version, D::Error>
50    where
51        D: serde::de::Deserializer<'de>,
52    {
53        deserializer.deserialize_str(VersionVisitor)
54    }
55}
56
57impl serde::Serialize for Version {
58    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
59    where
60        S: serde::Serializer,
61    {
62        serializer.serialize_str(&self.to_string())
63    }
64}
65
66/// Serializer for `Version`
67#[derive(Clone)]
68pub struct VersionSerializer {
69    u32_serializer: U32VarIntSerializer,
70}
71
72impl VersionSerializer {
73    /// Creates a `VersionSerializer`
74    pub const fn new() -> Self {
75        Self {
76            u32_serializer: U32VarIntSerializer::new(),
77        }
78    }
79}
80
81impl Default for VersionSerializer {
82    fn default() -> Self {
83        Self::new()
84    }
85}
86
87impl Serializer<Version> for VersionSerializer {
88    /// ```
89    /// use std::ops::Bound::Included;
90    /// use std::str::FromStr;
91    /// use massa_serialization::Serializer;
92    /// use massa_models::version::{Version, VersionSerializer};
93    ///
94    /// let version: Version = Version::from_str("TEST.1.10").unwrap();
95    /// let serializer = VersionSerializer::new();
96    /// let mut buffer = Vec::new();
97    /// serializer.serialize(&version, &mut buffer).unwrap();
98    /// ```
99    fn serialize(
100        &self,
101        value: &Version,
102        buffer: &mut Vec<u8>,
103    ) -> Result<(), massa_serialization::SerializeError> {
104        buffer.extend(value.instance.iter().map(|&c| c as u8));
105        self.u32_serializer.serialize(&value.major, buffer)?;
106        self.u32_serializer.serialize(&value.minor, buffer)?;
107        Ok(())
108    }
109}
110
111/// Serializer for `Version`
112#[derive(Clone)]
113pub struct VersionDeserializer {
114    u32_deserializer: U32VarIntDeserializer,
115}
116
117impl VersionDeserializer {
118    /// Creates a `VersionSerializer`
119    pub const fn new() -> Self {
120        Self {
121            u32_deserializer: U32VarIntDeserializer::new(Included(0), Included(u32::MAX)),
122        }
123    }
124}
125
126impl Default for VersionDeserializer {
127    fn default() -> Self {
128        Self::new()
129    }
130}
131
132impl Deserializer<Version> for VersionDeserializer {
133    /// ```
134    /// use std::ops::Bound::Included;
135    /// use std::str::FromStr;
136    /// use massa_serialization::{Serializer, Deserializer, DeserializeError};
137    /// use massa_models::version::{Version, VersionSerializer, VersionDeserializer};
138    ///
139    /// let version: Version = Version::from_str("TEST.1.10").unwrap();
140    /// let mut serialized = Vec::new();
141    /// let serializer = VersionSerializer::new();
142    /// let deserializer = VersionDeserializer::new();
143    /// serializer.serialize(&version, &mut serialized).unwrap();
144    /// let (rest, version_deser) = deserializer.deserialize::<DeserializeError>(&serialized).unwrap();
145    /// assert_eq!(rest.len(), 0);
146    /// assert_eq!(version, version_deser);
147    /// ```
148    fn deserialize<'a, E: ParseError<&'a [u8]> + ContextError<&'a [u8]>>(
149        &self,
150        buffer: &'a [u8],
151    ) -> IResult<&'a [u8], Version, E> {
152        context(
153            "Failed Version deserialization",
154            tuple((
155                context("Failed instance deserialization", |input: &'a [u8]| {
156                    let (rest, instance) = take(INSTANCE_LEN)(input)?;
157                    if instance.iter().any(|c| {
158                        !c.is_ascii() || !c.is_ascii_alphabetic() || !c.is_ascii_uppercase()
159                    }) {
160                        return Err(nom::Err::Error(ParseError::from_error_kind(
161                            input,
162                            nom::error::ErrorKind::Char,
163                        )));
164                    }
165                    // Safe: because `take` fail if there is not enough data
166                    let instance: [char; INSTANCE_LEN] = instance
167                        .iter()
168                        .map(|&c| char::from(c))
169                        .collect::<Vec<char>>()
170                        .try_into()
171                        .unwrap();
172                    Ok((rest, instance))
173                }),
174                context("Failed major deserialization", |input: &'a [u8]| {
175                    self.u32_deserializer.deserialize(input)
176                }),
177                context("Failed minor deserialization", |input: &'a [u8]| {
178                    self.u32_deserializer.deserialize(input)
179                }),
180            )),
181        )
182        .map(|(instance, major, minor)| Version {
183            instance,
184            major,
185            minor,
186        })
187        .parse(buffer)
188    }
189}
190
191impl Version {
192    /// true if instance and major are the same
193    pub fn is_compatible(&self, other: &Version) -> bool {
194        self.instance == other.instance
195            && (self.major == other.major
196                || (self.major == 2 && other.major == 3)
197                || (self.major == 3 && other.major == 2))
198    }
199}
200
201impl fmt::Display for Version {
202    /// ```rust
203    /// # use massa_models::*;
204    /// # use std::str::FromStr;
205    /// let v: version::Version = version::Version::from_str("TEST.1.10").unwrap();
206    /// assert_eq!(v.to_string(), "TEST.1.10");
207    /// ```
208    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
209        let network_str: String = self.instance.iter().cloned().collect();
210        write!(f, "{}.{}.{}", network_str, self.major, self.minor)
211    }
212}
213
214impl FromStr for Version {
215    type Err = ModelsError;
216
217    fn from_str(str_version: &str) -> Result<Self, Self::Err> {
218        let parts: Vec<String> = str_version.split('.').map(|v| v.to_string()).collect();
219        if parts.len() != 3 {
220            return Err(ModelsError::InvalidVersionError(
221                "version identifier is not in 3 parts separates by a dot".into(),
222            ));
223        }
224        if parts[0].len() != 4
225            || !parts[0].is_ascii()
226            || parts[0]
227                .chars()
228                .any(|c| !c.is_ascii_alphabetic() || !c.is_ascii_uppercase())
229        {
230            return Err(ModelsError::InvalidVersionError(
231                "version identifier instance part is not 4-char uppercase alphabetic ASCII".into(),
232            ));
233        }
234        let instance: [char; 4] = parts[0].chars().collect::<Vec<char>>().try_into().unwrap(); // will not panic as the length and type were checked above
235        let major: u32 = u32::from_str(&parts[1]).map_err(|_| {
236            ModelsError::InvalidVersionError("version identifier major number invalid".into())
237        })?;
238        let minor: u32 = u32::from_str(&parts[2]).map_err(|_| {
239            ModelsError::InvalidVersionError("version identifier minor number invalid".into())
240        })?;
241        Ok(Version {
242            instance,
243            major,
244            minor,
245        })
246    }
247}