use std::cmp::Ordering;
use std::error::Error;
use std::fmt::Display;
use std::ops::RangeBounds;
use jiff::Timestamp;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use crate::intervals::absolute::{
AbsEndBound,
AbsInterval,
AbsStartBound,
BoundedAbsInterval,
EmptiableAbsBoundPair,
EmptiableAbsInterval,
HalfBoundedAbsInterval,
HasEmptiableAbsBoundPair,
check_abs_start_end_bounds_for_interval_creation,
prepare_abs_start_end_bounds_for_interval_creation,
};
use crate::intervals::meta::{
BoundInclusivity,
Duration as IntervalDuration,
Epsilon,
HasBoundInclusivity,
HasDuration,
HasIntervalTypeWithRel,
HasOpenness,
HasRelativity,
Interval,
IntervalTypeWithRel,
IsEmpty,
OpeningDirection,
Openness,
Relativity,
};
use crate::intervals::ops::{BoundOrd, BoundOverlapDisambiguationRuleSet};
use crate::intervals::special::UnboundedInterval;
pub trait HasAbsBoundPair: HasEmptiableAbsBoundPair {
#[must_use]
fn abs_bound_pair(&self) -> AbsBoundPair;
#[must_use]
fn abs_start(&self) -> AbsStartBound;
#[must_use]
fn abs_end(&self) -> AbsEndBound;
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
pub struct AbsBoundPair {
start: AbsStartBound,
end: AbsEndBound,
}
impl AbsBoundPair {
#[must_use]
pub fn unchecked_new(start: AbsStartBound, end: AbsEndBound) -> Self {
AbsBoundPair {
start,
end,
}
}
#[must_use]
pub fn new(mut start: AbsStartBound, mut end: AbsEndBound) -> Self {
prepare_abs_start_end_bounds_for_interval_creation(&mut start, &mut end);
Self::unchecked_new(start, end)
}
#[must_use]
pub fn from_range<R>(range: R) -> Self
where
R: RangeBounds<Timestamp>,
{
AbsBoundPair::new(
AbsStartBound::from(range.start_bound().cloned()),
AbsEndBound::from(range.end_bound().cloned()),
)
}
#[must_use]
pub fn start(&self) -> AbsStartBound {
self.start
}
#[must_use]
pub fn end(&self) -> AbsEndBound {
self.end
}
#[must_use]
pub fn ord_by_start_and_inv_length(&self, other: &Self) -> Ordering {
match self
.start()
.bound_cmp(&other.start())
.disambiguate(BoundOverlapDisambiguationRuleSet::Strict)
{
Ordering::Less => Ordering::Less,
Ordering::Equal => self
.end()
.bound_cmp(&other.end())
.disambiguate(BoundOverlapDisambiguationRuleSet::Strict)
.reverse(),
Ordering::Greater => Ordering::Greater,
}
}
pub fn unchecked_set_start(&mut self, new_start: AbsStartBound) {
self.start = new_start;
}
pub fn unchecked_set_end(&mut self, new_end: AbsEndBound) {
self.end = new_end;
}
pub fn set_start(&mut self, new_start: AbsStartBound) -> bool {
match check_abs_start_end_bounds_for_interval_creation(&new_start, &self.end()) {
Ok(()) => {
self.unchecked_set_start(new_start);
true
},
Err(_) => false,
}
}
pub fn set_end(&mut self, new_end: AbsEndBound) -> bool {
match check_abs_start_end_bounds_for_interval_creation(&self.start(), &new_end) {
Ok(()) => {
self.unchecked_set_end(new_end);
true
},
Err(_) => false,
}
}
#[must_use]
pub fn to_interval(self) -> AbsInterval {
AbsInterval::from(self)
}
#[must_use]
pub fn to_emptiable_interval(self) -> EmptiableAbsInterval {
self.to_interval().to_emptiable()
}
#[must_use]
pub fn to_emptiable(self) -> EmptiableAbsBoundPair {
EmptiableAbsBoundPair::from(self)
}
}
impl Interval for AbsBoundPair {}
impl HasAbsBoundPair for AbsBoundPair {
fn abs_bound_pair(&self) -> AbsBoundPair {
self.clone()
}
fn abs_start(&self) -> AbsStartBound {
self.start()
}
fn abs_end(&self) -> AbsEndBound {
self.end()
}
}
impl HasDuration for AbsBoundPair {
fn duration(&self) -> IntervalDuration {
match (self.start(), self.end()) {
(AbsStartBound::InfinitePast, _) | (_, AbsEndBound::InfiniteFuture) => IntervalDuration::Infinite,
(AbsStartBound::Finite(finite_start), AbsEndBound::Finite(finite_end)) => IntervalDuration::Finite(
finite_end
.pos()
.time()
.duration_since(finite_start.pos().time())
.unsigned_abs(),
Epsilon::from((finite_start.pos().inclusivity(), finite_end.pos().inclusivity())),
),
}
}
}
impl HasOpenness for AbsBoundPair {
fn openness(&self) -> Openness {
match (self.start(), self.end()) {
(AbsStartBound::InfinitePast, AbsEndBound::InfiniteFuture) => Openness::Unbounded,
(AbsStartBound::InfinitePast, AbsEndBound::Finite(_))
| (AbsStartBound::Finite(_), AbsEndBound::InfiniteFuture) => Openness::HalfBounded,
(AbsStartBound::Finite(_), AbsEndBound::Finite(_)) => Openness::Bounded,
}
}
}
impl HasRelativity for AbsBoundPair {
fn relativity(&self) -> Relativity {
match (self.start(), self.end()) {
(AbsStartBound::InfinitePast, AbsEndBound::InfiniteFuture) => Relativity::Any,
_ => Relativity::Absolute,
}
}
}
impl PartialOrd for AbsBoundPair {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for AbsBoundPair {
fn cmp(&self, other: &Self) -> Ordering {
self.start()
.bound_cmp(&other.start())
.disambiguate(BoundOverlapDisambiguationRuleSet::Strict)
}
}
impl IsEmpty for AbsBoundPair {
fn is_empty(&self) -> bool {
false
}
}
impl HasIntervalTypeWithRel for AbsBoundPair {
fn interval_type_with_rel(&self) -> IntervalTypeWithRel {
match (self.start(), self.end()) {
(AbsStartBound::InfinitePast, AbsEndBound::InfiniteFuture) => IntervalTypeWithRel::Unbounded,
(AbsStartBound::InfinitePast, AbsEndBound::Finite(_)) => {
IntervalTypeWithRel::AbsHalfBounded(OpeningDirection::ToPast)
},
(AbsStartBound::Finite(_), AbsEndBound::InfiniteFuture) => {
IntervalTypeWithRel::AbsHalfBounded(OpeningDirection::ToFuture)
},
(AbsStartBound::Finite(_), AbsEndBound::Finite(_)) => IntervalTypeWithRel::AbsBounded,
}
}
}
impl From<(Option<Timestamp>, Option<Timestamp>)> for AbsBoundPair {
fn from((start_opt, end_opt): (Option<Timestamp>, Option<Timestamp>)) -> Self {
let start = AbsStartBound::from(start_opt);
let end = AbsEndBound::from(end_opt);
AbsBoundPair::new(start, end)
}
}
impl
From<(
Option<(Timestamp, BoundInclusivity)>,
Option<(Timestamp, BoundInclusivity)>,
)> for AbsBoundPair
{
fn from(
(start_opt, end_opt): (
Option<(Timestamp, BoundInclusivity)>,
Option<(Timestamp, BoundInclusivity)>,
),
) -> Self {
let start = AbsStartBound::from(start_opt);
let end = AbsEndBound::from(end_opt);
Self::new(start, end)
}
}
impl From<BoundedAbsInterval> for AbsBoundPair {
fn from(value: BoundedAbsInterval) -> Self {
value.abs_bound_pair()
}
}
impl From<HalfBoundedAbsInterval> for AbsBoundPair {
fn from(value: HalfBoundedAbsInterval) -> Self {
value.abs_bound_pair()
}
}
impl From<AbsInterval> for AbsBoundPair {
fn from(value: AbsInterval) -> Self {
value.abs_bound_pair()
}
}
impl From<UnboundedInterval> for AbsBoundPair {
fn from(value: UnboundedInterval) -> Self {
value.abs_bound_pair()
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct AbsBoundPairTryFromEmptiableAbsBoundPairError;
impl Display for AbsBoundPairTryFromEmptiableAbsBoundPairError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"An error occurred when trying to convert `EmptiableAbsBoundPair` into `AbsBoundPair`"
)
}
}
impl Error for AbsBoundPairTryFromEmptiableAbsBoundPairError {}
impl TryFrom<EmptiableAbsBoundPair> for AbsBoundPair {
type Error = AbsBoundPairTryFromEmptiableAbsBoundPairError;
fn try_from(value: EmptiableAbsBoundPair) -> Result<Self, Self::Error> {
value.bound().ok_or(AbsBoundPairTryFromEmptiableAbsBoundPairError)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct AbsBoundPairTryFromEmptiableAbsIntervalError;
impl Display for AbsBoundPairTryFromEmptiableAbsIntervalError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"An error occurred when trying to convert `EmptiableAbsInterval` into `AbsBoundPair`"
)
}
}
impl Error for AbsBoundPairTryFromEmptiableAbsIntervalError {}
impl TryFrom<EmptiableAbsInterval> for AbsBoundPair {
type Error = AbsBoundPairTryFromEmptiableAbsIntervalError;
fn try_from(value: EmptiableAbsInterval) -> Result<Self, Self::Error> {
Ok(value
.bound()
.ok_or(AbsBoundPairTryFromEmptiableAbsIntervalError)?
.abs_bound_pair())
}
}