odsek 0.1.0

Lazy, pull-based composition of mathematical interval sets with open/closed endpoints, no_std-compatible.
Documentation
/// Toggle the open/closed flavor of an endpoint value.
///
/// For [`EndpointOC`](crate::EndpointOC), this swaps `Open` ↔ `Closed`. For
/// [`EndpointSymmetric`](crate::EndpointSymmetric), the toggle is the identity
/// because its open/closed status depends only on which side it sits on.
///
/// Used by [`LeftT::into_right`] and [`RightT::into_left`] to convert between
/// "the right edge of one interval" and "the left edge of the next one"
/// without dropping the boundary semantics.
pub trait EndpointToggle {
    /// Toggle the open/closed flavor.
    fn toggle(self) -> Self;
}

/// Apply an in-place transform to the underlying endpoint value, preserving
/// the open/closed flavor.
///
/// Prefer this over manually destructuring [`EndpointOC`](crate::EndpointOC)
/// when the open/closed status should not change.
pub trait EndpointMap {
    /// The underlying value type carried by this endpoint.
    type EndpointValue;
    /// Apply `f` to the underlying value, leaving the open/closed flavor unchanged.
    fn map_endpoint(self, f: impl Fn(Self::EndpointValue) -> Self::EndpointValue) -> Self;
}

/// Like [`EndpointMap`], but allows the underlying value type to change
/// (e.g. `i32` → `i64`).
pub trait EndpointMapInto<U> {
    /// The underlying value type before the transform.
    type EndpointValue;
    /// The endpoint type after the transform.
    type Output;
    /// Apply `f` to the underlying value, producing an endpoint of `Output`
    /// with the same open/closed flavor.
    fn map_endpoint_into(self, f: impl Fn(Self::EndpointValue) -> U) -> Self::Output;
}

/// A range between two endpoints carrying an attached `value`.
///
/// Construct with [`Interval::new`] (bare endpoints) or [`Interval::new_lr`]
/// (already-wrapped [`LeftT`]/[`RightT`]).
#[derive(Debug, Clone, PartialEq)]
pub struct Interval<E, V> {
    /// The left (lower) endpoint.
    left_endpoint: LeftT<E>,
    /// The right (upper) endpoint.
    right_endpoint: RightT<E>,
    /// The value attached to this interval.
    value: V,
}

/// Wraps an endpoint to mark it as the *left* (lower) side of an interval.
///
/// The same endpoint value compares differently depending on which side it
/// sits on (e.g. an open `Open(2)` on the left is "after" `Closed(2)`, while
/// on the right it is "before"). Tagging the side via `LeftT` / [`RightT`]
/// lets the [`PartialOrd`]/[`Ord`] implementations encode those rules.
#[derive(Debug, Clone, PartialEq)]
pub enum LeftT<T> {
    /// The wrapped left endpoint.
    Left(T),
}

use LeftT::*;

/// Wraps an endpoint to mark it as the *right* (upper) side of an interval.
///
/// See [`LeftT`] for why side-tagging matters.
#[derive(Debug, Clone, PartialEq)]
pub enum RightT<T> {
    /// The wrapped right endpoint.
    Right(T),
}
use RightT::*;

impl<T> RightT<T> {
    /// Borrow the underlying endpoint value.
    pub fn endpoint(&self) -> &T {
        let Right(v) = self;
        v
    }
}

impl<T> LeftT<T> {
    /// Borrow the underlying endpoint value.
    pub fn endpoint(&self) -> &T {
        let Left(v) = self;
        v
    }
}

impl<E, V> Interval<E, V> {
    /// Borrow the side-tagged left endpoint.
    pub fn left_endpoint(&self) -> &LeftT<E> {
        &self.left_endpoint
    }

    /// Borrow the side-tagged right endpoint.
    pub fn right_endpoint(&self) -> &RightT<E> {
        &self.right_endpoint
    }

