1#![warn(clippy::all, clippy::pedantic, clippy::nursery, clippy::cargo)]
2#![doc = include_str!("../README.md")]
3
4pub mod comparator;
5pub mod constraint;
6pub mod error;
7pub mod interval;
8pub mod intervals;
9pub mod parser;
10pub mod version;
11
12pub use comparator::{
13 compare, equal_to, greater_than, greater_than_or_equal_to, less_than, less_than_or_equal_to,
14 not_equal_to,
15};
16pub use constraint::{
17 Bound, Constraint, MatchAllConstraint, MatchNoneConstraint, MultiConstraint, Operator,
18 SingleConstraint,
19};
20pub use error::{Result, SemverError};
21pub use interval::{BranchConstraint, Interval, IntervalResult};
22pub use intervals::Intervals;
23pub use parser::VersionParser;
24pub use version::{expand_stability, stability_order, version_compare, Stability};
25
26pub struct Semver;
28
29impl Semver {
30 pub fn satisfies(version: &str, constraints: &str) -> Result<bool> {
48 let normalized = VersionParser::normalize(version)?;
49 let provider = SingleConstraint::new(Operator::Eq, &normalized);
50 let parsed_constraints = VersionParser::parse_constraints(constraints)?;
51
52 Ok(parsed_constraints.matches(&provider))
53 }
54
55 pub fn satisfied_by(versions: &[&str], constraints: &str) -> Result<Vec<String>> {
73 let mut result = Vec::new();
74 for version in versions {
75 if Self::satisfies(version, constraints)? {
76 result.push((*version).to_string());
77 }
78 }
79 Ok(result)
80 }
81
82 pub fn sort(versions: &[&str]) -> Result<Vec<String>> {
96 Self::usort(versions, true)
97 }
98
99 pub fn rsort(versions: &[&str]) -> Result<Vec<String>> {
113 Self::usort(versions, false)
114 }
115
116 fn usort(versions: &[&str], ascending: bool) -> Result<Vec<String>> {
117 let mut normalized: Vec<(String, usize)> = Vec::with_capacity(versions.len());
119 for (idx, version) in versions.iter().enumerate() {
120 let mut norm = VersionParser::normalize(version)?;
121 #[allow(deprecated)]
122 {
123 norm = VersionParser::normalize_default_branch(&norm);
124 }
125 normalized.push((norm, idx));
126 }
127
128 normalized.sort_by(|(a, _), (b, _)| {
131 let cmp = version_compare(a, b);
132 if ascending {
133 cmp
134 } else {
135 cmp.reverse()
136 }
137 });
138
139 Ok(normalized
141 .into_iter()
142 .map(|(_, idx)| versions[idx].to_string())
143 .collect())
144 }
145}
146
147#[cfg(test)]
148mod tests {
149 use super::*;
150
151 #[test]
152 fn test_basic_constraint() {
153 let c = SingleConstraint::new(Operator::Ge, "1.0.0.0");
154 assert_eq!(c.operator(), Operator::Ge);
155 assert_eq!(c.version(), "1.0.0.0");
156 }
157
158 #[test]
159 fn test_match_all() {
160 let all = MatchAllConstraint::new();
161 let c = SingleConstraint::new(Operator::Eq, "1.0.0");
162 assert!(all.matches(&c));
163 }
164
165 #[test]
166 fn test_match_none() {
167 let none = MatchNoneConstraint::new();
168 let c = SingleConstraint::new(Operator::Eq, "1.0.0");
169 assert!(!none.matches(&c));
170 }
171}