use std::error::Error;
use std::fmt::Display;
use std::ops::Bound;
use std::time::Duration as StdDuration;
#[cfg(feature = "arbitrary")]
use arbitrary::Arbitrary;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
pub trait Interval {}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
pub enum Openness {
Bounded,
HalfBounded,
Unbounded,
Empty,
}
impl Display for Openness {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Bounded => write!(f, "Bounded"),
Self::HalfBounded => write!(f, "Half-bounded"),
Self::Unbounded => write!(f, "Unbounded"),
Self::Empty => write!(f, "Empty"),
}
}
}
pub trait HasOpenness {
#[must_use]
fn openness(&self) -> Openness;
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
pub enum Relativity {
Absolute,
Relative,
Any,
}
impl Display for Relativity {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Absolute => write!(f, "Absolute"),
Self::Relative => write!(f, "Relative"),
Self::Any => write!(f, "Any relativity"),
}
}
}
pub trait HasRelativity {
#[must_use]
fn relativity(&self) -> Relativity;
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
pub enum OpeningDirection {
ToFuture,
ToPast,
}
impl OpeningDirection {
#[must_use]
pub fn opposite(&self) -> Self {
match self {
Self::ToFuture => OpeningDirection::ToPast,
Self::ToPast => OpeningDirection::ToFuture,
}
}
}
impl Display for OpeningDirection {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::ToFuture => write!(f, "Opening direction towards future"),
Self::ToPast => write!(f, "Opening direction towards past"),
}
}
}
impl From<bool> for OpeningDirection {
fn from(goes_to_future: bool) -> Self {
if goes_to_future {
OpeningDirection::ToFuture
} else {
OpeningDirection::ToPast
}
}
}
pub trait HasOpeningDirection {
#[must_use]
fn opening_direction(&self) -> OpeningDirection;
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
pub enum Epsilon {
#[default]
None,
Start,
End,
Both,
}
impl Epsilon {
#[must_use]
pub fn has_epsilon(&self) -> bool {
!matches!(self, Self::None)
}
#[must_use]
pub fn has_epsilon_on_start(&self) -> bool {
matches!(self, Self::Start | Self::Both)
}
#[must_use]
pub fn has_epsilon_on_end(&self) -> bool {
matches!(self, Self::End | Self::Both)
}
pub fn interpret_as_duration_bound_specific(
&self,
start_epsilon_duration: StdDuration,
end_epsilon_duration: StdDuration,
) -> Result<StdDuration, EpsilonInterpretationDurationOverflowError> {
match self {
Self::None => Ok(StdDuration::ZERO),
Self::Start => Ok(start_epsilon_duration),
Self::End => Ok(end_epsilon_duration),
Self::Both => start_epsilon_duration
.checked_add(end_epsilon_duration)
.ok_or(EpsilonInterpretationDurationOverflowError),
}
}
pub fn interpret_as_duration(
&self,
epsilon_duration: StdDuration,
) -> Result<StdDuration, EpsilonInterpretationDurationOverflowError> {
self.interpret_as_duration_bound_specific(epsilon_duration, epsilon_duration)
}
}
impl Display for Epsilon {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::None => write!(f, "No epsilons"),
Self::Start => write!(f, "Epsilon on start bound"),
Self::End => write!(f, "Epsilon on end bound"),
Self::Both => write!(f, "Epsilon on both bounds"),
}
}
}
impl From<(BoundInclusivity, BoundInclusivity)> for Epsilon {
fn from((start_inclusivity, end_inclusivity): (BoundInclusivity, BoundInclusivity)) -> Self {
match (start_inclusivity, end_inclusivity) {
(BoundInclusivity::Inclusive, BoundInclusivity::Inclusive) => Epsilon::None,
(BoundInclusivity::Exclusive, BoundInclusivity::Inclusive) => Epsilon::Start,
(BoundInclusivity::Inclusive, BoundInclusivity::Exclusive) => Epsilon::End,
(BoundInclusivity::Exclusive, BoundInclusivity::Exclusive) => Epsilon::Both,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct EpsilonInterpretationDurationOverflowError;
impl Display for EpsilonInterpretationDurationOverflowError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Epsilon interpretation made the duration overflow")
}
}
impl Error for EpsilonInterpretationDurationOverflowError {}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
pub enum Duration {
Finite(StdDuration, Epsilon),
Infinite,
}
impl Duration {
#[must_use]
pub fn is_finite(&self) -> bool {
matches!(self, Duration::Finite(..))
}
#[must_use]
pub fn finite(self) -> Option<(StdDuration, Epsilon)> {
match self {
Self::Finite(duration, epsilon) => Some((duration, epsilon)),
Self::Infinite => None,
}
}
#[must_use]
pub fn finite_interpret_epsilon(self, epsilon_duration: StdDuration) -> Option<StdDuration> {
let (duration, epsilon) = self.finite()?;
let interpreted_epsilon = epsilon.interpret_as_duration(epsilon_duration).ok()?;
Some(duration.checked_sub(interpreted_epsilon).unwrap_or(StdDuration::ZERO))
}
#[must_use]
pub fn finite_strip_epsilon(self) -> Option<StdDuration> {
Some(self.finite()?.0)
}
}
impl From<StdDuration> for Duration {
fn from(duration: StdDuration) -> Self {
Duration::Finite(duration, Epsilon::default())
}
}
impl From<(StdDuration, Epsilon)> for Duration {
fn from((duration, epsilon): (StdDuration, Epsilon)) -> Self {
Duration::Finite(duration, epsilon)
}
}
pub trait HasDuration {
fn duration(&self) -> Duration;
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Hash)]
#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
pub enum BoundInclusivity {
#[default]
Inclusive,
Exclusive,
}
impl BoundInclusivity {
#[must_use]
pub fn opposite(&self) -> BoundInclusivity {
match self {
BoundInclusivity::Inclusive => BoundInclusivity::Exclusive,
BoundInclusivity::Exclusive => BoundInclusivity::Inclusive,
}
}
#[must_use]
pub fn to_range_bound_with<T>(self, value: T) -> Bound<T> {
match self {
Self::Inclusive => Bound::Included(value),
Self::Exclusive => Bound::Excluded(value),
}
}
}
impl Display for BoundInclusivity {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Inclusive => write!(f, "Inclusive bound"),
Self::Exclusive => write!(f, "Exclusive bound"),
}
}
}
impl From<bool> for BoundInclusivity {
fn from(is_inclusive: bool) -> Self {
if is_inclusive {
BoundInclusivity::Inclusive
} else {
BoundInclusivity::Exclusive
}
}
}
pub trait HasBoundInclusivity {
#[must_use]
fn inclusivity(&self) -> BoundInclusivity;
}
pub trait IsEmpty {
fn is_empty(&self) -> bool;
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum BoundExtremality {
Start,
End,
}
impl BoundExtremality {
#[must_use]
pub fn opposite(self) -> Self {
match self {
Self::Start => Self::End,
Self::End => Self::Start,
}
}
}
pub trait HasBoundExtremality {
#[must_use]
fn bound_extremality(&self) -> BoundExtremality;
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum IntervalType {
Empty,
Bounded,
HalfBounded(OpeningDirection),
Unbounded,
}
impl IntervalType {
pub fn try_with_rel(
self,
relativity: Relativity,
) -> Result<IntervalTypeWithRel, IntervalTypeWithRelTryFromIntervalTypeAndRel> {
IntervalTypeWithRel::try_from((self, relativity))
}
}
impl IsEmpty for IntervalType {
fn is_empty(&self) -> bool {
matches!(self, Self::Empty)
}
}
impl HasOpenness for IntervalType {
fn openness(&self) -> Openness {
match self {
Self::Empty => Openness::Empty,
Self::Bounded => Openness::Bounded,
Self::HalfBounded(_) => Openness::HalfBounded,
Self::Unbounded => Openness::Unbounded,
}
}
}
impl From<IntervalTypeWithRel> for IntervalType {
fn from(value: IntervalTypeWithRel) -> Self {
match value {
IntervalTypeWithRel::Empty => Self::Empty,
IntervalTypeWithRel::AbsBounded | IntervalTypeWithRel::RelBounded => Self::Bounded,
IntervalTypeWithRel::AbsHalfBounded(opening_direction)
| IntervalTypeWithRel::RelHalfBounded(opening_direction) => Self::HalfBounded(opening_direction),
IntervalTypeWithRel::Unbounded => Self::Unbounded,
}
}
}
pub trait HasIntervalType {
#[must_use]
fn interval_type(&self) -> IntervalType;
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum IntervalTypeWithRel {
Empty,
AbsBounded,
RelBounded,
AbsHalfBounded(OpeningDirection),
RelHalfBounded(OpeningDirection),
Unbounded,
}
impl IsEmpty for IntervalTypeWithRel {
fn is_empty(&self) -> bool {
matches!(self, Self::Empty)
}
}
impl HasRelativity for IntervalTypeWithRel {
fn relativity(&self) -> Relativity {
match self {
Self::Empty | Self::Unbounded => Relativity::Any,
Self::AbsBounded | Self::AbsHalfBounded(_) => Relativity::Absolute,
Self::RelBounded | Self::RelHalfBounded(_) => Relativity::Relative,
}
}
}
impl HasOpenness for IntervalTypeWithRel {
fn openness(&self) -> Openness {
match self {
Self::Empty => Openness::Empty,
Self::AbsBounded | Self::RelBounded => Openness::Bounded,
Self::AbsHalfBounded(_) | Self::RelHalfBounded(_) => Openness::HalfBounded,
Self::Unbounded => Openness::Unbounded,
}
}
}
impl HasIntervalType for IntervalTypeWithRel {
fn interval_type(&self) -> IntervalType {
match self {
Self::Empty => IntervalType::Empty,
Self::AbsBounded | Self::RelBounded => IntervalType::Bounded,
Self::AbsHalfBounded(opening_direction) | Self::RelHalfBounded(opening_direction) => {
IntervalType::HalfBounded(*opening_direction)
},
Self::Unbounded => IntervalType::Unbounded,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct IntervalTypeWithRelTryFromIntervalTypeAndRel;
impl Display for IntervalTypeWithRelTryFromIntervalTypeAndRel {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"An error occurred when trying to convert `(IntervalType, Relativity)` into `IntervalTypeWithRel`"
)
}
}
impl Error for IntervalTypeWithRelTryFromIntervalTypeAndRel {}
impl TryFrom<(IntervalType, Relativity)> for IntervalTypeWithRel {
type Error = IntervalTypeWithRelTryFromIntervalTypeAndRel;
fn try_from(interval_type_and_rel: (IntervalType, Relativity)) -> Result<Self, Self::Error> {
match interval_type_and_rel {
(IntervalType::Empty, Relativity::Any) => Ok(Self::Empty),
(IntervalType::Bounded, Relativity::Absolute) => Ok(Self::AbsBounded),
(IntervalType::Bounded, Relativity::Relative) => Ok(Self::RelBounded),
(IntervalType::HalfBounded(opening_direction), Relativity::Absolute) => {
Ok(Self::AbsHalfBounded(opening_direction))
},
(IntervalType::HalfBounded(opening_direction), Relativity::Relative) => {
Ok(Self::RelHalfBounded(opening_direction))
},
(IntervalType::Unbounded, Relativity::Any) => Ok(Self::Unbounded),
_ => Err(IntervalTypeWithRelTryFromIntervalTypeAndRel),
}
}
}
pub trait HasIntervalTypeWithRel: HasIntervalType {
#[must_use]
fn interval_type_with_rel(&self) -> IntervalTypeWithRel;
}
impl<T: HasIntervalTypeWithRel> HasIntervalType for T {
fn interval_type(&self) -> IntervalType {
self.interval_type_with_rel().interval_type()
}
}