version_lp/
version.rs

1//! the main **version** struct.
2
3use std::fmt;
4use std::cmp::Ordering;
5
6use regex::Regex;
7use serde;
8
9use std::marker::PhantomData;
10
11use versionpart::VersionPart;
12
13static VERSION_REGEX_STRING : &str = r"(?P<v>[1234567890\*]+)";
14
15#[derive(Hash)]
16pub struct Version {
17    parts : Vec<VersionPart>
18}
19
20impl PartialEq for Version {
21    fn eq(&self, other: &Version) -> bool {
22        //! in order for a version to be equal all the parts need to be equal.
23        //! and all parts need to be numbers `==` comparisons will always yield 
24        //! false when comparing against a pattern.
25        
26        let depth : usize = Version::get_shared_depth(&self, other);
27
28        for i in 0 .. depth {
29            // checks if there is a wildcard, if there is then we assume the previous 
30            // checks were all OK, and we ignore everything after a wildcard.
31            if self.parts[i].is_wildcard() || other.parts[i].is_wildcard() { return true; }
32            
33            // if the two parts don't equal, and neither was a wildcard (above), then
34            // we don't have the same version
35            if self.parts[i] != other.parts[i] { return false}
36        }
37
38        // if we get to this point then they always matched, then we are the same
39        return true;
40    }
41}
42
43impl Eq for Version { }
44
45
46impl std::cmp::Ord for Version {
47    fn cmp(&self, other : &Version) -> Ordering {
48        let depth : usize = Version::get_shared_depth(&self, other);
49
50        // checks each parts, drilling down deeper in the version
51        // struct
52        for i in 0 .. depth {
53            // checks if they are equal, if they are equal then 
54            // we won't do anything and check the next part
55            if self.parts[i] != other.parts[i] {
56                // if they are not equal then we compare those parts
57                // we only need to do this once and then return it
58                return self.parts[i].cmp(&other.parts[i]);
59            }
60        }
61        
62        // we should never get here unless the two are the same ..
63        Ordering::Equal
64    }
65}
66
67impl PartialOrd for Version {
68    fn partial_cmp(&self, other : &Version) -> Option<Ordering> {
69        Some(self.cmp(other))
70    }
71}
72
73impl fmt::Debug for Version {
74    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
75        //! prints "Version(x.x.x)"
76        write!(f, "Version ({})",self.to_string())
77    }
78}
79
80impl fmt::Display for Version {
81    fn fmt(&self,f:&mut fmt::Formatter) -> fmt::Result {
82        //! prints "x.x.x"
83        write!(f, "Version ({})",self.to_string())
84    }
85}
86
87
88impl Version {
89
90    fn get_shared_depth(v1 : &Version, v2 : &Version) -> usize {
91    //! checks how deep to compare. if they are different lenghts but all the 
92    //! numbers of the same length (i.e. `"1.2.3.4" == "1.2.3"`) then it will assume
93    //! that the smaller one has a wildcard at the end. 
94        if v1.parts.len() <= v2.parts.len() {
95            return v1.parts.len() 
96        } else { 
97            return v2.parts.len(); 
98        }
99    }
100
101    // initalizers
102    pub fn new(numbers : &[u8]) -> Version {
103        //! creates a new version directly.
104        
105        let mut parts : Vec<VersionPart> = Vec::new();
106
107        for i in 0 .. numbers.len() {
108            parts.push(VersionPart::Number(numbers[i]));
109        }
110
111        Version { parts : parts }
112    }
113
114    pub fn new_wildcard() -> Version {
115        //! creates a new wildcard version,"*", which matches compatible with everything
116
117        Version { parts : vec!(VersionPart::Wildcard("*".to_string())) } 
118    }
119
120    pub fn from_str(version : &str) -> Option<Version> {
121        //! creates a version from a string
122
123        let re = Regex::new(VERSION_REGEX_STRING).unwrap();
124        let mut parts : Vec<VersionPart> = Vec::new();
125
126        for caps in re.captures_iter(version) {
127            // there should be only one capture per match, but there could be multiple matches
128            match caps["v"].parse::<u8>() {
129                Err(_) => {
130                    // its not a number, so if it passes the regex part it must be a wildcard
131                    parts.push(VersionPart::Wildcard(caps["v"].to_string()));
132
133                    // if it matches a wild card it will ignore everything afterwards.
134                    return Some(Version{
135                        parts : parts
136                    });
137                },
138                Ok(number) => {
139                    // is a number, so an actual version number
140                    parts.push(VersionPart::Number(number));
141                }
142            }
143        }
144
145        if parts.len() > 0 { 
146            Some(Version{ 
147                parts : parts 
148            }) 
149        } else { 
150            None 
151        }
152    }
153
154    pub fn clone(&self) -> Version { 
155        //! creates a disconnected copy
156        Version::from_str(&self.to_string()).unwrap()
157    }
158
159    pub fn from_latest_vec(list : &Vec<String>) -> Option<Version> {
160        //! returns the largest number in the list of strings
161        //! assumes they all aren't wildcards (doesn't process wildcards, just skips them from the list)
162
163        let mut list_of_versions : Vec<Version> = Vec::new();
164        let mut selected = 0;
165
166        for l in list { 
167            if let Some(ver) = Version::from_str(l) { 
168                if !ver.has_wildcards() { 
169                    list_of_versions.push(ver); 
170                }
171            } 
172        }
173
174        if list_of_versions.len() <= 0 { return None; }
175
176        for cc in 1..list_of_versions.len() { 
177            if list_of_versions[cc] > list_of_versions[selected] { selected = cc; } 
178        }
179
180        Some(list_of_versions.remove(selected))
181    }
182
183    pub fn latest_compatible<'a>(&self,list : &'a Vec<String>) -> Option<&'a str> {
184        let mut latest = 0;
185        for i in 1..list.len() {
186            if let Some(ver) = Version::from_str(&list[i]){
187                if ver.is_compatible_with(&self) { 
188                    let ver_latest = Version::from_str(&list[latest]).unwrap();
189                    if ver_latest < ver {
190                        latest = i; 
191                    }
192                }
193            }
194        }
195
196        if list.len() > 0 { Some(&list[latest]) } else { None } 
197    }
198
199    pub fn latest_compatible_version<'a>(&self,list : &'a Vec<Version>) -> Option<&'a Version> {
200        let mut latest = 0;
201        for i in 1..list.len() {
202            if list[i].is_compatible_with(&self) { 
203                if &list[latest] < &list[i] {
204                    latest = i; 
205                }
206            }
207        }
208
209        if list.len() > 0 { Some(&list[latest]) } else { None } 
210    }
211
212    // checking functions, to get general booleans
213    pub fn has_wildcards(&self) -> bool { 
214        //! checks if the version has a wildcard in it
215        
216        for i in  0 .. self.parts.len() {
217            if self.parts[i].is_wildcard() { return true; }
218        }
219        
220        false
221    }
222    pub fn is_number(&self) -> bool { 
223        //! checks if the version is all numbers
224         
225        for i in 0 .. self.parts.len() {
226            if !self.parts[i].is_number() { return false; }
227        }
228        
229        true
230    }
231
232    pub fn is_wildcard(&self) -> bool {
233        //! returns true if 100% wild
234         
235        for i in 0 .. self.parts.len() {
236            if self.parts[i].is_number() { return false; }
237        }
238        
239        true
240    }
241    
242    pub fn is_compatible_with(&self,other : &Version) -> bool {
243        //! checks compatibility between versions
244        //!
245        //! uses wildcards in the comparision. if the `self` version has wildcards then it will not be compatible with anything else since it is not an actual version
246
247        // if the version number is a wildcard, it can not be compatible with anything else,
248        // compatibility is only for compairing real numbers against other real or wildcard numbers
249        if self.has_wildcards() { return false; }
250        if other.is_wildcard() { return true; }
251
252        // same version so it is compatible
253        if self == other { return true; }
254
255        let depth : usize = Version::get_shared_depth(&self, other);
256
257        for i in 0 .. depth {
258            if let VersionPart::Number(n) = self.parts[i] {
259                match other.parts[i] {
260                    VersionPart::Number(on) => { if on != n { return false; } },
261                    VersionPart::Wildcard(_) => { return true; }
262                }
263            }
264        }
265
266        false
267    }
268
269    // data structure covnersion
270    pub fn to_string(&self) -> String {
271        //! returns a string formated as "x.x.x.x"
272        
273        let mut rendered_string : String = String::new();
274
275        for i in 0 .. self.parts.len() - 1 {
276            rendered_string += &format!("{}.",self.parts[i]); 
277        }
278        rendered_string += &format!("{}", self.parts[self.parts.len()-1]);
279
280        return rendered_string;
281    }
282
283    pub fn to_string_serializer(&self) -> String {
284        //! returns a string formated as "x_x_x_x"
285        
286        let mut rendered_string : String = String::new();
287
288        for i in 0 .. self.parts.len() - 1 {
289            rendered_string += &format!("{}_",self.parts[i]); 
290        }
291        rendered_string += &format!("{}", self.parts[self.parts.len()-1]);
292
293        return rendered_string;
294    }
295
296}
297
298impl serde::Serialize for Version {
299    fn serialize<S>(&self,serializer : S) -> Result<S::Ok, S::Error> where S : serde::Serializer {
300        serializer.serialize_str(&self.to_string_serializer())
301    }
302}
303
304impl <'de> serde::Deserialize<'de> for Version {
305    fn deserialize<D>(deserializer : D) -> Result<Version, D::Error> where D : serde::Deserializer<'de> {
306        deserializer.deserialize_str(VersionVisitor::new())
307    }
308}
309
310struct VersionVisitor {
311    marker: PhantomData<fn() -> Version>
312}
313
314impl VersionVisitor {
315    fn new() -> Self {
316        VersionVisitor {
317            marker : PhantomData
318        }
319    }
320}
321
322impl <'de>serde::de::Visitor<'de> for VersionVisitor {
323    type Value = Version;
324
325    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
326        formatter.write_str("version")
327    }
328
329    fn visit_str<A>(self, string:&str) -> Result<Self::Value, A> {
330        if let Some(version) = Version::from_str(string) { Ok(version) } 
331        else { Ok(Version::from_str("0.0.0").unwrap()) }
332    }
333}
334
335
336//////////////////////////////////////////////////////////////////////////////////////////////////////////////
337// TESTS GO HERE
338
339#[cfg(test)]
340mod tests {
341    use super::*;
342
343    #[test]
344    fn comparisons() {
345        use super::Version as V;
346
347        assert!(V::new(&[1,2,3]) == V::from_str("1.2.3").unwrap());
348        assert!(V::new(&[1,2,3]) < V::new(&[2,3]));
349        assert!(V::new(&[1,2,3]) <= V::new(&[1,2,4]));
350        assert!(V::new(&[233]) > V::new(&[2,3]));
351        assert!(V::new(&[22,3,56,8]) >= V::new(&[22,3,56]));
352
353        // should it be less than since its technically not versioned as much?
354        // this test will be for when the 'behavior change' is implemented
355        // that adds strick version checking, no assumed wildcards.
356        // assert_eq!(false,V::new(&[22,3,56]) >= V::new(&[22,3,56,223]));
357    }
358
359    #[test]
360    fn sorting() {
361        let mut vector = vec![
362            super::Version::new(&[1,4,5]),
363            super::Version::new(&[1,0,5]),
364            super::Version::new(&[0,4,5]),
365            super::Version::new(&[0,9,5]),
366            super::Version::new(&[3,0,5])
367        ];
368
369        vector.sort();
370
371        assert_eq!(vector[0],super::Version::new(&[0,4,5]));
372        assert_eq!(vector[4],super::Version::new(&[3,0,5]));
373        assert_eq!(vector[3],super::Version::new(&[1,4,5]));
374    }
375
376    #[test]
377    fn version_is_compatible_with() {
378        // various compatibility checks
379        assert!(super::Version::from_str("0.1.0").unwrap().is_compatible_with(&super::Version::from_str("0.*.*").unwrap()));
380        assert!(super::Version::from_str("4.1.0").unwrap().is_compatible_with(&super::Version::from_str("4.*.*").unwrap()));
381        assert!(!super::Version::from_str("1.2.0").unwrap().is_compatible_with(&super::Version::from_str("1.1.*").unwrap()));
382        assert!(!super::Version::from_str("11.1.*").unwrap().is_compatible_with(&super::Version::from_str("11.1.4").unwrap()));
383        assert!(super::Version::from_str("1.1").unwrap().is_compatible_with(&super::Version::from_str("1.1.0").unwrap()));
384        assert!(!super::Version::from_str("2.1.0.2.43").unwrap().is_compatible_with(&super::Version::from_str("2.1.1").unwrap()));
385        assert!(!super::Version::from_str("1.1.*").unwrap().is_compatible_with(&super::Version::from_str("1.*.*").unwrap()));
386        assert!(super::Version::from_str("21.11.0").unwrap().is_compatible_with(&super::Version::from_str("*").unwrap()));
387        // various failing ones
388        assert_eq!(false,super::Version::from_str("21.11.0").unwrap().is_compatible_with(&super::Version::from_str("12.*").unwrap()));
389        assert_eq!(false,super::Version::from_str("12.0").unwrap().is_compatible_with(&super::Version::from_str("12.1.2").unwrap()));
390        assert_eq!(false,super::Version::from_str("21.11").unwrap().is_compatible_with(&super::Version::from_str("12").unwrap()));
391        assert_eq!(false,super::Version::from_str("21.*").unwrap().is_compatible_with(&super::Version::from_str("22.12").unwrap()));
392    }
393
394    #[test]
395    fn version_comparisons() {
396        assert!(super::Version::from_str("1.1.0").unwrap() > super::Version::from_str("1.0.0").unwrap());
397        assert!(super::Version::from_str("1.2.0").unwrap() < super::Version::from_str("1.3.1").unwrap());
398        assert!(super::Version::from_str("2.3.2").unwrap() > super::Version::from_str("1.4.8").unwrap());
399        assert!(super::Version::from_str("1.10.2").unwrap() > super::Version::from_str("1.4.22").unwrap());
400    }
401
402    #[test]
403    fn latest_compatible() {
404        let versions : Vec<String> = vec![
405            "1.0.1".to_string(),
406            "1.0.2".to_string(),
407            "1.1.0".to_string(),
408            "1.0.0".to_string()
409        ];
410
411        let version = Version::from_str("1.*.*").unwrap();
412
413        assert_eq!(version.latest_compatible(&versions).unwrap().to_string(),"1.1.0".to_string());
414    }
415
416    #[test]
417    fn latest_compatible_version() {
418        let versions : Vec<Version> = vec![
419            Version::from_str("1.0.0").unwrap(),
420            Version::from_str("1.0.1").unwrap(),
421            Version::from_str("1.0.2").unwrap(),
422            Version::from_str("1.1.0").unwrap()
423        ];
424
425        let version = Version::from_str("1.*.*").unwrap();
426
427        assert_eq!(version.latest_compatible_version(&versions).unwrap().to_string(),"1.1.0".to_string());
428        assert_eq!(Version::new(&[1]).latest_compatible_version(&versions).unwrap().to_string(),"1.1.0".to_string());
429    }
430
431    #[test]
432    fn versionpart_is_number() {
433        let vp = super::VersionPart::Number(12);
434        let vp2 = super::VersionPart::Wildcard("*".to_string());
435        assert!(vp.is_number());
436        assert!(!vp2.is_number());
437    }
438
439    #[test]
440    fn versionpart_is_wildcard() {
441        let vp = super::VersionPart::Number(12);
442        let vp2 = super::VersionPart::Wildcard("*".to_string());
443        assert!(!vp.is_wildcard());
444        assert!(vp2.is_wildcard());
445    }
446
447    #[test]
448    fn versionpart_equals() {
449        let vp = super::VersionPart::Number(14);
450        let vp2 = super::VersionPart::Number(65);
451        let vp3 = super::VersionPart::Wildcard("*".to_string());
452        assert!(vp == super::VersionPart::Number(14));
453        assert!(vp2 == super::VersionPart::Number(65));
454        assert!(vp2 != vp);
455        assert!(vp != vp3);
456    }
457
458    #[test]
459    fn version_from_string() {
460        let ver1 : super::Version = super::Version::from_str("1.0.0").unwrap();
461        assert_eq!(super::Version::new(&[1,0,0]),ver1);
462    }
463
464    #[test]
465    fn serde() {
466        use serde_test::{Token, assert_tokens};
467
468        let version = super::Version::from_str("0.1.2").unwrap();
469        assert_tokens(&version,&[Token::Str("0_1_2")]);
470
471    }
472
473
474}