diff --git a/examples/branching_error_reporting.rs b/examples/branching_error_reporting.rs index 9d258dcc..2b43f8e1 100644 --- a/examples/branching_error_reporting.rs +++ b/examples/branching_error_reporting.rs @@ -13,15 +13,15 @@ fn main() { // root 1.0.0 depends on foo ^1.0.0 dependency_provider.add_dependencies( "root", (1, 0, 0), - [("foo", Range::between((1, 0, 0), (2, 0, 0)))], + [("foo", Range::from_range_bounds((1, 0, 0)..(2, 0, 0)))], ); #[rustfmt::skip] // foo 1.0.0 depends on a ^1.0.0 and b ^1.0.0 dependency_provider.add_dependencies( "foo", (1, 0, 0), [ - ("a", Range::between((1, 0, 0), (2, 0, 0))), - ("b", Range::between((1, 0, 0), (2, 0, 0))), + ("a", Range::from_range_bounds((1, 0, 0)..(2, 0, 0))), + ("b", Range::from_range_bounds((1, 0, 0)..(2, 0, 0))), ], ); #[rustfmt::skip] @@ -29,15 +29,15 @@ fn main() { dependency_provider.add_dependencies( "foo", (1, 1, 0), [ - ("x", Range::between((1, 0, 0), (2, 0, 0))), - ("y", Range::between((1, 0, 0), (2, 0, 0))), + ("x", Range::from_range_bounds((1, 0, 0)..(2, 0, 0))), + ("y", Range::from_range_bounds((1, 0, 0)..(2, 0, 0))), ], ); #[rustfmt::skip] // a 1.0.0 depends on b ^2.0.0 dependency_provider.add_dependencies( "a", (1, 0, 0), - [("b", Range::between((2, 0, 0), (3, 0, 0)))], + [("b", Range::from_range_bounds((2, 0, 0)..(3, 0, 0)))], ); // b 1.0.0 and 2.0.0 have no dependencies. dependency_provider.add_dependencies("b", (1, 0, 0), []); @@ -46,7 +46,7 @@ fn main() { // x 1.0.0 depends on y ^2.0.0. dependency_provider.add_dependencies( "x", (1, 0, 0), - [("y", Range::between((2, 0, 0), (3, 0, 0)))], + [("y", Range::from_range_bounds((2, 0, 0)..(3, 0, 0)))], ); // y 1.0.0 and 2.0.0 have no dependencies. dependency_provider.add_dependencies("y", (1, 0, 0), []); diff --git a/examples/doc_interface_error.rs b/examples/doc_interface_error.rs index 0d98f257..990a0f19 100644 --- a/examples/doc_interface_error.rs +++ b/examples/doc_interface_error.rs @@ -25,22 +25,22 @@ fn main() { // Dependencies of the menu lib. dependency_provider.add_dependencies("menu", (1, 0, 0), [ - ("dropdown", Range::strictly_lower_than((2, 0, 0))), + ("dropdown", Range::from_range_bounds(..(2, 0, 0))), ]); dependency_provider.add_dependencies("menu", (1, 1, 0), [ - ("dropdown", Range::higher_than((2, 0, 0))), + ("dropdown", Range::from_range_bounds((2, 0, 0)..)), ]); dependency_provider.add_dependencies("menu", (1, 2, 0), [ - ("dropdown", Range::higher_than((2, 0, 0))), + ("dropdown", Range::from_range_bounds((2, 0, 0)..)), ]); dependency_provider.add_dependencies("menu", (1, 3, 0), [ - ("dropdown", Range::higher_than((2, 0, 0))), + ("dropdown", Range::from_range_bounds((2, 0, 0)..)), ]); dependency_provider.add_dependencies("menu", (1, 4, 0), [ - ("dropdown", Range::higher_than((2, 0, 0))), + ("dropdown", Range::from_range_bounds((2, 0, 0)..)), ]); dependency_provider.add_dependencies("menu", (1, 5, 0), [ - ("dropdown", Range::higher_than((2, 0, 0))), + ("dropdown", Range::from_range_bounds((2, 0, 0)..)), ]); // Dependencies of the dropdown lib. diff --git a/examples/doc_interface_semantic.rs b/examples/doc_interface_semantic.rs index d284aaa8..2a8331ed 100644 --- a/examples/doc_interface_semantic.rs +++ b/examples/doc_interface_semantic.rs @@ -23,22 +23,22 @@ fn main() { // Dependencies of the menu lib. dependency_provider.add_dependencies("menu", (1, 0, 0), [ - ("dropdown", Range::strictly_lower_than((2, 0, 0))), + ("dropdown", Range::from_range_bounds(..(2, 0, 0))), ]); dependency_provider.add_dependencies("menu", (1, 1, 0), [ - ("dropdown", Range::higher_than((2, 0, 0))), + ("dropdown", Range::from_range_bounds((2, 0, 0)..)), ]); dependency_provider.add_dependencies("menu", (1, 2, 0), [ - ("dropdown", Range::higher_than((2, 0, 0))), + ("dropdown", Range::from_range_bounds((2, 0, 0)..)), ]); dependency_provider.add_dependencies("menu", (1, 3, 0), [ - ("dropdown", Range::higher_than((2, 0, 0))), + ("dropdown", Range::from_range_bounds((2, 0, 0)..)), ]); dependency_provider.add_dependencies("menu", (1, 4, 0), [ - ("dropdown", Range::higher_than((2, 0, 0))), + ("dropdown", Range::from_range_bounds((2, 0, 0)..)), ]); dependency_provider.add_dependencies("menu", (1, 5, 0), [ - ("dropdown", Range::higher_than((2, 0, 0))), + ("dropdown", Range::from_range_bounds((2, 0, 0)..)), ]); // Dependencies of the dropdown lib. diff --git a/examples/linear_error_reporting.rs b/examples/linear_error_reporting.rs index fa38c538..195ff388 100644 --- a/examples/linear_error_reporting.rs +++ b/examples/linear_error_reporting.rs @@ -14,21 +14,21 @@ fn main() { dependency_provider.add_dependencies( "root", (1, 0, 0), [ - ("foo", Range::between((1, 0, 0), (2, 0, 0))), - ("baz", Range::between((1, 0, 0), (2, 0, 0))), + ("foo", Range::from_range_bounds((1, 0, 0)..(2, 0, 0))), + ("baz", Range::from_range_bounds((1, 0, 0)..(2, 0, 0))), ], ); #[rustfmt::skip] // foo 1.0.0 depends on bar ^2.0.0 dependency_provider.add_dependencies( "foo", (1, 0, 0), - [("bar", Range::between((2, 0, 0), (3, 0, 0)))], + [("bar", Range::from_range_bounds((2, 0, 0)..(3, 0, 0)))], ); #[rustfmt::skip] // bar 2.0.0 depends on baz ^3.0.0 dependency_provider.add_dependencies( "bar", (2, 0, 0), - [("baz", Range::between((3, 0, 0), (4, 0, 0)))], + [("baz", Range::from_range_bounds((3, 0, 0)..(4, 0, 0)))], ); // baz 1.0.0 and 3.0.0 have no dependencies dependency_provider.add_dependencies("baz", (1, 0, 0), []); diff --git a/src/range.rs b/src/range.rs index 8de8b3ff..5b1f9bbf 100644 --- a/src/range.rs +++ b/src/range.rs @@ -16,6 +16,7 @@ use std::cmp::Ordering; use std::fmt; +use std::ops::{Bound, RangeBounds}; use crate::internal::small_vec::SmallVec; use crate::version::Version; @@ -85,6 +86,31 @@ impl Range { Self::none() } } + + /// Construct a simple range from anything that impls [RangeBounds] like `v1..v2`. + pub fn from_range_bounds(bounds: R) -> Self + where + R: RangeBounds, + for<'a> &'a IV: Into, + { + let start = match bounds.start_bound() { + Bound::Included(s) => s.into(), + Bound::Excluded(s) => s.into().bump(), + Bound::Unbounded => V::lowest(), + }; + let end = match bounds.end_bound() { + Bound::Included(e) => Some(e.into().bump()), + Bound::Excluded(e) => Some(e.into()), + Bound::Unbounded => None, + }; + if end.is_some() && end.as_ref() <= Some(&start) { + Self::none() + } else { + Self { + segments: SmallVec::one((start, end)), + } + } + } } // Set operations. @@ -260,6 +286,24 @@ impl Range { pub fn lowest_version(&self) -> Option { self.segments.first().map(|(start, _)| start).cloned() } + + /// Convert to something that can be used with + /// [BTreeMap::range](std::collections::BTreeMap::range). + /// All versions contained in self, will be in the output, + /// but there may be versions in the output that are not contained in self. + /// Returns None if the range is empty. + pub fn bounding_range(&self) -> Option<(Bound<&V>, Bound<&V>)> { + self.segments.first().map(|(start, _)| { + let end = { + self.segments + .last() + .and_then(|(_, l)| l.as_ref()) + .map(Bound::Excluded) + .unwrap_or(Bound::Unbounded) + }; + (Bound::Included(start), end) + }) + } } // REPORT ###################################################################### @@ -405,5 +449,25 @@ pub mod tests { fn contains_intersection(range in strategy(), version in version_strat()) { assert_eq!(range.contains(&version), range.intersection(&Range::exact(version)) != Range::none()); } + + #[test] + fn contains_bounding_range(range in strategy(), version in version_strat()) { + if range.contains(&version) { + assert!(range.bounding_range().map(|b| b.contains(&version)).unwrap_or(false)); + } + } + + #[test] + fn from_range_bounds(range in any::<(Bound, Bound)>(), version in version_strat()) { + let rv: Range = Range::from_range_bounds(range); + assert_eq!(range.contains(&version.0), rv.contains(&version)); + } + + #[test] + fn from_range_bounds_round_trip(range in any::<(Bound, Bound)>()) { + let rv: Range = Range::from_range_bounds(range); + let rv2: Range = rv.bounding_range().map(Range::from_range_bounds::<_, NumberVersion>).unwrap_or_else(Range::none); + assert_eq!(rv, rv2); + } } } diff --git a/src/version.rs b/src/version.rs index c7d749ee..bf0524b4 100644 --- a/src/version.rs +++ b/src/version.rs @@ -80,6 +80,21 @@ impl From<(u32, u32, u32)> for SemanticVersion { } } +// Convert a &(major, minor, patch) into a version. +impl From<&(u32, u32, u32)> for SemanticVersion { + fn from(tuple: &(u32, u32, u32)) -> Self { + let (major, minor, patch) = *tuple; + Self::new(major, minor, patch) + } +} + +// Convert an &version into a version. +impl From<&SemanticVersion> for SemanticVersion { + fn from(v: &SemanticVersion) -> Self { + *v + } +} + // Convert a version into a tuple (major, minor, patch). impl From for (u32, u32, u32) { fn from(v: SemanticVersion) -> Self { @@ -237,6 +252,20 @@ impl From for NumberVersion { } } +// Convert an &usize into a version. +impl From<&u32> for NumberVersion { + fn from(v: &u32) -> Self { + Self(*v) + } +} + +// Convert an &version into a version. +impl From<&NumberVersion> for NumberVersion { + fn from(v: &NumberVersion) -> Self { + *v + } +} + // Convert a version into an usize. impl From for u32 { fn from(version: NumberVersion) -> Self {