use core::{
convert::Infallible,
fmt,
hash::Hash,
mem,
ops::{RangeFrom, RangeTo},
};
use crate::{
Strategy,
accum::{Accum, AccumObj, Finished},
side::Side,
strategy::StrategyObj,
};
#[non_exhaustive]
pub struct CapacityExhausted<'a, A: ?Sized + AccumObj> {
pub item: A::Item,
pub side: Side,
pub accum: &'a A,
}
impl<'a, A: Accum> CapacityExhausted<'a, A> {
pub fn erased_accum(self) -> CapacityExhausted<'a, dyn 'a + AccumObj<Item = A::Item>> {
CapacityExhausted {
item: self.item,
side: self.side,
accum: self.accum,
}
}
}
impl<A: ?Sized + AccumObj<Item: Copy>> Copy for CapacityExhausted<'_, A> {}
impl<A: ?Sized + AccumObj<Item: Clone>> Clone for CapacityExhausted<'_, A> {
#[inline]
fn clone(&self) -> Self {
CapacityExhausted {
item: self.item.clone(),
side: self.side,
accum: self.accum,
}
}
}
impl<'a, A: PartialEq + AccumObj<Item: PartialEq>> PartialEq for CapacityExhausted<'a, A> {
#[inline]
fn eq(&self, rhs: &CapacityExhausted<'a, A>) -> bool {
self.item == rhs.item && self.side == rhs.side && self.accum == rhs.accum
}
}
impl<A: Eq + AccumObj<Item: Eq>> Eq for CapacityExhausted<'_, A> {}
impl<A: Hash + AccumObj<Item: Hash>> Hash for CapacityExhausted<'_, A> {
#[inline]
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
(&self.item, self.side, self.accum).hash(state);
}
}
impl<A: fmt::Debug + AccumObj<Item: fmt::Debug>> fmt::Debug for CapacityExhausted<'_, A> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("CapacityExhausted")
.field("item", &self.item)
.field("side", &self.side)
.field("accum", &self.accum)
.finish_non_exhaustive()
}
}
impl<'a, A: ?Sized + Accum> From<Infallible> for CapacityExhausted<'a, A> {
#[inline]
fn from(err: Infallible) -> CapacityExhausted<'a, A> {
match err {}
}
}
impl<A: ?Sized + AccumObj<Item: fmt::Debug>> fmt::Display for CapacityExhausted<'_, A> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "capacity exhausted: ")?;
let mut list = f.debug_list();
list.entries((0..self.accum.len_forward()).filter_map(|idx| self.accum.get_forward(idx)));
if self.side == Side::Backward {
list.entry(&format_args!(".."));
}
list.entry(&format_args!("(no room for {:?})", self.item));
if self.side == Side::Forward {
list.entry(&format_args!(".."));
}
list.entries(
(0..self.accum.len_backward())
.rev()
.filter_map(|idx| self.accum.get_backward(idx)),
);
list.finish()
}
}
pub struct FailedStrategy<'a, T, S: ?Sized, V> {
pub(crate) side: Side,
pub(crate) forward: usize,
pub(crate) backward: usize,
pub(crate) accum: Finished<'a, T>,
pub strategy: &'a S,
pub cause: V,
}
impl<'a, T, S: Strategy, V> FailedStrategy<'a, T, S, V> {
#[inline]
pub fn erased_strategy(self) -> FailedStrategy<'a, T, dyn 'a + StrategyObj, V> {
FailedStrategy {
side: self.side,
forward: self.forward,
backward: self.backward,
accum: self.accum,
strategy: self.strategy,
cause: self.cause,
}
}
}
impl<'a, T, S: ?Sized, V> FailedStrategy<'a, T, S, V> {
#[inline]
pub fn empty(strategy: &'a S, cause: V) -> FailedStrategy<'a, T, S, V> {
FailedStrategy {
side: Side::Forward,
forward: 0,
backward: 0,
accum: Finished::empty(),
strategy,
cause,
}
}
#[inline]
pub fn map<U, F: FnOnce(V) -> U>(self, f: F) -> FailedStrategy<'a, T, S, U> {
FailedStrategy {
side: self.side,
forward: self.forward,
backward: self.backward,
accum: self.accum,
strategy: self.strategy,
cause: f(self.cause),
}
}
#[inline]
pub fn index(&self) -> Option<usize> {
match self.side {
Side::Forward => self.forward.checked_sub(1),
Side::Backward => Some(self.backward),
}
}
#[inline]
pub fn valid_forward(&self) -> RangeTo<usize> {
..match self.side {
Side::Forward => self.forward.saturating_sub(1),
Side::Backward => self.forward,
}
}
#[inline]
pub fn valid_backward(&self) -> RangeFrom<usize> {
(match self.side {
Side::Forward => self.backward,
Side::Backward => self.backward + 1,
})..
}
}
impl<T, S: ?Sized, V: Copy> Copy for FailedStrategy<'_, T, S, V> {}
impl<T, S: ?Sized, V: Clone> Clone for FailedStrategy<'_, T, S, V> {
#[inline]
fn clone(&self) -> Self {
FailedStrategy {
side: self.side,
forward: self.forward,
backward: self.backward,
accum: self.accum,
strategy: self.strategy,
cause: self.cause.clone(),
}
}
}
impl<'a, T: PartialEq, S: ?Sized + PartialEq, V: PartialEq> PartialEq
for FailedStrategy<'a, T, S, V>
{
#[inline]
fn eq(&self, rhs: &FailedStrategy<'a, T, S, V>) -> bool {
self.side == rhs.side
&& self.forward == rhs.forward
&& self.backward == rhs.backward
&& self.accum == rhs.accum
&& self.strategy == rhs.strategy
&& self.cause == rhs.cause
}
}
impl<T: Eq, S: ?Sized + Eq, V: Eq> Eq for FailedStrategy<'_, T, S, V> {}
impl<T: Hash, S: ?Sized + Hash, V: Hash> Hash for FailedStrategy<'_, T, S, V> {
#[inline]
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
(
self.side,
self.forward,
self.backward,
self.accum,
self.strategy,
&self.cause,
)
.hash(state);
}
}
impl<T: fmt::Debug, S: ?Sized + fmt::Debug, V: fmt::Debug> fmt::Debug
for FailedStrategy<'_, T, S, V>
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("FailedStrategy")
.field("valid_forward", &self.valid_forward())
.field("index", &self.index())
.field("valid_backward", &self.valid_backward())
.field("accum", &self.accum)
.field("strategy", &self.strategy)
.field("cause", &self.cause)
.finish_non_exhaustive()
}
}
impl<'a, T, S: ?Sized, V> From<Infallible> for FailedStrategy<'a, T, S, V> {
#[inline]
fn from(err: Infallible) -> FailedStrategy<'a, T, S, V> {
match err {}
}
}
impl<T: fmt::Debug, S: ?Sized + fmt::Display, V: fmt::Display> fmt::Display
for FailedStrategy<'_, T, S, V>
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, r#"failed strategy "{}": "#, self.strategy)?;
let mut list = f.debug_list();
let fwd = self.valid_forward();
let bwd = self.valid_backward();
list.entries(&self.accum.as_slice()[fwd]);
if self.side == Side::Backward {
list.entry(&..);
}
if let Some(idx) = self.index() {
list.entry(&format_args!(
"(unexpected {:?})",
self.accum.get_unsigned(idx).unwrap()
));
}
if self.side == Side::Forward {
list.entry(&..);
}
list.entries(&self.accum.as_slice()[bwd]);
list.finish()?;
write!(f, " due to verification error: {}", self.cause)
}
}
pub enum AccumVerifyError<'a, A: ?Sized + AccumObj, S: ?Sized, V> {
Accum(CapacityExhausted<'a, A>),
Verify(FailedStrategy<'a, A::Item, S, V>),
}
impl<'a, A: ?Sized + Accum, S: Strategy, V> AccumVerifyError<'a, A, S, V> {
pub fn erased_strategy(self) -> AccumVerifyError<'a, A, dyn 'a + StrategyObj, V> {
match self {
AccumVerifyError::Accum(err) => AccumVerifyError::Accum(err),
AccumVerifyError::Verify(err) => AccumVerifyError::Verify(err.erased_strategy()),
}
}
}
impl<'a, A: Accum, S: ?Sized + Strategy, V> AccumVerifyError<'a, A, S, V> {
pub fn erased_accum(self) -> AccumVerifyError<'a, dyn 'a + AccumObj<Item = A::Item>, S, V> {
match self {
AccumVerifyError::Accum(err) => AccumVerifyError::Accum(err.erased_accum()),
AccumVerifyError::Verify(err) => AccumVerifyError::Verify(err),
}
}
}
impl<'a, A: ?Sized + Accum, S: ?Sized + Strategy, V> AccumVerifyError<'a, A, S, V> {
#[inline]
pub fn map<U, F: FnOnce(V) -> U>(self, f: F) -> AccumVerifyError<'a, A, S, U> {
match self {
AccumVerifyError::Accum(err) => AccumVerifyError::Accum(err),
AccumVerifyError::Verify(err) => AccumVerifyError::Verify(err.map(f)),
}
}
}
impl<A: Copy + Accum<Item: Copy>, S: ?Sized, V: Copy> Copy for AccumVerifyError<'_, A, S, V> {}
impl<A: ?Sized + Accum<Item: Clone>, S: ?Sized, V: Clone> Clone for AccumVerifyError<'_, A, S, V> {
fn clone(&self) -> Self {
match self {
AccumVerifyError::Accum(err) => AccumVerifyError::Accum(err.clone()),
AccumVerifyError::Verify(err) => AccumVerifyError::Verify(err.clone()),
}
}
}
impl<'a, A: PartialEq + Accum<Item: PartialEq>, S: ?Sized + PartialEq, V: PartialEq> PartialEq
for AccumVerifyError<'a, A, S, V>
{
#[inline]
fn eq(&self, rhs: &AccumVerifyError<'a, A, S, V>) -> bool {
match (self, rhs) {
(AccumVerifyError::Accum(lhs), AccumVerifyError::Accum(rhs)) => lhs == rhs,
(AccumVerifyError::Verify(lhs), AccumVerifyError::Verify(rhs)) => lhs == rhs,
_ => false,
}
}
}
impl<A: Eq + Accum<Item: Eq>, S: ?Sized + Eq, V: Eq> Eq for AccumVerifyError<'_, A, S, V> {}
impl<A: Hash + Accum<Item: Hash>, S: ?Sized + Hash, V: Hash> Hash
for AccumVerifyError<'_, A, S, V>
{
#[inline]
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
mem::discriminant(self).hash(state);
match self {
AccumVerifyError::Accum(err) => err.hash(state),
AccumVerifyError::Verify(err) => err.hash(state),
}
}
}
impl<A: fmt::Debug + Accum<Item: fmt::Debug>, S: ?Sized + fmt::Debug, V: fmt::Debug> fmt::Debug
for AccumVerifyError<'_, A, S, V>
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
AccumVerifyError::Accum(err) => fmt::Debug::fmt(err, f),
AccumVerifyError::Verify(err) => fmt::Debug::fmt(err, f),
}
}
}
impl<A: Accum<Item: fmt::Debug>, S: ?Sized + fmt::Display, V: fmt::Display> fmt::Display
for AccumVerifyError<'_, A, S, V>
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
AccumVerifyError::Accum(err) => fmt::Display::fmt(err, f),
AccumVerifyError::Verify(err) => fmt::Display::fmt(err, f),
}
}
}
impl<'a, A: ?Sized + Accum, S: ?Sized, V> From<Infallible> for AccumVerifyError<'a, A, S, V> {
#[inline]
fn from(err: Infallible) -> AccumVerifyError<'a, A, S, V> {
match err {}
}
}
impl<'a, A: ?Sized + Accum, S: ?Sized, V> From<CapacityExhausted<'a, A>>
for AccumVerifyError<'a, A, S, V>
{
#[inline]
fn from(err: CapacityExhausted<'a, A>) -> AccumVerifyError<'a, A, S, V> {
AccumVerifyError::Accum(err)
}
}
impl<'a, A: ?Sized + Accum, S: ?Sized, V> From<FailedStrategy<'a, A::Item, S, V>>
for AccumVerifyError<'a, A, S, V>
{
#[inline]
fn from(err: FailedStrategy<'a, A::Item, S, V>) -> AccumVerifyError<'a, A, S, V> {
AccumVerifyError::Verify(err)
}
}
pub type SuiteError<'a, A, V> = AccumVerifyError<'a, A, dyn 'a + StrategyObj, V>;
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub enum Inconsistent<'a, T> {
Empty,
Unequal(Unequal<'a, T>),
}
impl<T: fmt::Debug> fmt::Display for Inconsistent<'_, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Inconsistent::Empty => write!(f, "no data was returned by iterator"),
Inconsistent::Unequal(err) => fmt::Display::fmt(err, f),
}
}
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
#[non_exhaustive]
pub struct Unequal<'a, T> {
pub expected: Option<&'a T>,
pub actual: &'a T,
}
impl<T: fmt::Debug> fmt::Display for Unequal<'_, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let actual = self.actual;
match self.expected {
Some(expected) => write!(f, "{actual:?} != {expected:?}"),
None => write!(f, "extraneous value {actual:?}"),
}
}
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub struct InvalidPair<'a, T> {
pub pair: (&'a T, &'a T),
pub(crate) index_is_negative: bool,
}
impl<T: fmt::Debug> fmt::Display for InvalidPair<'_, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let (lo, hi) = self.pair;
if self.index_is_negative {
write!(
f,
"sequence (invalid {lo:?}, {hi:?}) didn't satisfy requirements"
)
} else {
write!(
f,
"sequence ({lo:?}, invalid {hi:?}) didn't satisfy requirements"
)
}
}
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
#[non_exhaustive]
pub struct InvalidIndex<'a, T> {
pub item: &'a T,
pub index: usize,
}
impl<T: fmt::Debug> fmt::Display for InvalidIndex<'_, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let item = self.item;
let index = self.index;
write!(
f,
"item {item:?} at index {index:?} didn't satisfy requirements"
)
}
}
#[cfg(test)]
mod tests {
use crate::{
accum::{Accum, Bounded, Finished},
error::{CapacityExhausted, FailedStrategy},
side::Side,
strategy::Todo,
};
const SLICE: Finished<'static, u32> = Finished::new(&[1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 5);
#[test]
fn failed_insert_fwd() {
let error: CapacityExhausted<Bounded<u32, 10>> = CapacityExhausted {
item: 4,
side: Side::Forward,
accum: &Bounded::with_data(&[1, 2, 3], &[8, 9, 10]),
};
assert_eq!(
error.to_string(),
"capacity exhausted: [1, 2, 3, (no room for 4), .., 8, 9, 10]"
);
}
#[test]
fn failed_insert_bwd() {
let error: CapacityExhausted<Bounded<u32, 10>> = CapacityExhausted {
item: 7,
side: Side::Backward,
accum: &Bounded::with_data(&[1, 2, 3], &[8, 9, 10]),
};
assert_eq!(
error.to_string(),
"capacity exhausted: [1, 2, 3, .., (no room for 7), 8, 9, 10]"
);
}
#[test]
fn failed_empty() {
let error: FailedStrategy<u32, Todo, &str> = FailedStrategy::empty(&Todo, "bad");
assert_eq!(
error.to_string(),
r#"failed strategy "unimplemented": [..] due to verification error: bad"#
);
}
#[test]
fn failed_empty_fwd_empty() {
let error: FailedStrategy<u32, Todo, &str> = FailedStrategy {
side: Side::Forward,
forward: 1,
backward: 10,
cause: "bad",
accum: SLICE,
strategy: &Todo,
};
assert_eq!(
error.to_string(),
r#"failed strategy "unimplemented": [(unexpected 1), ..] due to verification error: bad"#
);
}
#[test]
fn failed_empty_bwd_empty() {
let error: FailedStrategy<u32, Todo, &str> = FailedStrategy {
side: Side::Backward,
forward: 0,
backward: 9,
cause: "bad",
accum: SLICE,
strategy: &Todo,
};
assert_eq!(
error.to_string(),
r#"failed strategy "unimplemented": [.., (unexpected 10)] due to verification error: bad"#
);
}
#[test]
fn failed_nonempty_fwd_empty() {
let error: FailedStrategy<u32, Todo, &str> = FailedStrategy {
side: Side::Forward,
forward: 3,
backward: 10,
cause: "bad",
accum: SLICE,
strategy: &Todo,
};
assert_eq!(
error.to_string(),
r#"failed strategy "unimplemented": [1, 2, (unexpected 3), ..] due to verification error: bad"#
);
}
#[test]
fn failed_nonempty_bwd_empty() {
let error: FailedStrategy<u32, Todo, &str> = FailedStrategy {
side: Side::Backward,
forward: 3,
backward: 9,
cause: "bad",
accum: SLICE,
strategy: &Todo,
};
assert_eq!(
error.to_string(),
r#"failed strategy "unimplemented": [1, 2, 3, .., (unexpected 10)] due to verification error: bad"#
);
}
#[test]
fn failed_empty_fwd_nonempty() {
let error: FailedStrategy<u32, Todo, &str> = FailedStrategy {
side: Side::Forward,
forward: 1,
backward: 7,
cause: "bad",
accum: SLICE,
strategy: &Todo,
};
assert_eq!(
error.to_string(),
r#"failed strategy "unimplemented": [(unexpected 1), .., 8, 9, 10] due to verification error: bad"#
);
}
#[test]
fn failed_empty_bwd_nonempty() {
let error: FailedStrategy<u32, Todo, &str> = FailedStrategy {
side: Side::Backward,
forward: 0,
backward: 7,
cause: "bad",
accum: SLICE,
strategy: &Todo,
};
assert_eq!(
error.to_string(),
r#"failed strategy "unimplemented": [.., (unexpected 8), 9, 10] due to verification error: bad"#
);
}
#[test]
fn failed_nonempty_fwd_nonempty() {
let error: FailedStrategy<u32, Todo, &str> = FailedStrategy {
side: Side::Forward,
forward: 3,
backward: 7,
cause: "bad",
accum: SLICE,
strategy: &Todo,
};
assert_eq!(
error.to_string(),
r#"failed strategy "unimplemented": [1, 2, (unexpected 3), .., 8, 9, 10] due to verification error: bad"#
);
}
#[test]
fn failed_nonempty_bwd_nonempty() {
let error: FailedStrategy<u32, Todo, &str> = FailedStrategy {
side: Side::Backward,
forward: 3,
backward: 7,
cause: "bad",
accum: SLICE,
strategy: &Todo,
};
assert_eq!(
error.to_string(),
r#"failed strategy "unimplemented": [1, 2, 3, .., (unexpected 8), 9, 10] due to verification error: bad"#
);
}
}