added cut tests for atomnicity and fixed them
This commit is contained in:
parent
87ed4ad4e3
commit
47a1c425ff
@ -178,9 +178,9 @@ pub struct OverlapError;
|
||||
/// In this example we use a `RangeBounds` type that can be either
|
||||
/// Inclusive-Inclusive OR Exlusive-Exlusive. We then try to use
|
||||
/// [`RangeBoundsMap::insert_coalesce_touching()`] to "coalesce" an
|
||||
/// Inclusive-Inclusive and a Exlusive-Exlusive `MultiRange`. This
|
||||
/// Inclusive-Inclusive and a Exlusive-Exlusive `MultiBounds`. This
|
||||
/// will however fail as the resulting "coalesced" `RangeBounds` would
|
||||
/// have to be Inclusive-Exlusive which `MultiRange` does not support.
|
||||
/// have to be Inclusive-Exlusive which `MultiBounds` does not support.
|
||||
///
|
||||
/// ```
|
||||
/// use std::ops::{Bound, RangeBounds};
|
||||
@ -191,41 +191,45 @@ pub struct OverlapError;
|
||||
/// };
|
||||
///
|
||||
/// #[derive(Debug, PartialEq)]
|
||||
/// enum MultiRange {
|
||||
/// enum MultiBounds {
|
||||
/// Inclusive(u8, u8),
|
||||
/// Exclusive(u8, u8),
|
||||
/// }
|
||||
///
|
||||
/// impl RangeBounds<u8> for MultiRange {
|
||||
/// impl RangeBounds<u8> for MultiBounds {
|
||||
/// fn start_bound(&self) -> Bound<&u8> {
|
||||
/// match self {
|
||||
/// MultiRange::Inclusive(start, _) => {
|
||||
/// MultiBounds::Inclusive(start, _) => {
|
||||
/// Bound::Included(start)
|
||||
/// }
|
||||
/// MultiRange::Exclusive(start, _) => {
|
||||
/// MultiBounds::Exclusive(start, _) => {
|
||||
/// Bound::Excluded(start)
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// fn end_bound(&self) -> Bound<&u8> {
|
||||
/// match self {
|
||||
/// MultiRange::Inclusive(_, end) => Bound::Included(end),
|
||||
/// MultiRange::Exclusive(_, end) => Bound::Excluded(end),
|
||||
/// MultiBounds::Inclusive(_, end) => {
|
||||
/// Bound::Included(end)
|
||||
/// }
|
||||
/// MultiBounds::Exclusive(_, end) => {
|
||||
/// Bound::Excluded(end)
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// impl TryFromBounds<u8> for MultiRange {
|
||||
/// impl TryFromBounds<u8> for MultiBounds {
|
||||
/// fn try_from_bounds(
|
||||
/// start_bound: Bound<u8>,
|
||||
/// end_bound: Bound<u8>,
|
||||
/// ) -> Option<Self> {
|
||||
/// match (start_bound, end_bound) {
|
||||
/// (Bound::Included(start), Bound::Included(end)) => {
|
||||
/// Some(MultiRange::Inclusive(start, end))
|
||||
/// Some(MultiBounds::Inclusive(start, end))
|
||||
/// }
|
||||
/// (Bound::Excluded(start), Bound::Excluded(end)) => {
|
||||
/// Some(MultiRange::Exclusive(start, end))
|
||||
/// Some(MultiBounds::Exclusive(start, end))
|
||||
/// }
|
||||
/// _ => None,
|
||||
/// }
|
||||
@ -233,14 +237,14 @@ pub struct OverlapError;
|
||||
/// }
|
||||
///
|
||||
/// let mut range_bounds_map = RangeBoundsMap::try_from([(
|
||||
/// MultiRange::Inclusive(2, 4),
|
||||
/// MultiBounds::Inclusive(2, 4),
|
||||
/// true,
|
||||
/// )])
|
||||
/// .unwrap();
|
||||
///
|
||||
/// assert_eq!(
|
||||
/// range_bounds_map.insert_coalesce_touching(
|
||||
/// MultiRange::Exclusive(4, 6),
|
||||
/// MultiBounds::Exclusive(4, 6),
|
||||
/// false
|
||||
/// ),
|
||||
/// Err(OverlapOrTryFromBoundsError::TryFromBounds(
|
||||
@ -673,65 +677,66 @@ where
|
||||
K: TryFromBounds<I>,
|
||||
V: Clone,
|
||||
{
|
||||
// only the first and last range_bounds in overlapping stand a
|
||||
// change of remaining after the cut so we don't need to
|
||||
// collect the iterator and can just look at the first and
|
||||
// last elements since range is a double ended iterator ;p
|
||||
let mut overlapping = self.remove_overlapping(range_bounds);
|
||||
let mut to_insert = Vec::new();
|
||||
|
||||
let first_last = (overlapping.next(), overlapping.next_back());
|
||||
{
|
||||
// only the first and last range_bounds in overlapping stand a
|
||||
// change of remaining after the cut so we don't need to
|
||||
// collect the iterator and can just look at the first and
|
||||
// last elements since range is a double ended iterator ;p
|
||||
let mut overlapping = self.overlapping(range_bounds);
|
||||
|
||||
// optimisation don't clone the value when only changing the
|
||||
// RangeBounds via CutResult::Single()
|
||||
let mut attempt_insert_platonic =
|
||||
|(start_bound, end_bound),
|
||||
value|
|
||||
-> Result<(), TryFromBoundsError> {
|
||||
match K::try_from_bounds(start_bound, end_bound)
|
||||
.ok_or(TryFromBoundsError)
|
||||
{
|
||||
Ok(key) => {
|
||||
self.insert_platonic(key, value).unwrap();
|
||||
return Ok(());
|
||||
}
|
||||
Err(cut_error) => return Err(cut_error),
|
||||
}
|
||||
};
|
||||
let first_last = (overlapping.next(), overlapping.next_back());
|
||||
|
||||
match first_last {
|
||||
(Some(first), Some(last)) => {
|
||||
match cut_range_bounds(&first.0, range_bounds) {
|
||||
CutResult::Nothing => {}
|
||||
CutResult::Single(left_section) => {
|
||||
attempt_insert_platonic(left_section, first.1)?;
|
||||
match first_last {
|
||||
(Some(first), Some(last)) => {
|
||||
match cut_range_bounds(first.0, range_bounds) {
|
||||
CutResult::Nothing => {}
|
||||
CutResult::Single(left_section) => {
|
||||
to_insert.push((left_section, first.1.clone()));
|
||||
}
|
||||
CutResult::Double(_, _) => unreachable!(),
|
||||
}
|
||||
CutResult::Double(_, _) => unreachable!(),
|
||||
}
|
||||
match cut_range_bounds(&last.0, range_bounds) {
|
||||
CutResult::Nothing => {}
|
||||
CutResult::Single(right_section) => {
|
||||
attempt_insert_platonic(right_section, last.1)?;
|
||||
match cut_range_bounds(last.0, range_bounds) {
|
||||
CutResult::Nothing => {}
|
||||
CutResult::Single(right_section) => {
|
||||
to_insert.push((right_section, last.1.clone()));
|
||||
}
|
||||
CutResult::Double(_, _) => unreachable!(),
|
||||
}
|
||||
CutResult::Double(_, _) => unreachable!(),
|
||||
}
|
||||
(Some(first), None) => {
|
||||
match cut_range_bounds(first.0, range_bounds) {
|
||||
CutResult::Nothing => {}
|
||||
CutResult::Single(section) => {
|
||||
to_insert.push((section, first.1.clone()));
|
||||
}
|
||||
CutResult::Double(left_section, right_section) => {
|
||||
to_insert.push((left_section, first.1.clone()));
|
||||
to_insert.push((right_section, first.1.clone()));
|
||||
}
|
||||
}
|
||||
}
|
||||
(None, None) => {}
|
||||
(None, Some(_)) => unreachable!(),
|
||||
}
|
||||
(Some(first), None) => {
|
||||
match cut_range_bounds(&first.0, range_bounds) {
|
||||
CutResult::Nothing => {}
|
||||
CutResult::Single(section) => {
|
||||
attempt_insert_platonic(section, first.1)?;
|
||||
}
|
||||
CutResult::Double(left_section, right_section) => {
|
||||
attempt_insert_platonic(left_section, first.1.clone())?;
|
||||
attempt_insert_platonic(right_section, first.1)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
(None, None) => {}
|
||||
(None, Some(_)) => unreachable!(),
|
||||
}
|
||||
|
||||
return Ok(());
|
||||
// Make sure that the inserts will work before we try to do
|
||||
// them, so if one fails the map remains unchanged
|
||||
if to_insert.iter().all(|(x, _)| K::is_valid(x)) {
|
||||
self.remove_overlapping(range_bounds).next();
|
||||
for ((start, end), value) in to_insert.into_iter() {
|
||||
self.insert_platonic(
|
||||
K::try_from_bounds(start, end).unwrap(),
|
||||
value.clone(),
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
return Ok(());
|
||||
} else {
|
||||
return Err(TryFromBoundsError);
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns an iterator of `(Bound<&I>, Bound<&I>)` over all the
|
||||
@ -1395,6 +1400,52 @@ mod tests {
|
||||
pub(crate) const NUMBERS_DOMAIN: &'static [u8] =
|
||||
&[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11];
|
||||
|
||||
fn basic() -> RangeBoundsMap<u8, TestBounds, bool> {
|
||||
RangeBoundsMap::try_from([
|
||||
(ui(4), false),
|
||||
(ee(5, 7), true),
|
||||
(ii(7, 7), false),
|
||||
(ie(14, 16), true),
|
||||
])
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
enum MultiBounds {
|
||||
Inclusive(u8, u8),
|
||||
Exclusive(u8, u8),
|
||||
}
|
||||
impl RangeBounds<u8> for MultiBounds {
|
||||
fn start_bound(&self) -> Bound<&u8> {
|
||||
match self {
|
||||
MultiBounds::Inclusive(start, _) => Bound::Included(start),
|
||||
MultiBounds::Exclusive(start, _) => Bound::Excluded(start),
|
||||
}
|
||||
}
|
||||
fn end_bound(&self) -> Bound<&u8> {
|
||||
match self {
|
||||
MultiBounds::Inclusive(_, end) => Bound::Included(end),
|
||||
MultiBounds::Exclusive(_, end) => Bound::Excluded(end),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl TryFromBounds<u8> for MultiBounds {
|
||||
fn try_from_bounds(
|
||||
start_bound: Bound<u8>,
|
||||
end_bound: Bound<u8>,
|
||||
) -> Option<Self> {
|
||||
match (start_bound, end_bound) {
|
||||
(Bound::Included(start), Bound::Included(end)) => {
|
||||
Some(MultiBounds::Inclusive(start, end))
|
||||
}
|
||||
(Bound::Excluded(start), Bound::Excluded(end)) => {
|
||||
Some(MultiBounds::Exclusive(start, end))
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[rustfmt::skip]
|
||||
#[test]
|
||||
fn insert_platonic_tests() {
|
||||
@ -1555,10 +1606,18 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
fn special() -> RangeBoundsMap<u8, MultiBounds, bool> {
|
||||
RangeBoundsMap::try_from([
|
||||
(MultiBounds::Inclusive(4, 6), false),
|
||||
(MultiBounds::Exclusive(7, 8), true),
|
||||
(MultiBounds::Inclusive(8, 12), false),
|
||||
])
|
||||
.unwrap()
|
||||
}
|
||||
#[rustfmt::skip]
|
||||
#[test]
|
||||
fn cut_tests() {
|
||||
assert_cut::<0>(basic(), ii(50, 60), Ok(()), None);
|
||||
assert_cut(basic(), ii(50, 60), Ok(()), None::<[_; 0]>);
|
||||
assert_cut(basic(), uu(), Ok(()), Some([]));
|
||||
assert_cut(basic(), ui(6), Ok(()), Some([
|
||||
(ee(6, 7), true),
|
||||
@ -1569,13 +1628,39 @@ mod tests {
|
||||
(ui(4), false),
|
||||
(ee(5, 6), true),
|
||||
]));
|
||||
|
||||
assert_cut(special(), MultiBounds::Exclusive(5, 7), Ok(()), Some([
|
||||
(MultiBounds::Inclusive(4, 5), false),
|
||||
(MultiBounds::Exclusive(7, 8), true),
|
||||
(MultiBounds::Inclusive(8, 12), false),
|
||||
]));
|
||||
assert_cut(special(), MultiBounds::Exclusive(6, 7), Ok(()), None::<[_; 0]>);
|
||||
assert_cut(special(), MultiBounds::Inclusive(5, 6), Err(TryFromBoundsError), None::<[_; 0]>);
|
||||
assert_cut(special(), MultiBounds::Inclusive(6, 7), Err(TryFromBoundsError), None::<[_; 0]>);
|
||||
assert_cut(special(), 7..8, Ok(()), Some([
|
||||
(MultiBounds::Inclusive(4, 6), false),
|
||||
(MultiBounds::Inclusive(8, 12), false),
|
||||
]));
|
||||
assert_cut(special(), MultiBounds::Inclusive(7, 10), Err(TryFromBoundsError), None::<[_; 0]>);
|
||||
assert_cut(special(), MultiBounds::Exclusive(4, 6), Ok(()), Some([
|
||||
(MultiBounds::Inclusive(4, 4), false),
|
||||
(MultiBounds::Inclusive(6, 6), false),
|
||||
(MultiBounds::Exclusive(7, 8), true),
|
||||
(MultiBounds::Inclusive(8, 12), false),
|
||||
]));
|
||||
}
|
||||
fn assert_cut<const N: usize>(
|
||||
mut before: RangeBoundsMap<u8, TestBounds, bool>,
|
||||
to_cut: TestBounds,
|
||||
fn assert_cut<const N: usize, Q, I, K, V>(
|
||||
mut before: RangeBoundsMap<I, K, V>,
|
||||
to_cut: Q,
|
||||
result: Result<(), TryFromBoundsError>,
|
||||
after: Option<[(TestBounds, bool); N]>,
|
||||
) {
|
||||
after: Option<[(K, V); N]>,
|
||||
) where
|
||||
I: Ord + Clone + Debug,
|
||||
K: Clone + TryFromBounds<I> + Debug + PartialEq,
|
||||
V: Clone + Debug + PartialEq,
|
||||
K: RangeBounds<I>,
|
||||
Q: RangeBounds<I>,
|
||||
{
|
||||
let clone = before.clone();
|
||||
assert_eq!(before.cut(&to_cut), result);
|
||||
match after {
|
||||
@ -1613,16 +1698,6 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
fn basic() -> RangeBoundsMap<u8, TestBounds, bool> {
|
||||
RangeBoundsMap::try_from([
|
||||
(ui(4), false),
|
||||
(ee(5, 7), true),
|
||||
(ii(7, 7), false),
|
||||
(ie(14, 16), true),
|
||||
])
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
#[rustfmt::skip]
|
||||
#[test]
|
||||
fn insert_coalesce_touching_tests() {
|
||||
|
@ -18,7 +18,7 @@ along with range_bounds_map. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
use std::ops::{
|
||||
Bound, Range, RangeFrom, RangeFull, RangeInclusive, RangeTo,
|
||||
Bound, Range, RangeBounds, RangeFrom, RangeFull, RangeInclusive, RangeTo,
|
||||
RangeToInclusive,
|
||||
};
|
||||
|
||||
@ -37,17 +37,32 @@ use labels::{not_a_fn, trivial};
|
||||
/// [`TryFrom`]: https://doc.rust-lang.org/std/convert/trait.TryFrom.html
|
||||
/// [`Range`]: https://doc.rust-lang.org/std/ops/struct.Range.html
|
||||
pub trait TryFromBounds<I> {
|
||||
#[not_a_fn]
|
||||
#[not_a_fn]
|
||||
fn try_from_bounds(
|
||||
start_bound: Bound<I>,
|
||||
end_bound: Bound<I>,
|
||||
) -> Option<Self>
|
||||
where
|
||||
Self: Sized;
|
||||
// optimisation: make this non-implemented so the trait impls can
|
||||
// define them more efficiently
|
||||
#[not_a_fn]
|
||||
fn is_valid<Q>(range_bounds: &Q) -> bool
|
||||
where
|
||||
Q: RangeBounds<I>,
|
||||
Self: Sized,
|
||||
I: Clone,
|
||||
{
|
||||
Self::try_from_bounds(
|
||||
range_bounds.start_bound().cloned(),
|
||||
range_bounds.end_bound().cloned(),
|
||||
)
|
||||
.is_some()
|
||||
}
|
||||
}
|
||||
|
||||
impl<I> TryFromBounds<I> for (Bound<I>, Bound<I>) {
|
||||
#[trivial]
|
||||
#[trivial]
|
||||
fn try_from_bounds(
|
||||
start_bound: Bound<I>,
|
||||
end_bound: Bound<I>,
|
||||
@ -57,7 +72,7 @@ impl<I> TryFromBounds<I> for (Bound<I>, Bound<I>) {
|
||||
}
|
||||
|
||||
impl<I> TryFromBounds<I> for Range<I> {
|
||||
#[trivial]
|
||||
#[trivial]
|
||||
fn try_from_bounds(
|
||||
start_bound: Bound<I>,
|
||||
end_bound: Bound<I>,
|
||||
@ -70,7 +85,7 @@ impl<I> TryFromBounds<I> for Range<I> {
|
||||
}
|
||||
|
||||
impl<I> TryFromBounds<I> for RangeInclusive<I> {
|
||||
#[trivial]
|
||||
#[trivial]
|
||||
fn try_from_bounds(
|
||||
start_bound: Bound<I>,
|
||||
end_bound: Bound<I>,
|
||||
@ -83,7 +98,7 @@ impl<I> TryFromBounds<I> for RangeInclusive<I> {
|
||||
}
|
||||
|
||||
impl<I> TryFromBounds<I> for RangeFrom<I> {
|
||||
#[trivial]
|
||||
#[trivial]
|
||||
fn try_from_bounds(
|
||||
start_bound: Bound<I>,
|
||||
end_bound: Bound<I>,
|
||||
@ -96,7 +111,7 @@ impl<I> TryFromBounds<I> for RangeFrom<I> {
|
||||
}
|
||||
|
||||
impl<I> TryFromBounds<I> for RangeTo<I> {
|
||||
#[trivial]
|
||||
#[trivial]
|
||||
fn try_from_bounds(
|
||||
start_bound: Bound<I>,
|
||||
end_bound: Bound<I>,
|
||||
@ -109,7 +124,7 @@ impl<I> TryFromBounds<I> for RangeTo<I> {
|
||||
}
|
||||
|
||||
impl<I> TryFromBounds<I> for RangeToInclusive<I> {
|
||||
#[trivial]
|
||||
#[trivial]
|
||||
fn try_from_bounds(
|
||||
start_bound: Bound<I>,
|
||||
end_bound: Bound<I>,
|
||||
@ -122,7 +137,7 @@ impl<I> TryFromBounds<I> for RangeToInclusive<I> {
|
||||
}
|
||||
|
||||
impl<I> TryFromBounds<I> for RangeFull {
|
||||
#[trivial]
|
||||
#[trivial]
|
||||
fn try_from_bounds(
|
||||
start_bound: Bound<I>,
|
||||
end_bound: Bound<I>,
|
||||
|
8
todo.txt
8
todo.txt
@ -1,10 +1,15 @@
|
||||
# refactor
|
||||
- change <0> to use None::<[_;0]>
|
||||
- try and fix all the uses of cloned() in the library
|
||||
|
||||
# testing
|
||||
- test #[untested]'s
|
||||
- add tests for TryFromBounds fails for those that produce them with
|
||||
atomnicity tests
|
||||
|
||||
# features
|
||||
- RangeMap, RangeSet, RangeInclusiveMap...
|
||||
- add coalesce if same-value otherwise overwrite) function to make
|
||||
finally make range_bounds_map a superset of rangemap
|
||||
|
||||
# docs
|
||||
- write something somewhere about wrapper types for RangeBoundsMap
|
||||
@ -17,6 +22,7 @@
|
||||
- use it in robot_Sweet_graph for a bit before publishing
|
||||
|
||||
# final checks
|
||||
- remove most rustfmt::skips and cargo fmt
|
||||
- check toml meta-data, github meta-data and readme opener
|
||||
- copy map to set again
|
||||
- copy readme to lib.rs docs again
|
||||
|
Loading…
x
Reference in New Issue
Block a user