1#[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
9pub struct Version(VersionPart, Vec<VersionPart>);
10
11impl<'de> Deserialize<'de> for Version
12{
13 #[inline(always)]
15 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error>
16 {
17 struct SupportVisitor;
18
19 impl<'de> Visitor<'de> for SupportVisitor
20 {
21 type Value = Version;
22
23 #[inline(always)]
24 fn expecting(&self, formatter: &mut Formatter) -> fmt::Result
25 {
26 formatter.write_str("a string which contains a period-delimited version")
27 }
28
29 #[inline(always)]
30 fn visit_str<E: de::Error>(self, v: &str) -> Result<Self::Value, E>
31 {
32 Ok(Version::parse(v))
33 }
34 }
35
36 deserializer.deserialize_str(SupportVisitor)
37 }
38}
39
40impl<'a, I: Into<&'a str>> From<I> for Version
41{
42 #[inline(always)]
45 fn from(value: I) -> Self
46 {
47 Version::parse(value.into())
48 }
49}
50
51impl FromStr for Version
52{
53 type Err = ();
54
55 #[inline(always)]
58 fn from_str(s: &str) -> Result<Self, Self::Err>
59 {
60 Ok(Version::parse(s))
61 }
62}
63
64impl Version
65{
66 #[inline(always)]
68 pub fn opera_mini_all() -> Self
69 {
70 Version(VersionPart::All, vec![])
71 }
72
73 #[inline(always)]
75 pub fn safari_technology_preview() -> Self
76 {
77 Version(VersionPart::TechnologyPreview, vec![])
78 }
79
80 #[inline(always)]
82 pub fn major(major_version: u64) -> Self
83 {
84 Version(VersionPart::Number(major_version), vec![])
85 }
86
87 #[inline(always)]
89 pub fn major_minor(major_version: u64, minor_version: u64) -> Self
90 {
91 Version(VersionPart::Number(major_version), vec![VersionPart::Number(minor_version)])
92 }
93
94 #[inline(always)]
96 pub fn major_minor_revision(major_version: u64, minor_version: u64, revision_version: u64) -> Self
97 {
98 Version(VersionPart::Number(major_version), vec![VersionPart::Number(minor_version), VersionPart::Number(revision_version)])
99 }
100
101 #[inline(always)]
103 pub fn is_safari_technology_preview(&self) -> bool
104 {
105 match self.0
106 {
107 VersionPart::TechnologyPreview => true,
108 _ => false,
109 }
110 }
111
112 #[inline(always)]
114 pub fn is_invalid_or_unknown(&self) -> bool
115 {
116 match self.0
117 {
118 VersionPart::Number(0) => true,
119 VersionPart::Unknown(_) => true,
120 _ => false,
121 }
122 }
123
124 #[inline(always)]
125 fn parse(v: &str) -> Self
126 {
127 use self::VersionPart::*;
128
129 if let Some(index) = v.find('-')
131 {
132 return Self::parse(&v[..index]);
133 }
134
135 match v
137 {
138 "TP" => return Self::safari_technology_preview(),
139 "all" => return Self::opera_mini_all(),
140 _ => (),
141 }
142
143 let parts = v.split('.');
144
145 let (lower, upper) = parts.size_hint();
146 let mut capacity = if let Some(upper) = upper
147 {
148 upper
149 }
150 else
151 {
152 lower
153 };
154 if capacity != 0
155 {
156 capacity -= 1;
157 }
158
159 let mut first = None;
160 let mut subsequent = Vec::with_capacity(capacity);
161 for part in parts
162 {
163 let versionPart = match part.parse::<u64>()
164 {
165 Ok(value) => Number(value),
166 Err(_) => Unknown(part.to_owned())
167 };
168 if first.is_none()
169 {
170 first = Some(versionPart);
171 }
172 else
173 {
174 subsequent.push(versionPart);
175 }
176 }
177
178 subsequent.shrink_to_fit();
179 Version(first.unwrap(), subsequent)
180 }
181}