    /// Borrow the attached value.
    pub fn value_ref(&self) -> &V {
        &self.value
    }

    /// Clone the left endpoint.
    pub fn left(&self) -> LeftT<E>
    where
        E: Clone,
    {
        self.left_endpoint.clone()
    }
    /// Clone the right endpoint.
    pub fn right(&self) -> RightT<E>
    where
        E: Clone,
    {
        self.right_endpoint.clone()
    }
    /// Copy the attached value.
    pub fn value(&self) -> V
    where
        V: Copy,
    {
        self.value
    }
    /// Build an `Interval` whose value is a borrow of this interval's value,
    /// without cloning the value.
    pub fn ref_val(&self) -> Interval<E, &V>
    where
        E: Clone,
    {
        Interval::new_lr(
            self.left_endpoint.clone(),
            self.right_endpoint.clone(),
            &self.value,
        )
    }

    /// Build an interval from bare endpoint values. The left side is wrapped
    /// in [`LeftT::Left`] and the right in [`RightT::Right`].
    pub fn new(left_endpoint: E, right_endpoint: E, value: V) -> Self {
        Interval {
            left_endpoint: Left(left_endpoint),
            right_endpoint: Right(right_endpoint),
            value,
        }
    }
    /// Build an interval from already side-tagged endpoints.
    pub fn new_lr(left: LeftT<E>, right: RightT<E>, value: V) -> Self {
        Interval {
            left_endpoint: left,
            right_endpoint: right,
            value,
        }
    }
}

impl<E> LeftT<E> {
    /// Convert this left endpoint into a right endpoint with toggled open/closed flavor.
    ///
    /// Useful for splitting an interval — the boundary that ended one segment
    /// becomes the (toggled) start of the next.
    pub fn into_right(self) -> RightT<E>
    where
        E: EndpointToggle,
    {
        let Left(x) = self;
        Right(x.toggle())
    }

    /// Unwrap the underlying endpoint value.
    pub fn value(self) -> E {
        let Left(x) = self;
        x
    }
}

impl<E> RightT<E> {
    /// Convert this right endpoint into a left endpoint with toggled open/closed flavor.
    ///
    /// This is how the iterator advances: after emitting an interval ending at
    /// `Right(Open(x))`, the next query position is `Left(Closed(x))`.
    pub fn into_left(self) -> LeftT<E>
    where
        E: EndpointToggle,
    {
        let Right(x) = self;
        Left(x.toggle())
    }

    /// Unwrap the underlying endpoint value.
    pub fn value(self) -> E {
        let Right(x) = self;
        x
    }
}

impl<E> EndpointMap for LeftT<E>
where
    E: EndpointMap,
{
    type EndpointValue = E::EndpointValue;
    fn map_endpoint(self, f: impl Fn(E::EndpointValue) -> E::EndpointValue) -> Self {
        Left(self.value().map_endpoint(f))
    }
}

impl<E> EndpointMap for RightT<E>
where
    E: EndpointMap,
{
    type EndpointValue = E::EndpointValue;
    fn map_endpoint(self, f: impl Fn(E::EndpointValue) -> E::EndpointValue) -> Self {
        Right(self.value().map_endpoint(f))
    }
}

impl<E, U> EndpointMapInto<U> for LeftT<E>
where
    E: EndpointMapInto<U>,
{
    type EndpointValue = E::EndpointValue;
    type Output = LeftT<E::Output>;
    fn map_endpoint_into(self, f: impl Fn(E::EndpointValue) -> U) -> LeftT<E::Output> {
        Left(self.value().map_endpoint_into(f))
    }
}

impl<E, U> EndpointMapInto<U> for RightT<E>
where
    E: EndpointMapInto<U>,
{
    type EndpointValue = E::EndpointValue;
    type Output = RightT<E::Output>;
    fn map_endpoint_into(self, f: impl Fn(E::EndpointValue) -> U) -> RightT<E::Output> {
        Right(self.value().map_endpoint_into(f))
    }
}