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
139 #[fuchsia::test]
140 async fn read_version_success_file_exists() {
141 let p = TestUpdatePackage::new().add_file("version", "123").await;
142 assert_eq!(
143 p.version().await.unwrap(),
144 SystemVersion::Semantic(SemanticVersion::from([123]))
145 );
146 }
147
148 #[fuchsia::test]
149 async fn read_version_success_opaque() {
150 let p = TestUpdatePackage::new().add_file("version", "2020-09-08T10:17:00+10:00").await;
151 assert_eq!(
152 p.version().await.unwrap(),
153 SystemVersion::Opaque("2020-09-08T10:17:00+10:00".to_owned())
154 );
155 }
156
157 #[fuchsia::test]
158 async fn read_version_trims_trailing_whitespace() {
159 let p = TestUpdatePackage::new().add_file("version", "2020-09-08T10:17:00+10:00\n").await;
160 assert_eq!(
161 p.version().await.unwrap(),
162 SystemVersion::Opaque("2020-09-08T10:17:00+10:00".to_owned())
163 );
164 }
165
166 #[fuchsia::test]
167 async fn read_version_fail_file_does_not_exist() {
168 let p = TestUpdatePackage::new();
169 assert_matches!(read_version(p.proxy()).await, Err(ReadVersionError::OpenFile(_)));
170 }
171
172 #[test]
173 fn test_deserialize_version() {
174 let version: SystemVersion =
175 serde_json::from_str(r#""not a real version number""#).unwrap();
176 assert_eq!(version, SystemVersion::Opaque("not a real version number".to_owned()));
177 let version: SystemVersion = serde_json::from_str(r#""1.2.3""#).unwrap();
178 assert_eq!(version, SystemVersion::Semantic(SemanticVersion::from([1, 2, 3])));
179 }
180
181 #[test]
182 fn test_serialize_version() {
183 let version = SystemVersion::Opaque("not a real version number".to_owned());
184 assert_eq!(
185 serde_json::to_string(&version).unwrap(),
186 r#""not a real version number""#.to_owned()
187 );
188 let version = SystemVersion::Semantic(SemanticVersion::from([1, 3, 4, 5]));
189 assert_eq!(serde_json::to_string(&version).unwrap(), r#""1.3.4.5""#.to_owned());
190 }
191
192 #[test]
193 fn test_version_order_both_opaque() {
194 let a = SystemVersion::Opaque("version 1".to_string());
196 let b = SystemVersion::Opaque("another version".to_string());
197 assert_eq!(a < b, false);
198 assert_eq!(a > b, false);
199 assert_eq!(a >= b, false);
200 assert_eq!(a <= b, false);
201 }
202
203 #[test]
204 fn test_version_order_one_opaque() {
205 let a = SystemVersion::Opaque("opaque".to_string());
206 let b = SystemVersion::Semantic(SemanticVersion::from([1, 2, 3, 4]));
207 assert_eq!(a < b, false);
208 assert_eq!(a > b, false);
209 assert_eq!(a >= b, false);
210 assert_eq!(a <= b, false);
211 assert_eq!(b < a, false);
212 assert_eq!(b > a, false);
213 assert_eq!(b >= a, false);
214 assert_eq!(b <= a, false);
215 }
216
217 #[test]
218 fn test_version_order_both_semantic() {
219 let a = SystemVersion::Semantic(SemanticVersion::from([1, 2, 3, 4]));
220 let b = SystemVersion::Semantic(SemanticVersion::from([1, 2, 3, 5]));
221
222 assert_eq!(a < b, true);
223 assert_eq!(a <= b, true);
224 assert_eq!(a > b, false);
225 assert_eq!(a >= b, false);
226 assert_eq!(b < a, false);
227 assert_eq!(b <= a, false);
228 assert_eq!(b >= a, true);
229 assert_eq!(b > a, true);
230 }
231}