periodical 0.3.0

Management of all kinds of time intervals, use it to manage schedules, find overlaps, and more!
Documentation
use std::cmp::Ordering;
use std::ops::Bound;

use jiff::Timestamp;

use super::end_bound::*;
use crate::intervals::absolute::{AbsBound, AbsFiniteBoundPos, AbsStartBound};
use crate::intervals::meta::{BoundExtremality, BoundInclusivity, HasBoundExtremality};
use crate::test_utils::{date_timestamp, datetime_timestamp};

#[test]
fn is_finite() {
    assert!(
        AbsFiniteBoundPos::new(date_timestamp(2025, 1, 1))
            .to_end_bound()
            .is_finite()
    );
    assert!(!AbsEndBound::InfiniteFuture.is_finite());
}

#[test]
fn is_infinite_future() {
    assert!(
        !AbsFiniteBoundPos::new(date_timestamp(2025, 1, 1))
            .to_end_bound()
            .is_infinite_future()
    );
    assert!(AbsEndBound::InfiniteFuture.is_infinite_future());
}

#[test]
fn opposite() {
    assert_eq!(
        AbsFiniteBoundPos::new(date_timestamp(2025, 1, 1))
            .to_end_bound()
            .opposite(),
        Some(
            AbsFiniteBoundPos::new_with_incl(date_timestamp(2025, 1, 1), BoundInclusivity::Exclusive,).to_start_bound()
        ),
    );
    assert_eq!(AbsEndBound::InfiniteFuture.opposite(), None);
}

#[test]
fn finite() {
    assert_eq!(
        AbsFiniteBoundPos::new(date_timestamp(2025, 1, 1))
            .to_end_bound()
            .finite(),
        Some(AbsFiniteBoundPos::new(date_timestamp(2025, 1, 1)).to_finite_end_bound()),
    );
    assert_eq!(AbsEndBound::InfiniteFuture.finite(), None);
}

#[test]
fn to_bound() {
    let end_bound = AbsFiniteBoundPos::new(date_timestamp(2026, 1, 1)).to_end_bound();

    assert_eq!(end_bound.to_bound(), AbsBound::End(end_bound));
}

#[test]
fn bound_extremality() {
    assert_eq!(
        AbsFiniteBoundPos::new(date_timestamp(2026, 1, 1))
            .to_end_bound()
            .bound_extremality(),
        BoundExtremality::End
    );
}

mod ord {
    use super::*;

    #[test]
    fn inf_inf() {
        assert_eq!(
            AbsEndBound::InfiniteFuture.cmp(&AbsEndBound::InfiniteFuture),
            Ordering::Equal
        );
    }

    #[test]
    fn inf_finite() {
        assert_eq!(
            AbsEndBound::InfiniteFuture.cmp(&AbsFiniteBoundPos::new(date_timestamp(2025, 1, 1)).to_end_bound()),
            Ordering::Greater,
        );
    }

    #[test]
    fn finite_inf() {
        assert_eq!(
            AbsFiniteBoundPos::new(date_timestamp(2025, 1, 1))
                .to_end_bound()
                .cmp(&AbsEndBound::InfiniteFuture),
            Ordering::Less,
        );
    }

    #[test]
    fn different_times_greater() {
        assert_eq!(
            AbsFiniteBoundPos::new(date_timestamp(2025, 1, 2))
                .to_end_bound()
                .cmp(&AbsFiniteBoundPos::new(date_timestamp(2025, 1, 1)).to_end_bound()),
            Ordering::Greater,
        );
    }

    #[test]
    fn different_times_less() {
        assert_eq!(
            AbsFiniteBoundPos::new(date_timestamp(2025, 1, 1))
                .to_end_bound()
                .cmp(&AbsFiniteBoundPos::new(date_timestamp(2025, 1, 2)).to_end_bound()),
            Ordering::Less,
        );
    }

    #[test]
    fn same_times_exclusive_bounds() {
        assert_eq!(
            AbsFiniteBoundPos::new_with_incl(date_timestamp(2025, 1, 1), BoundInclusivity::Exclusive,)
                .to_end_bound()
                .cmp(
                    &AbsFiniteBoundPos::new_with_incl(date_timestamp(2025, 1, 1), BoundInclusivity::Exclusive,)
                        .to_end_bound()
                ),
            Ordering::Equal,
        );
    }

    #[test]
    fn same_times_exclusive_inclusive_bounds() {
        assert_eq!(
            AbsFiniteBoundPos::new_with_incl(date_timestamp(2025, 1, 1), BoundInclusivity::Exclusive,)
                .to_end_bound()
                .cmp(
                    &AbsFiniteBoundPos::new_with_incl(date_timestamp(2025, 1, 1), BoundInclusivity::Inclusive,)
                        .to_end_bound()
                ),
            Ordering::Less,
        );
    }

