diff --git a/Cargo.lock b/Cargo.lock index 4a9a4f4..670bef9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -108,7 +108,7 @@ dependencies = [ [[package]] name = "range_bounds_map" -version = "0.0.6" +version = "0.1.0" dependencies = [ "either", "itertools", diff --git a/Cargo.toml b/Cargo.toml index 06040b8..1d14eca 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "range_bounds_map" -version = "0.0.6" +version = "0.1.0" authors = ["James Forster "] edition = "2021" description = """ diff --git a/README.md b/README.md index 66d2a58..f3f20ec 100644 --- a/README.md +++ b/README.md @@ -145,6 +145,7 @@ topic area: - Very similar to this crate but can only use [`Range`]s and [`RangeInclusive`]s as keys in it's `map` and `set` structs (separately). +- - Cool library for fully-generic ranges (unlike std::ops ranges), along with a `Ranges` datastructure for storing them (Vec-based diff --git a/src/range_bounds_map.rs b/src/range_bounds_map.rs index 6e2ba61..401bcca 100644 --- a/src/range_bounds_map.rs +++ b/src/range_bounds_map.rs @@ -972,7 +972,7 @@ where pub fn gaps_same<'a, Q>( &'a self, outer_range_bounds: &'a Q, - ) -> impl Iterator> + '_ + ) -> impl Iterator> + 'a where Q: RangeBounds, K: TryFromBounds, @@ -1528,6 +1528,112 @@ where return Ok(output); } + + /// Similar to [`RangeBoundsMap::overlapping()`] except the + /// `(Bound, Bound)`s returned in the iterator have been + /// trimmed/cut by the given `range_bounds`. + /// + /// This is sort of the analogue to the AND function between a + /// `RangeBounds` AND a [`RangeBoundsMap`]. + /// + /// # Examples + /// ``` + /// use std::ops::Bound; + /// + /// use range_bounds_map::RangeBoundsMap; + /// + /// let range_bounds_map = RangeBoundsMap::try_from([ + /// (1..4, false), + /// (4..8, true), + /// (8..100, false), + /// ]) + /// .unwrap(); + /// + /// let mut overlapping_trimmed = + /// range_bounds_map.overlapping_trimmed(&(2..20)); + /// + /// assert_eq!( + /// overlapping_trimmed.collect::>(), + /// [ + /// ((Bound::Included(&2), Bound::Excluded(&4)), &false), + /// ((Bound::Included(&4), Bound::Excluded(&8)), &true), + /// ((Bound::Included(&8), Bound::Excluded(&20)), &false) + /// ] + /// ); + /// ``` + #[tested] + pub fn overlapping_trimmed<'a, Q>( + &'a self, + range_bounds: &'a Q, + ) -> impl DoubleEndedIterator, Bound<&I>), &V)> + where + Q: RangeBounds, + { + let mut overlapping = self.overlapping(range_bounds); + let first = overlapping.next(); + let mut overlapping = overlapping.rev(); + let last = overlapping.next(); + let overlapping = overlapping.rev(); + + let trimmed_first = + first.and_then(|x| cut_range_bounds(x.0, range_bounds).inside_cut); + let trimmed_last = + last.and_then(|x| cut_range_bounds(x.0, range_bounds).inside_cut); + + let trimmed_first_entry = trimmed_first.map(|x| (x, first.unwrap().1)); + let trimmed_last_entry = trimmed_last.map(|x| (x, last.unwrap().1)); + + return trimmed_first_entry + .into_iter() + .chain(overlapping.map(|(key, value)| (expand(key), value))) + .chain(trimmed_last_entry.into_iter()); + } + + /// Identical to [`RangeBoundsMap::overlapping_trimmed()`] except + /// it returns an iterator of `(Result, Value)`. + /// + /// # Examples + /// ``` + /// use range_bounds_map::{RangeBoundsMap, TryFromBoundsError}; + /// + /// let range_bounds_map = RangeBoundsMap::try_from([ + /// (1..4, false), + /// (4..8, true), + /// (8..100, false), + /// ]) + /// .unwrap(); + /// + /// let mut overlapping_trimmed_same = + /// range_bounds_map.overlapping_trimmed_same(&(2..=20)); + /// + /// assert_eq!( + /// overlapping_trimmed_same.collect::>(), + /// [ + /// (Ok(2..4), &false), + /// (Ok(4..8), &true), + /// // Due to using a RangeInclusive in `overlapping_trimmed_same()` + /// (Err(TryFromBoundsError), &false) + /// ] + /// ); + /// ``` + #[trivial] + pub fn overlapping_trimmed_same<'a, Q>( + &'a self, + range_bounds: &'a Q, + ) -> impl DoubleEndedIterator, &V)> + where + Q: RangeBounds, + K: TryFromBounds, + { + self.overlapping_trimmed(range_bounds).map(|(key, value)| { + ( + K::try_from_bounds(key.0.cloned(), key.1.cloned()) + .ok_or(TryFromBoundsError), + value, + ) + }) + } } impl TryFrom<[(K, V); N]> for RangeBoundsMap @@ -1631,6 +1737,7 @@ where K: RangeBounds + Serialize, V: Serialize, { + #[trivial] fn serialize(&self, serializer: S) -> Result where S: Serializer, @@ -1649,6 +1756,7 @@ where I: Ord + Clone, V: Deserialize<'de>, { + #[trivial] fn deserialize(deserializer: D) -> Result where D: Deserializer<'de>, @@ -1675,10 +1783,12 @@ where { type Value = RangeBoundsMap; + #[trivial] fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { formatter.write_str("a RangeBoundsMap") } + #[trivial] fn visit_map(self, mut access: A) -> Result where A: MapAccess<'de>, @@ -2085,15 +2195,15 @@ mod tests { //case one for overlap_range in all_valid_test_bounds() { for inside_range in all_valid_test_bounds() { - let mut range_bounds_set = RangeBoundsMap::new(); - range_bounds_set.insert_platonic(inside_range, ()).unwrap(); + let mut range_bounds_map = RangeBoundsMap::new(); + range_bounds_map.insert_platonic(inside_range, ()).unwrap(); let mut expected_overlapping = Vec::new(); if overlaps(&overlap_range, &inside_range) { expected_overlapping.push(inside_range); } - let overlapping = range_bounds_set + let overlapping = range_bounds_map .overlapping(&overlap_range) .map(|(key, _)| key) .copied() @@ -2114,9 +2224,9 @@ mod tests { for (inside_range1, inside_range2) in all_non_overlapping_test_bound_pairs() { - let mut range_bounds_set = RangeBoundsMap::new(); - range_bounds_set.insert_platonic(inside_range1, ()).unwrap(); - range_bounds_set.insert_platonic(inside_range2, ()).unwrap(); + let mut range_bounds_map = RangeBoundsMap::new(); + range_bounds_map.insert_platonic(inside_range1, ()).unwrap(); + range_bounds_map.insert_platonic(inside_range2, ()).unwrap(); let mut expected_overlapping = Vec::new(); if overlaps(&overlap_range, &inside_range1) { @@ -2134,7 +2244,7 @@ mod tests { } } - let overlapping = range_bounds_set + let overlapping = range_bounds_map .overlapping(&overlap_range) .map(|(key, _)| key) .copied() @@ -2151,6 +2261,65 @@ mod tests { } } + #[test] + fn overlapping_trimmed_tests() { + //case zero + for overlap_range in all_valid_test_bounds() { + //you can't overlap nothing + assert!( + RangeBoundsMap::, ()>::new() + .overlapping_trimmed(&overlap_range) + .next() + .is_none() + ); + } + + //case one + for overlap_range in all_valid_test_bounds() { + for inside_range in all_valid_test_bounds() { + let mut range_bounds_map = RangeBoundsMap::new(); + range_bounds_map.insert_platonic(inside_range, ()).unwrap(); + + let result = range_bounds_map + .overlapping_trimmed(&overlap_range) + .map(|(key, value)| (cloned_bounds(key), value.clone())) + .collect::>(); + + for i in NUMBERS_DOMAIN { + assert_eq!( + overlap_range.contains(i) && inside_range.contains(i), + result.contains_point(i) + ); + } + } + } + + //case two + for overlap_range in all_valid_test_bounds() { + for (inside_range1, inside_range2) in + all_non_overlapping_test_bound_pairs() + { + let mut range_bounds_map = RangeBoundsMap::new(); + range_bounds_map.insert_platonic(inside_range1, ()).unwrap(); + range_bounds_map.insert_platonic(inside_range2, ()).unwrap(); + + let result = range_bounds_map + .overlapping_trimmed(&overlap_range) + .map(|(key, value)| (cloned_bounds(key), value.clone())) + .collect::>(); + + for i in NUMBERS_DOMAIN { + assert_eq!( + overlap_range.contains(i) + && (inside_range1.contains(i) + || inside_range2.contains(i)), + result.contains_point(i) + ); + } + } + } + } + #[test] fn remove_overlapping_tests() { assert_remove_overlapping(basic(), ii(5, 5), [], None::<[_; 0]>); diff --git a/src/range_bounds_set.rs b/src/range_bounds_set.rs index a4cb02e..ad4cedc 100644 --- a/src/range_bounds_set.rs +++ b/src/range_bounds_set.rs @@ -492,7 +492,7 @@ where pub fn gaps_same<'a, Q>( &'a self, outer_range_bounds: &'a Q, - ) -> impl Iterator> + '_ + ) -> impl Iterator> + 'a where Q: RangeBounds, K: TryFromBounds, @@ -837,6 +837,81 @@ where .split_off(start_bound) .map(|map| map.into_iter().map(first).collect()) } + + /// Similar to [`RangeBoundsSet::overlapping()`] except the + /// `(Bound, Bound)`s returned in the iterator have been + /// trimmed/cut by the given `range_bounds`. + /// + /// This is sort of the analogue to the AND function between a + /// `RangeBounds` AND a [`RangeBoundsSet`]. + /// + /// # Examples + /// ``` + /// use std::ops::Bound; + /// + /// use range_bounds_map::RangeBoundsSet; + /// + /// let range_bounds_set = + /// RangeBoundsSet::try_from([1..4, 4..8, 8..100]).unwrap(); + /// + /// let mut overlapping_trimmed = + /// range_bounds_set.overlapping_trimmed(&(2..20)); + /// + /// assert_eq!( + /// overlapping_trimmed.collect::>(), + /// [ + /// (Bound::Included(&2), Bound::Excluded(&4)), + /// (Bound::Included(&4), Bound::Excluded(&8)), + /// (Bound::Included(&8), Bound::Excluded(&20)), + /// ] + /// ); + /// ``` + #[trivial] + pub fn overlapping_trimmed<'a, Q>( + &'a self, + range_bounds: &'a Q, + ) -> impl DoubleEndedIterator, Bound<&I>)> + where + Q: RangeBounds, + { + self.map.overlapping_trimmed(range_bounds).map(first) + } + + /// Identical to [`RangeBoundsSet::overlapping_trimmed()`] except + /// it returns an iterator of `Result`. + /// + /// # Examples + /// ``` + /// use range_bounds_map::{RangeBoundsSet, TryFromBoundsError}; + /// + /// let range_bounds_set = + /// RangeBoundsSet::try_from([1..4, 4..8, 8..100]).unwrap(); + /// + /// let mut overlapping_trimmed_same = + /// range_bounds_set.overlapping_trimmed_same(&(2..=20)); + /// + /// assert_eq!( + /// overlapping_trimmed_same.collect::>(), + /// [ + /// Ok(2..4), + /// Ok(4..8), + /// // Due to using a RangeInclusive in `overlapping_trimmed_same()` + /// Err(TryFromBoundsError), + /// ] + /// ); + /// ``` + #[trivial] + pub fn overlapping_trimmed_same<'a, Q>( + &'a self, + range_bounds: &'a Q, + ) -> impl DoubleEndedIterator> + 'a + where + Q: RangeBounds, + K: TryFromBounds, + { + self.map.overlapping_trimmed_same(range_bounds).map(first) + } } impl TryFrom<[K; N]> for RangeBoundsSet @@ -938,6 +1013,7 @@ where I: Ord + Clone, K: RangeBounds + Serialize, { + #[trivial] fn serialize(&self, serializer: S) -> Result where S: Serializer, @@ -955,6 +1031,7 @@ where K: Deserialize<'de> + RangeBounds, I: Ord + Clone, { + #[trivial] fn deserialize(deserializer: D) -> Result where D: Deserializer<'de>, @@ -978,10 +1055,12 @@ where { type Value = RangeBoundsSet; + #[trivial] fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { formatter.write_str("a RangeBoundsSet") } + #[trivial] fn visit_seq(self, mut access: A) -> Result where A: SeqAccess<'de>, diff --git a/todo.md b/todo.md index ab7cf1b..bb6c0d9 100644 --- a/todo.md +++ b/todo.md @@ -8,6 +8,8 @@ some reason(?) - take a look around idiomatic rust for a bit - review method parameter names for all public functions +- make try_from_bounds trait return TryFromBoundsError rather than + mapping the option all the time # optimisations @@ -18,6 +20,8 @@ - replace `RangeBounds` with `K` where applicatble in docs - replace rust types URL links with direct rust links +- add a # Panics section to every method that can panic, probably most + given invalid RangeBounds # features @@ -33,7 +37,7 @@ - should we implement FromIterator? If so which insert should we use? (At the moment we do implement it using insert_platonic()) -- should append_* functions not change the base if they fail half way? +- should append\_\* functions not change the base if they fail half way? #### PUBLISH