1use std::collections::HashSet;
2
3use semver::{Version, VersionReq};
4
5pub fn what_version(
44 version_requirements: HashSet<VersionReq>,
45 versions: Vec<Version>,
46) -> Result<Version, ()> {
47 versions
48 .iter()
49 .filter(|ver| version_requirements.iter().all(|req| req.matches(ver)))
50 .max_by(|arg0: &&semver::Version, other: &&semver::Version| {
51 Version::cmp_precedence(*arg0, *other)
52 })
53 .cloned()
54 .ok_or(())
55}
56
57#[cfg(test)]
58mod tests {
59 use super::*;
60
61 const VERSIONS: [&str; 14] = [
62 "2.5.1", "2.5.0", "2.4.0", "2.3.0", "2.2.0", "2.1.1", "2.1.0", "2.0.2", "2.0.1", "2.0.0",
63 "1.1.4", "1.1.2", "1.1.1", "1.0.3",
64 ];
65
66 #[yare::parameterized(
67 all_same = { vec!["2.5.1", "2.5.1", "2.5.1"], Some("2.5.1") },
68 all_caret = { vec!["^2", "^2", "^2"], Some("2.5.1") },
69 all_wildcard = { vec!["*", "*", "*"], Some("2.5.1") },
70 one_tilde = { vec!["~1.1.1", "^1", "1"], Some("1.1.4") },
71 one_wildcard = { vec!["1.*", "^1", "1.1.0"], Some("1.1.4") },
72 one_equals = { vec!["=2.2.0", "^2", "2.1.0"], Some("2.2.0") },
73 upper_bound = { vec!["^2.0", "<2.5"], Some("2.4.0") },
74 all_ranges = { vec!["^2.0", ">=2.1", "<2.3"], Some("2.2.0") },
75 lower_bound = { vec!["^1.0", "<1.1"], Some("1.0.3") },
76 no_requirements = { vec![], Some("2.5.1") }
77 )]
78 fn happy(version_reqs: Vec<&str>, expected: Option<&str>) {
79 let version_requirements: HashSet<VersionReq> = version_reqs
80 .into_iter()
81 .map(|req| req.parse().unwrap())
82 .collect();
83 let versions: Vec<Version> = VERSIONS
84 .into_iter()
85 .map(|ver| ver.parse().unwrap())
86 .collect();
87 let expected = expected.map(|ver| ver.parse::<Version>().unwrap());
88
89 let actual = what_version(version_requirements, versions);
90
91 assert_eq!(actual.ok(), expected);
92 }
93
94 #[yare::parameterized(
95 no_match = { vec!["^3"], None },
96 conflicting_ranges = { vec!["^2.0", "<2.0"], None },
97 invalid_version = { vec!["=3.0.0", "^2"], None },
98 )]
99 fn sad(version_reqs: Vec<&str>, expected: Option<&str>) {
100 let version_requirements: HashSet<VersionReq> = version_reqs
101 .into_iter()
102 .map(|req| req.parse().unwrap())
103 .collect();
104 let versions: Vec<Version> = VERSIONS
105 .into_iter()
106 .map(|ver| ver.parse().unwrap())
107 .collect();
108 let expected = expected.map(|ver| ver.parse::<Version>().unwrap());
109
110 let actual = what_version(version_requirements, versions);
111
112 assert_eq!(actual.ok(), expected);
113 }
114}