#[cfg(feature = "arbitrary")]
use arbitrary::Arbitrary;
use jiff::{SignedDuration, Timestamp};
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use super::point_containment::CanPositionPointContainment;
use crate::intervals::absolute::{
AbsBoundPair,
AbsFiniteBoundPos,
AbsInterval,
BoundedAbsInterval,
EmptiableAbsBoundPair,
EmptiableAbsInterval,
HalfBoundedAbsInterval,
HasAbsBoundPair,
HasEmptiableAbsBoundPair,
check_abs_start_end_bounds_for_interval_creation,
};
use crate::intervals::meta::BoundInclusivity;
use crate::intervals::relative::{
BoundedRelInterval,
EmptiableRelBoundPair,
EmptiableRelInterval,
HalfBoundedRelInterval,
HasEmptiableRelBoundPair,
HasRelBoundPair,
RelBoundPair,
RelFiniteBoundPos,
RelInterval,
check_rel_start_end_bounds_for_interval_creation,
};
use crate::intervals::special::{EmptyInterval, UnboundedInterval};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
pub struct CutType(BoundInclusivity, BoundInclusivity);
impl CutType {
#[must_use]
pub fn new(past_inclusivity: BoundInclusivity, future_inclusivity: BoundInclusivity) -> Self {
CutType(past_inclusivity, future_inclusivity)
}
#[must_use]
pub fn past_bound_inclusivity(&self) -> BoundInclusivity {
self.0
}
#[must_use]
pub fn future_bound_inclusivity(&self) -> BoundInclusivity {
self.1
}
#[must_use]
pub fn opposite(&self) -> Self {
CutType::new(self.0.opposite(), self.1.opposite())
}
}
impl From<(BoundInclusivity, BoundInclusivity)> for CutType {
fn from((past_inclusivity, future_inclusivity): (BoundInclusivity, BoundInclusivity)) -> Self {
CutType::new(past_inclusivity, future_inclusivity)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
pub enum CutResult<T> {
Uncut,
Cut(T, T),
}
impl<T> CutResult<T> {
#[must_use]
pub fn is_uncut(&self) -> bool {
matches!(self, CutResult::Uncut)
}
#[must_use]
pub fn is_cut(&self) -> bool {
matches!(self, CutResult::Cut(..))
}
#[must_use]
pub fn cut(self) -> Option<(T, T)> {
match self {
Self::Uncut => None,
Self::Cut(a, b) => Some((a, b)),
}
}
#[must_use]
pub fn map_cut<F, U>(self, mut f: F) -> CutResult<U>
where
F: FnMut(T, T) -> (U, U),
{
match self {
Self::Uncut => CutResult::Uncut,
Self::Cut(c1, c2) => {
let mapped_cut = (f)(c1, c2);
CutResult::Cut(mapped_cut.0, mapped_cut.1)
},
}
}
}
pub trait Cuttable<P> {
type Output;
fn cut_at(&self, position: P, cut_type: CutType) -> CutResult<Self::Output>;
}
impl Cuttable<Timestamp> for AbsBoundPair {
type Output = Self;
fn cut_at(&self, position: Timestamp, cut_type: CutType) -> CutResult<Self::Output> {
cut_abs_bound_pair(self, position, cut_type)
}
}
impl Cuttable<Timestamp> for EmptiableAbsBoundPair {
type Output = Self;
fn cut_at(&self, position: Timestamp, cut_type: CutType) -> CutResult<Self::Output> {
cut_emptiable_abs_bound_pair(self, position, cut_type)
}
}
impl Cuttable<Timestamp> for AbsInterval {
type Output = Self;
fn cut_at(&self, position: Timestamp, cut_type: CutType) -> CutResult<Self::Output> {
cut_abs_bound_pair(&self.abs_bound_pair(), position, cut_type)
.map_cut(|c1, c2| (c1.to_interval(), c2.to_interval()))
}
}
impl Cuttable<Timestamp> for EmptiableAbsInterval {
type Output = Self;
fn cut_at(&self, position: Timestamp, cut_type: CutType) -> CutResult<Self::Output> {
cut_emptiable_abs_bound_pair(&self.emptiable_abs_bound_pair(), position, cut_type)
.map_cut(|c1, c2| (c1.to_emptiable_interval(), c2.to_emptiable_interval()))
}
}
impl Cuttable<Timestamp> for BoundedAbsInterval {
type Output = Self;
fn cut_at(&self, position: Timestamp, cut_type: CutType) -> CutResult<Self::Output> {
cut_bounded_abs_interval(self, position, cut_type)
}
}
impl Cuttable<Timestamp> for HalfBoundedAbsInterval {
type Output = AbsInterval;
fn cut_at(&self, position: Timestamp, cut_type: CutType) -> CutResult<Self::Output> {
cut_abs_bound_pair(&self.abs_bound_pair(), position, cut_type)
.map_cut(|c1, c2| (c1.to_interval(), c2.to_interval()))
}
}
impl Cuttable<SignedDuration> for RelBoundPair {
type Output = Self;
fn cut_at(&self, position: SignedDuration, cut_type: CutType) -> CutResult<Self::Output> {
cut_rel_bound_pair(self, position, cut_type)
}
}
impl Cuttable<SignedDuration> for EmptiableRelBoundPair {
type Output = Self;
fn cut_at(&self, position: SignedDuration, cut_type: CutType) -> CutResult<Self::Output> {
cut_emptiable_rel_bound_pair(self, position, cut_type)
}
}
impl Cuttable<SignedDuration> for RelInterval {
type Output = Self;
fn cut_at(&self, position: SignedDuration, cut_type: CutType) -> CutResult<Self::Output> {
cut_rel_bound_pair(&self.rel_bound_pair(), position, cut_type)
.map_cut(|c1, c2| (c1.to_interval(), c2.to_interval()))
}
}
impl Cuttable<SignedDuration> for EmptiableRelInterval {
type Output = Self;
fn cut_at(&self, position: SignedDuration, cut_type: CutType) -> CutResult<Self::Output> {
cut_emptiable_rel_bound_pair(&self.emptiable_rel_bound_pair(), position, cut_type)
.map_cut(|c1, c2| (c1.to_emptiable_interval(), c2.to_emptiable_interval()))
}
}
impl Cuttable<SignedDuration> for BoundedRelInterval {
type Output = Self;
fn cut_at(&self, position: SignedDuration, cut_type: CutType) -> CutResult<Self::Output> {
cut_bounded_rel_interval(self, position, cut_type)
}
}
impl Cuttable<SignedDuration> for HalfBoundedRelInterval {
type Output = RelInterval;
fn cut_at(&self, position: SignedDuration, cut_type: CutType) -> CutResult<Self::Output> {
cut_rel_bound_pair(&self.rel_bound_pair(), position, cut_type)
.map_cut(|c1, c2| (c1.to_interval(), c2.to_interval()))
}
}
impl Cuttable<Timestamp> for UnboundedInterval {
type Output = AbsInterval;
fn cut_at(&self, position: Timestamp, cut_type: CutType) -> CutResult<Self::Output> {
cut_abs_bound_pair(&self.abs_bound_pair(), position, cut_type)
.map_cut(|c1, c2| (c1.to_interval(), c2.to_interval()))
}
}
impl Cuttable<SignedDuration> for UnboundedInterval {
type Output = RelInterval;
fn cut_at(&self, position: SignedDuration, cut_type: CutType) -> CutResult<Self::Output> {
cut_rel_bound_pair(&self.rel_bound_pair(), position, cut_type)
.map_cut(|c1, c2| (c1.to_interval(), c2.to_interval()))
}
}
impl Cuttable<Timestamp> for EmptyInterval {
type Output = ();
fn cut_at(&self, _position: Timestamp, _cut_type: CutType) -> CutResult<Self::Output> {
CutResult::Uncut
}
}
impl Cuttable<SignedDuration> for EmptyInterval {
type Output = ();
fn cut_at(&self, _position: SignedDuration, _cut_type: CutType) -> CutResult<Self::Output> {
CutResult::Uncut
}
}
#[must_use]
pub fn cut_abs_bound_pair(bounds: &AbsBoundPair, at: Timestamp, cut_type: CutType) -> CutResult<AbsBoundPair> {
if !bounds.simple_contains_point(at) {
return CutResult::Uncut;
}
let past_cut_end = AbsFiniteBoundPos::new_with_incl(at, cut_type.past_bound_inclusivity()).to_end_bound();
let future_cut_start = AbsFiniteBoundPos::new_with_incl(at, cut_type.future_bound_inclusivity()).to_start_bound();
if check_abs_start_end_bounds_for_interval_creation(&bounds.start(), &past_cut_end).is_err()
|| check_abs_start_end_bounds_for_interval_creation(&future_cut_start, &bounds.end()).is_err()
{
return CutResult::Uncut;
}
let mut past_split = bounds.clone();
let mut future_split = bounds.clone();
past_split.set_end(AbsFiniteBoundPos::new_with_incl(at, cut_type.past_bound_inclusivity()).to_end_bound());
future_split.set_start(AbsFiniteBoundPos::new_with_incl(at, cut_type.future_bound_inclusivity()).to_start_bound());
CutResult::Cut(past_split, future_split)
}
#[must_use]
pub fn cut_emptiable_abs_bound_pair(
bounds: &EmptiableAbsBoundPair,
at: Timestamp,
cut_type: CutType,
) -> CutResult<EmptiableAbsBoundPair> {
let EmptiableAbsBoundPair::Bound(non_empty_bounds) = bounds else {
return CutResult::Uncut;
};
cut_abs_bound_pair(non_empty_bounds, at, cut_type)
.map_cut(|c1, c2| (EmptiableAbsBoundPair::from(c1), EmptiableAbsBoundPair::from(c2)))
}
#[must_use]
pub fn cut_bounded_abs_interval(
interval: &BoundedAbsInterval,
at: Timestamp,
cut_type: CutType,
) -> CutResult<BoundedAbsInterval> {
if !interval.simple_contains_point(at) {
return CutResult::Uncut;
}
let past_cut_end = AbsFiniteBoundPos::new_with_incl(at, cut_type.past_bound_inclusivity()).to_end_bound();
let future_cut_start = AbsFiniteBoundPos::new_with_incl(at, cut_type.future_bound_inclusivity()).to_start_bound();
if check_abs_start_end_bounds_for_interval_creation(&interval.abs_start(), &past_cut_end).is_err()
|| check_abs_start_end_bounds_for_interval_creation(&future_cut_start, &interval.abs_end()).is_err()
{
return CutResult::Uncut;
}
let past_split = BoundedAbsInterval::unchecked_from_times_incl(
interval.start_time(),
interval.start_inclusivity(),
at,
cut_type.past_bound_inclusivity(),
);
let future_split = BoundedAbsInterval::unchecked_from_times_incl(
at,
cut_type.future_bound_inclusivity(),
interval.end_time(),
interval.end_inclusivity(),
);
CutResult::Cut(past_split, future_split)
}
#[must_use]
pub fn cut_rel_bound_pair(bounds: &RelBoundPair, at: SignedDuration, cut_type: CutType) -> CutResult<RelBoundPair> {
if !bounds.simple_contains_point(at) {
return CutResult::Uncut;
}
let past_cut_end = RelFiniteBoundPos::new_with_incl(at, cut_type.past_bound_inclusivity()).to_end_bound();
let future_cut_start = RelFiniteBoundPos::new_with_incl(at, cut_type.future_bound_inclusivity()).to_start_bound();
if check_rel_start_end_bounds_for_interval_creation(&bounds.start(), &past_cut_end).is_err()
|| check_rel_start_end_bounds_for_interval_creation(&future_cut_start, &bounds.end()).is_err()
{
return CutResult::Uncut;
}
let mut past_split = bounds.clone();
let mut future_split = bounds.clone();
past_split.set_end(RelFiniteBoundPos::new_with_incl(at, cut_type.past_bound_inclusivity()).to_end_bound());
future_split.set_start(RelFiniteBoundPos::new_with_incl(at, cut_type.future_bound_inclusivity()).to_start_bound());
CutResult::Cut(past_split, future_split)
}
#[must_use]
pub fn cut_emptiable_rel_bound_pair(
bounds: &EmptiableRelBoundPair,
at: SignedDuration,
cut_type: CutType,
) -> CutResult<EmptiableRelBoundPair> {
let EmptiableRelBoundPair::Bound(non_empty_bounds) = bounds else {
return CutResult::Uncut;
};
cut_rel_bound_pair(non_empty_bounds, at, cut_type)
.map_cut(|c1, c2| (EmptiableRelBoundPair::from(c1), EmptiableRelBoundPair::from(c2)))
}
#[must_use]
pub fn cut_bounded_rel_interval(
interval: &BoundedRelInterval,
at: SignedDuration,
cut_type: CutType,
) -> CutResult<BoundedRelInterval> {
if !interval.simple_contains_point(at) {
return CutResult::Uncut;
}
let past_cut_end = RelFiniteBoundPos::new_with_incl(at, cut_type.past_bound_inclusivity()).to_end_bound();
let future_cut_start = RelFiniteBoundPos::new_with_incl(at, cut_type.future_bound_inclusivity()).to_start_bound();
if check_rel_start_end_bounds_for_interval_creation(&interval.rel_start(), &past_cut_end).is_err()
|| check_rel_start_end_bounds_for_interval_creation(&future_cut_start, &interval.rel_end()).is_err()
{
return CutResult::Uncut;
}
let past_split = BoundedRelInterval::unchecked_from_offsets_incl(
interval.start_offset(),
interval.start_inclusivity(),
at,
cut_type.past_bound_inclusivity(),
);
let future_split = BoundedRelInterval::unchecked_from_offsets_incl(
at,
cut_type.future_bound_inclusivity(),
interval.end_offset(),
interval.end_inclusivity(),
);
CutResult::Cut(past_split, future_split)
}