Skip to main content

update_package/
version.rs

1// Copyright 2020 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5//! Typesafe wrappers around reading the version file.
6
7use omaha_client::version::Version as SemanticVersion;
8use serde::de::{self, Visitor};
9use serde::{Deserialize, Deserializer, Serialize, Serializer};
10use std::convert::Infallible;
11use std::fmt;
12use std::str::FromStr;
13
14struct SystemVersionVisitor;
15
16impl Visitor<'_> for SystemVersionVisitor {
17    type Value = SystemVersion;
18
19    fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
20        formatter.write_str("a string")
21    }
22
23    fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
24    where
25        E: de::Error,
26    {
27        Ok(SystemVersion::from_str(v).unwrap())
28    }
29}
30
31#[derive(Clone, Debug, Eq, PartialEq)]
32/// Represents the version of an update package.
33pub enum SystemVersion {
34    /// An unrecognised version format.
35    Opaque(String),
36    /// A version of the format "a.b.c.d".
37    Semantic(SemanticVersion),
38}
39
40impl SystemVersion {
41    /// Returns true if this SystemVersion is an empty string.
42    pub fn is_empty(&self) -> bool {
43        if let SystemVersion::Opaque(value) = self { value.is_empty() } else { false }
44    }
45}
46
47impl PartialOrd for SystemVersion {
48    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
49        let my_version = match &self {
50            SystemVersion::Opaque(_) => return None,
51            SystemVersion::Semantic(ver) => ver,
52        };
53
54        let other_version = match other {
55            SystemVersion::Opaque(_) => return None,
56            SystemVersion::Semantic(ver) => ver,
57        };
58
59        my_version.partial_cmp(other_version)
60    }
61}
62
63// We have manual implementations of deserialize/serialize so that we can parse simple strings.
64impl<'de> Deserialize<'de> for SystemVersion {
65    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
66    where
67        D: Deserializer<'de>,
68    {
69        deserializer.deserialize_str(SystemVersionVisitor)
70    }
71}
72
73impl Serialize for SystemVersion {
74    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
75    where
76        S: Serializer,
77    {
78        match self {
79            SystemVersion::Semantic(version) => serializer.serialize_str(&version.to_string()),
80            SystemVersion::Opaque(string) => serializer.serialize_str(string),
81        }
82    }
83}
84
85impl FromStr for SystemVersion {
86    type Err = Infallible;
87
88    fn from_str(s: &str) -> Result<Self, Self::Err> {
89        let result = match SemanticVersion::from_str(s) {
90            Ok(version) => SystemVersion::Semantic(version),
91            Err(_) => SystemVersion::Opaque(s.trim_end().to_owned()),
92        };
93
94        Ok(result)
95    }
96}
97
98impl fmt::Display for SystemVersion {
99    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
100        match self {
101            SystemVersion::Opaque(string) => f.write_str(string),
102            SystemVersion::Semantic(version) => version.fmt(f),
103        }
104    }
105}
106
107#[cfg(test)]
108#[allow(clippy::bool_assert_comparison)]
109mod tests {
110    use super::*;
111
112    #[test]
113    fn test_deserialize_version() {
114        let version: SystemVersion =
115            serde_json::from_str(r#""not a real version number""#).unwrap();
116        assert_eq!(version, SystemVersion::Opaque("not a real version number".to_owned()));
117        let version: SystemVersion = serde_json::from_str(r#""1.2.3""#).unwrap();
118        assert_eq!(version, SystemVersion::Semantic(SemanticVersion::from([1, 2, 3])));
119    }
120
121    #[test]
122    fn test_serialize_version() {
123        let version = SystemVersion::Opaque("not a real version number".to_owned());
124        assert_eq!(
125            serde_json::to_string(&version).unwrap(),
126            r#""not a real version number""#.to_owned()
127        );
128        let version = SystemVersion::Semantic(SemanticVersion::from([1, 3, 4, 5]));
129        assert_eq!(serde_json::to_string(&version).unwrap(), r#""1.3.4.5""#.to_owned());
130    }
131
132    #[test]
133    fn test_version_order_both_opaque() {
134        // We don't attempt to assign ordering to opaque versions.
135        let a = SystemVersion::Opaque("version 1".to_string());
136        let b = SystemVersion::Opaque("another version".to_string());
137        assert_eq!(a < b, false);
138        assert_eq!(a > b, false);
139        assert_eq!(a >= b, false);
140        assert_eq!(a <= b, false);
141    }
142
143    #[test]
144    fn test_version_order_one_opaque() {
145        let a = SystemVersion::Opaque("opaque".to_string());
146        let b = SystemVersion::Semantic(SemanticVersion::from([1, 2, 3, 4]));
147        assert_eq!(a < b, false);
148        assert_eq!(a > b, false);
149        assert_eq!(a >= b, false);
150        assert_eq!(a <= b, false);
151        assert_eq!(b < a, false);
152        assert_eq!(b > a, false);
153        assert_eq!(b >= a, false);
154        assert_eq!(b <= a, false);
155    }
156
157    #[test]
158    fn test_version_order_both_semantic() {
159        let a = SystemVersion::Semantic(SemanticVersion::from([1, 2, 3, 4]));
160        let b = SystemVersion::Semantic(SemanticVersion::from([1, 2, 3, 5]));
161
162        assert_eq!(a < b, true);
163        assert_eq!(a <= b, true);
164        assert_eq!(a > b, false);
165        assert_eq!(a >= b, false);
166        assert_eq!(b < a, false);
167        assert_eq!(b <= a, false);
168        assert_eq!(b >= a, true);
169        assert_eq!(b > a, true);
170    }
171}