update_package/
version.rs
1use fidl_fuchsia_io as fio;
8use omaha_client::version::Version as SemanticVersion;
9use serde::de::{self, Visitor};
10use serde::{Deserialize, Deserializer, Serialize, Serializer};
11use std::convert::Infallible;
12use std::fmt;
13use std::str::FromStr;
14use thiserror::Error;
15
16#[derive(Debug, Error)]
18#[allow(missing_docs)]
19pub enum ReadVersionError {
20 #[error("while opening the file")]
21 OpenFile(#[source] fuchsia_fs::node::OpenError),
22
23 #[error("while reading the file")]
24 ReadFile(#[source] fuchsia_fs::file::ReadError),
25}
26
27struct SystemVersionVisitor;
28
29impl Visitor<'_> for SystemVersionVisitor {
30 type Value = SystemVersion;
31
32 fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
33 formatter.write_str("a string")
34 }
35
36 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
37 where
38 E: de::Error,
39 {
40 Ok(SystemVersion::from_str(v).unwrap())
41 }
42}
43
44#[derive(Clone, Debug, Eq, PartialEq)]
45pub enum SystemVersion {
47 Opaque(String),
49 Semantic(SemanticVersion),
51}
52
53impl SystemVersion {
54 pub fn is_empty(&self) -> bool {
56 if let SystemVersion::Opaque(value) = self {
57 value.is_empty()
58 } else {
59 false
60 }
61 }
62}
63
64impl PartialOrd for SystemVersion {
65 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
66 let my_version = match &self {
67 SystemVersion::Opaque(_) => return None,
68 SystemVersion::Semantic(ver) => ver,
69 };
70
71 let other_version = match other {
72 SystemVersion::Opaque(_) => return None,
73 SystemVersion::Semantic(ver) => ver,
74 };
75
76 my_version.partial_cmp(other_version)
77 }
78}
79
80impl<'de> Deserialize<'de> for SystemVersion {
82 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
83 where
84 D: Deserializer<'de>,
85 {
86 deserializer.deserialize_str(SystemVersionVisitor)
87 }
88}
89
90impl Serialize for SystemVersion {
91 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
92 where
93 S: Serializer,
94 {
95 match self {
96 SystemVersion::Semantic(ref version) => serializer.serialize_str(&version.to_string()),
97 SystemVersion::Opaque(ref string) => serializer.serialize_str(string),
98 }
99 }
100}
101
102impl FromStr for SystemVersion {
103 type Err = Infallible;
104
105 fn from_str(s: &str) -> Result<Self, Self::Err> {
106 let result = match SemanticVersion::from_str(s) {
107 Ok(version) => SystemVersion::Semantic(version),
108 Err(_) => SystemVersion::Opaque(s.trim_end().to_owned()),
109 };
110
111 Ok(result)
112 }
113}
114
115impl fmt::Display for SystemVersion {
116 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
117 match self {
118 SystemVersion::Opaque(ref string) => f.write_str(string),
119 SystemVersion::Semantic(ref version) => version.fmt(f),
120 }
121 }
122}
123
124pub(crate) async fn read_version(
125 proxy: &fio::DirectoryProxy,
126) -> Result<SystemVersion, ReadVersionError> {
127 let file = fuchsia_fs::directory::open_file(proxy, "version", fio::PERM_READABLE)
128 .await
129 .map_err(ReadVersionError::OpenFile)?;
130 let version_str =
131 fuchsia_fs::file::read_to_string(&file).await.map_err(ReadVersionError::ReadFile)?;
132
133 Ok(SystemVersion::from_str(&version_str).unwrap())
134}
135
136#[cfg(test)]
137#[allow(clippy::bool_assert_comparison)]
138mod tests {
139 use super::*;
140 use crate::TestUpdatePackage;
141 use assert_matches::assert_matches;
142 use fuchsia_async as fasync;
143
144 #[fasync::run_singlethreaded(test)]
145 async fn read_version_success_file_exists() {
146 let p = TestUpdatePackage::new().add_file("version", "123").await;
147 assert_eq!(
148 p.version().await.unwrap(),
149 SystemVersion::Semantic(SemanticVersion::from([123]))
150 );
151 }
152
153 #[fasync::run_singlethreaded(test)]
154 async fn read_version_success_opaque() {
155 let p = TestUpdatePackage::new().add_file("version", "2020-09-08T10:17:00+10:00").await;
156 assert_eq!(
157 p.version().await.unwrap(),
158 SystemVersion::Opaque("2020-09-08T10:17:00+10:00".to_owned())
159 );
160 }
161
162 #[fasync::run_singlethreaded(test)]
163 async fn read_version_trims_trailing_whitespace() {
164 let p = TestUpdatePackage::new().add_file("version", "2020-09-08T10:17:00+10:00\n").await;
165 assert_eq!(
166 p.version().await.unwrap(),
167 SystemVersion::Opaque("2020-09-08T10:17:00+10:00".to_owned())
168 );
169 }
170
171 #[fasync::run_singlethreaded(test)]
172 async fn read_version_fail_file_does_not_exist() {
173 let p = TestUpdatePackage::new();
174 assert_matches!(read_version(p.proxy()).await, Err(ReadVersionError::OpenFile(_)));
175 }
176
177 #[test]
178 fn test_deserialize_version() {
179 let version: SystemVersion =
180 serde_json::from_str(r#""not a real version number""#).unwrap();
181 assert_eq!(version, SystemVersion::Opaque("not a real version number".to_owned()));
182 let version: SystemVersion = serde_json::from_str(r#""1.2.3""#).unwrap();
183 assert_eq!(version, SystemVersion::Semantic(SemanticVersion::from([1, 2, 3])));
184 }
185
186 #[test]
187 fn test_serialize_version() {
188 let version = SystemVersion::Opaque("not a real version number".to_owned());
189 assert_eq!(
190 serde_json::to_string(&version).unwrap(),
191 r#""not a real version number""#.to_owned()
192 );
193 let version = SystemVersion::Semantic(SemanticVersion::from([1, 3, 4, 5]));
194 assert_eq!(serde_json::to_string(&version).unwrap(), r#""1.3.4.5""#.to_owned());
195 }
196
197 #[test]
198 fn test_version_order_both_opaque() {
199 let a = SystemVersion::Opaque("version 1".to_string());
201 let b = SystemVersion::Opaque("another version".to_string());
202 assert_eq!(a < b, false);
203 assert_eq!(a > b, false);
204 assert_eq!(a >= b, false);
205 assert_eq!(a <= b, false);
206 }
207
208 #[test]
209 fn test_version_order_one_opaque() {
210 let a = SystemVersion::Opaque("opaque".to_string());
211 let b = SystemVersion::Semantic(SemanticVersion::from([1, 2, 3, 4]));
212 assert_eq!(a < b, false);
213 assert_eq!(a > b, false);
214 assert_eq!(a >= b, false);
215 assert_eq!(a <= b, false);
216 assert_eq!(b < a, false);
217 assert_eq!(b > a, false);
218 assert_eq!(b >= a, false);
219 assert_eq!(b <= a, false);
220 }
221
222 #[test]
223 fn test_version_order_both_semantic() {
224 let a = SystemVersion::Semantic(SemanticVersion::from([1, 2, 3, 4]));
225 let b = SystemVersion::Semantic(SemanticVersion::from([1, 2, 3, 5]));
226
227 assert_eq!(a < b, true);
228 assert_eq!(a <= b, true);
229 assert_eq!(a > b, false);
230 assert_eq!(a >= b, false);
231 assert_eq!(b < a, false);
232 assert_eq!(b <= a, false);
233 assert_eq!(b >= a, true);
234 assert_eq!(b > a, true);
235 }
236}