update_package/
version.rs1use 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 { value.is_empty() } else { false }
57 }
58}
59
60impl PartialOrd for SystemVersion {
61 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
62 let my_version = match &self {
63 SystemVersion::Opaque(_) => return None,
64 SystemVersion::Semantic(ver) => ver,
65 };
66
67 let other_version = match other {
68 SystemVersion::Opaque(_) => return None,
69 SystemVersion::Semantic(ver) => ver,
70 };
71
72 my_version.partial_cmp(other_version)
73 }
74}
75
76impl<'de> Deserialize<'de> for SystemVersion {
78 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
79 where
80 D: Deserializer<'de>,
81 {
82 deserializer.deserialize_str(SystemVersionVisitor)
83 }
84}
85
86impl Serialize for SystemVersion {
87 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
88 where
89 S: Serializer,
90 {
91 match self {
92 SystemVersion::Semantic(version) => serializer.serialize_str(&version.to_string()),
93 SystemVersion::Opaque(string) => serializer.serialize_str(string),
94 }
95 }
96}
97
98impl FromStr for SystemVersion {
99 type Err = Infallible;
100
101 fn from_str(s: &str) -> Result<Self, Self::Err> {
102 let result = match SemanticVersion::from_str(s) {
103 Ok(version) => SystemVersion::Semantic(version),
104 Err(_) => SystemVersion::Opaque(s.trim_end().to_owned()),
105 };
106
107 Ok(result)
108 }
109}
110
111impl fmt::Display for SystemVersion {
112 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
113 match self {
114 SystemVersion::Opaque(string) => f.write_str(string),
115 SystemVersion::Semantic(version) => version.fmt(f),
116 }
117 }
118}
119
120pub(crate) async fn read_version(
121 proxy: &fio::DirectoryProxy,
122) -> Result<SystemVersion, ReadVersionError> {
123 let file = fuchsia_fs::directory::open_file(proxy, "version", fio::PERM_READABLE)
124 .await
125 .map_err(ReadVersionError::OpenFile)?;
126 let version_str =
127 fuchsia_fs::file::read_to_string(&file).await.map_err(ReadVersionError::ReadFile)?;
128
129 Ok(SystemVersion::from_str(&version_str).unwrap())
130}
131
132#[cfg(test)]
133#[allow(clippy::bool_assert_comparison)]
134mod tests {
135 use super::*;
136 use crate::TestUpdatePackage;
137 use assert_matches::assert_matches;
138 use fuchsia_async as fasync;
139
140 #[fasync::run_singlethreaded(test)]
141 async fn read_version_success_file_exists() {
142 let p = TestUpdatePackage::new().add_file("version", "123").await;
143 assert_eq!(
144 p.version().await.unwrap(),
145 SystemVersion::Semantic(SemanticVersion::from([123]))
146 );
147 }
148
149 #[fasync::run_singlethreaded(test)]
150 async fn read_version_success_opaque() {
151 let p = TestUpdatePackage::new().add_file("version", "2020-09-08T10:17:00+10:00").await;
152 assert_eq!(
153 p.version().await.unwrap(),
154 SystemVersion::Opaque("2020-09-08T10:17:00+10:00".to_owned())
155 );
156 }
157
158 #[fasync::run_singlethreaded(test)]
159 async fn read_version_trims_trailing_whitespace() {
160 let p = TestUpdatePackage::new().add_file("version", "2020-09-08T10:17:00+10:00\n").await;
161 assert_eq!(
162 p.version().await.unwrap(),
163 SystemVersion::Opaque("2020-09-08T10:17:00+10:00".to_owned())
164 );
165 }
166
167 #[fasync::run_singlethreaded(test)]
168 async fn read_version_fail_file_does_not_exist() {
169 let p = TestUpdatePackage::new();
170 assert_matches!(read_version(p.proxy()).await, Err(ReadVersionError::OpenFile(_)));
171 }
172
173 #[test]
174 fn test_deserialize_version() {
175 let version: SystemVersion =
176 serde_json::from_str(r#""not a real version number""#).unwrap();
177 assert_eq!(version, SystemVersion::Opaque("not a real version number".to_owned()));
178 let version: SystemVersion = serde_json::from_str(r#""1.2.3""#).unwrap();
179 assert_eq!(version, SystemVersion::Semantic(SemanticVersion::from([1, 2, 3])));
180 }
181
182 #[test]
183 fn test_serialize_version() {
184 let version = SystemVersion::Opaque("not a real version number".to_owned());
185 assert_eq!(
186 serde_json::to_string(&version).unwrap(),
187 r#""not a real version number""#.to_owned()
188 );
189 let version = SystemVersion::Semantic(SemanticVersion::from([1, 3, 4, 5]));
190 assert_eq!(serde_json::to_string(&version).unwrap(), r#""1.3.4.5""#.to_owned());
191 }
192
193 #[test]
194 fn test_version_order_both_opaque() {
195 let a = SystemVersion::Opaque("version 1".to_string());
197 let b = SystemVersion::Opaque("another version".to_string());
198 assert_eq!(a < b, false);
199 assert_eq!(a > b, false);
200 assert_eq!(a >= b, false);
201 assert_eq!(a <= b, false);
202 }
203
204 #[test]
205 fn test_version_order_one_opaque() {
206 let a = SystemVersion::Opaque("opaque".to_string());
207 let b = SystemVersion::Semantic(SemanticVersion::from([1, 2, 3, 4]));
208 assert_eq!(a < b, false);
209 assert_eq!(a > b, false);
210 assert_eq!(a >= b, false);
211 assert_eq!(a <= b, false);
212 assert_eq!(b < a, false);
213 assert_eq!(b > a, false);
214 assert_eq!(b >= a, false);
215 assert_eq!(b <= a, false);
216 }
217
218 #[test]
219 fn test_version_order_both_semantic() {
220 let a = SystemVersion::Semantic(SemanticVersion::from([1, 2, 3, 4]));
221 let b = SystemVersion::Semantic(SemanticVersion::from([1, 2, 3, 5]));
222
223 assert_eq!(a < b, true);
224 assert_eq!(a <= b, true);
225 assert_eq!(a > b, false);
226 assert_eq!(a >= b, false);
227 assert_eq!(b < a, false);
228 assert_eq!(b <= a, false);
229 assert_eq!(b >= a, true);
230 assert_eq!(b > a, true);
231 }
232}