    #[test]
    fn same_times_inclusive_exclusive_bounds() {
        assert_eq!(
            AbsFiniteBoundPos::new_with_incl(date_timestamp(2025, 1, 1), BoundInclusivity::Inclusive,)
                .to_end_bound()
                .cmp(
                    &AbsFiniteBoundPos::new_with_incl(date_timestamp(2025, 1, 1), BoundInclusivity::Exclusive,)
                        .to_end_bound()
                ),
            Ordering::Greater,
        );
    }

    #[test]
    fn same_times_inclusive_bounds() {
        assert_eq!(
            AbsFiniteBoundPos::new_with_incl(date_timestamp(2025, 1, 1), BoundInclusivity::Inclusive,)
                .to_end_bound()
                .cmp(
                    &AbsFiniteBoundPos::new_with_incl(date_timestamp(2025, 1, 1), BoundInclusivity::Inclusive,)
                        .to_end_bound()
                ),
            Ordering::Equal,
        );
    }
}

#[test]
fn from_absolute_finite_end_bound() {
    assert_eq!(
        AbsEndBound::from(AbsFiniteBoundPos::new(date_timestamp(2026, 1, 1)).to_finite_end_bound()),
        AbsEndBound::Finite(AbsFiniteBoundPos::new(date_timestamp(2026, 1, 1)).to_finite_end_bound())
    );
}

#[test]
fn from_absolute_finite_bound_position() {
    assert_eq!(
        AbsEndBound::from(AbsFiniteBoundPos::new_with_incl(
            date_timestamp(2025, 1, 1),
            BoundInclusivity::Exclusive,
        )),
        AbsEndBound::Finite(
            AbsFiniteBoundPos::new_with_incl(date_timestamp(2025, 1, 1), BoundInclusivity::Exclusive,)
                .to_finite_end_bound()
        ),
    );
}

#[test]
fn from_opt_timestamp() {
    assert_eq!(
        AbsEndBound::from(Some(datetime_timestamp(2026, 1, 1, 8, 0, 0))),
        AbsFiniteBoundPos::new(datetime_timestamp(2026, 1, 1, 8, 0, 0)).to_end_bound()
    );
    assert_eq!(AbsEndBound::from(None::<Timestamp>), AbsEndBound::InfiniteFuture);
}

#[test]
fn from_opt_timestamp_inclusivity() {
    assert_eq!(
        AbsEndBound::from(Some((
            datetime_timestamp(2026, 1, 1, 8, 0, 0),
            BoundInclusivity::Exclusive
        ))),
        AbsFiniteBoundPos::new_with_incl(datetime_timestamp(2026, 1, 1, 8, 0, 0), BoundInclusivity::Exclusive)
            .to_end_bound()
    );
    assert_eq!(
        AbsEndBound::from(None::<(Timestamp, BoundInclusivity)>),
        AbsEndBound::InfiniteFuture
    );
}

#[test]
fn from_bound() {
    assert_eq!(
        AbsEndBound::from(Bound::Included(date_timestamp(2025, 1, 1))),
        AbsFiniteBoundPos::new_with_incl(date_timestamp(2025, 1, 1), BoundInclusivity::Inclusive,).to_end_bound(),
    );
    assert_eq!(
        AbsEndBound::from(Bound::Excluded(date_timestamp(2025, 1, 1))),
        AbsFiniteBoundPos::new_with_incl(date_timestamp(2025, 1, 1), BoundInclusivity::Exclusive,).to_end_bound(),
    );
    assert_eq!(AbsEndBound::from(Bound::Unbounded), AbsEndBound::InfiniteFuture);
}

#[test]
fn try_from_abs_bound() {
    assert_eq!(
        AbsEndBound::try_from(
            AbsFiniteBoundPos::new_with_incl(date_timestamp(2026, 1, 1), BoundInclusivity::Exclusive)
                .to_end_bound()
                .to_bound()
        ),
        Ok(AbsFiniteBoundPos::new_with_incl(date_timestamp(2026, 1, 1), BoundInclusivity::Exclusive).to_end_bound())
    );
    assert_eq!(
        AbsEndBound::try_from(AbsEndBound::InfiniteFuture.to_bound()),
        Ok(AbsEndBound::InfiniteFuture)
    );

    assert_eq!(
        AbsEndBound::try_from(
            AbsFiniteBoundPos::new_with_incl(date_timestamp(2026, 1, 1), BoundInclusivity::Exclusive)
                .to_start_bound()
                .to_bound()
        ),
        Err(AbsEndBoundTryFromAbsBoundError)
    );
    assert_eq!(
        AbsEndBound::try_from(AbsStartBound::InfinitePast.to_bound()),
        Err(AbsEndBoundTryFromAbsBoundError)
    );
}