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::start_bound::*;
use crate::intervals::absolute::{AbsBound, AbsEndBound, AbsFiniteBoundPos};
use crate::intervals::meta::{BoundExtremality, BoundInclusivity, HasBoundExtremality};
use crate::test_utils::date_timestamp;

#[test]
fn is_finite() {
    assert!(
        AbsFiniteBoundPos::new(date_timestamp(2025, 1, 1))
            .to_start_bound()
            .is_finite()
    );
    assert!(!AbsStartBound::InfinitePast.is_finite());
}

#[test]
fn is_infinite_past() {
    assert!(
        !AbsFiniteBoundPos::new(date_timestamp(2025, 1, 1))
            .to_start_bound()
            .is_infinite_past()
    );
    assert!(AbsStartBound::InfinitePast.is_infinite_past());
}

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

#[test]
fn finite() {
    assert_eq!(
        AbsFiniteBoundPos::new(date_timestamp(2025, 1, 1))
            .to_start_bound()
            .finite(),
        Some(AbsFiniteBoundPos::new(date_timestamp(2025, 1, 1)).to_finite_start_bound()),
    );
    assert_eq!(AbsStartBound::InfinitePast.finite(), None);
}

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

    assert_eq!(start_bound.to_bound(), AbsBound::Start(start_bound),);
}

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

mod ord {
    use super::*;

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

    #[test]
    fn inf_finite() {
        assert_eq!(
            AbsStartBound::InfinitePast.cmp(&AbsFiniteBoundPos::new(date_timestamp(2025, 1, 1)).to_start_bound()),
            Ordering::Less,
        );
    }

    #[test]
    fn finite_inf() {
        assert_eq!(
            AbsFiniteBoundPos::new(date_timestamp(2025, 1, 1))
                .to_start_bound()
                .cmp(&AbsStartBound::InfinitePast),
            Ordering::Greater,
        );
    }

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

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

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

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

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

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

#[test]
fn from_absolute_finite_start_bound() {
    assert_eq!(
        AbsStartBound::from(AbsFiniteBoundPos::new(date_timestamp(2026, 1, 1)).to_finite_start_bound()),
        AbsStartBound::Finite(AbsFiniteBoundPos::new(date_timestamp(2026, 1, 1)).to_finite_start_bound())
    );
}

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

#[test]
fn from_opt_timestamp() {
    assert_eq!(
        AbsStartBound::from(Some(date_timestamp(2026, 1, 1))),
        AbsFiniteBoundPos::new(date_timestamp(2026, 1, 1)).to_start_bound()
    );
    assert_eq!(AbsStartBound::from(None::<Timestamp>), AbsStartBound::InfinitePast);
}

#[test]
fn from_opt_timestamp_inclusivity() {
    assert_eq!(
        AbsStartBound::from(Some((date_timestamp(2026, 1, 1), BoundInclusivity::Exclusive))),
        AbsFiniteBoundPos::new_with_incl(date_timestamp(2026, 1, 1), BoundInclusivity::Exclusive).to_start_bound()
    );
    assert_eq!(
        AbsStartBound::from(None::<(Timestamp, BoundInclusivity)>),
        AbsStartBound::InfinitePast
    );
}

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

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

    assert_eq!(
        AbsStartBound::try_from(
            AbsFiniteBoundPos::new_with_incl(date_timestamp(2026, 1, 1), BoundInclusivity::Exclusive)
                .to_end_bound()
                .to_bound()
        ),
        Err(AbsStartBoundTryFromAbsBoundError)
    );
    assert_eq!(
        AbsStartBound::try_from(AbsEndBound::InfiniteFuture.to_bound()),
        Err(AbsStartBoundTryFromAbsBoundError)
    );
}