willow25 0.7.0

A ready-to-use implementation of the Willow specifications.
Documentation
//! Non-empty [groupings](https://willowprotocol.org/specs/grouping-entries/) of entries in 3d space.
//!
//! This module implements the [groupings](https://willowprotocol.org/specs/grouping-entries/) used by the Willow specifications:
//!
//! - [ranges](https://willowprotocol.org/specs/grouping-entries/#ranges) ([`WillowRange`]),
//! - [3d ranges](https://willowprotocol.org/specs/grouping-entries/#D3Range) ([`Range3d`]),
//! - [areas](https://willowprotocol.org/specs/grouping-entries/#areas) ([`Area`]), and
//! - [areas of interest](https://willowprotocol.org/specs/grouping-entries/#aois) ([`AreaOfInterest`]).
//!
//! Our implementations center around two traits: [`Coordinatelike`] describes values which fall into some position in three-dimensional Willow space, and [`Grouping`] describes some non-empty region of that space. The [`CoordinatelikeExt`] trait then provides convenient methods for checking which values are included in which groupings.
//!
//! ```
//! use willow25::prelude::*;
//!
//! let entry = Entry::builder()
//!     .namespace_id([0; 32].into())
//!     .subspace_id([1; 32].into())
//!     .path(path!("/vacation/plan"))
//!     .timestamp(12345)
//!     .payload_digest([77; 32].into())
//!     .payload_length(17)
//!     .build();
//!
//! assert!(entry.is_in(&Range3d::full()));
//! ```

use core::fmt;
use core::ops::RangeBounds;

#[cfg(feature = "dev")]
use arbitrary::Arbitrary;

use crate::prelude::*;

mod willow_range;
pub use willow_range::*;

mod keylike;
pub use keylike::*;

mod coordinatelike;
pub use coordinatelike::*;

mod namespaced;
pub use namespaced::*;

mod range_3d;
pub use range_3d::*;

mod area;
pub use area::*;

mod area_of_interest;
pub use area_of_interest::*;

pub mod private_context;

/// A [`WillowRange`] of [SubspaceIds](https://willowprotocol.org/specs/data-model/index.html#SubspaceId).
///
/// [Specification](https://willowprotocol.org/specs/grouping-entries/index.html#SubspaceRange)
pub type SubspaceRange = WillowRange<SubspaceId>;

/// A [`WillowRange`] of [Paths](https://willowprotocol.org/specs/data-model/index.html#Path).
///
/// [Specification](https://willowprotocol.org/specs/grouping-entries/index.html#PathRange)
pub type PathRange = WillowRange<Path>;

/// A [`WillowRange`] of [Timestamps](https://willowprotocol.org/specs/data-model/index.html#Timestamp).
///
/// [Specification](https://willowprotocol.org/specs/grouping-entries/index.html#TimeRange)
pub type TimeRange = WillowRange<Timestamp>;

/// An error returned whenever an operation would result in an empty grouping.
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
#[cfg_attr(feature = "dev", derive(Arbitrary))]
pub struct EmptyGrouping;

impl fmt::Display for EmptyGrouping {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(
            f,
            "An operation would have resulted in an empty grouping of Willow entries."
        )
    }
}

impl core::error::Error for EmptyGrouping {}

/// A grouping of entries.
///
/// A grouping describes a non-empty set of [`Coordinatelike`] values, with [`Grouping::includes`] providing the membership test. Groupings must implement [`PartialOrd`] as the subset relation.
pub trait Grouping: PartialOrd + Sized {
    /// Returns `true` iff the given [`Coordinatelike`] value is included in this grouping.
    fn includes<Coord>(&self, coord: &Coord) -> bool
    where
        Coord: Coordinatelike + ?Sized;

    /// Returns `true` iff every value [included](Grouping::includes) in `other` is also [included](Grouping::includes) in `self`.
    fn includes_grouping(&self, other: &Self) -> bool {
        self >= other
    }

    /// Returns `true` iff every value [included](Grouping::includes) in `other` is also [included](Grouping::includes) in `self` *and* `self` and `other` are not equal.
    fn strictly_includes_grouping(&self, other: &Self) -> bool {
        self > other
    }

    /// Returns a grouping which [includes](Grouping::includes) exactly those values [included](Grouping::includes) by both `self` and `other`. If that set would be empty, returns an [`EmptyGrouping`] error instead.
    fn intersection(&self, other: &Self) -> Result<Self, EmptyGrouping>;

    /// Returns `true` iff the given [`Coordinatelike`] value is [included](Grouping::includes) in both `self` and `other`.
    fn includes_in_intersection<Coord>(&self, other: &Self, coord: &Coord) -> bool
    where
        Coord: Coordinatelike + ?Sized,
    {
        self.includes(coord) && other.includes(coord)
    }
}

/// Returns `true` if the outer `Option<S>` includes the inner `Option<S>`, where `None` denotes interest in all subspaces of the namespace.
pub fn subspace_includes_subspace<S: PartialEq>(outer: Option<&S>, inner: Option<&S>) -> bool {
    match (outer, inner) {
        (Some(s1), Some(s2)) => s1 == s2,
        (Some(_), None) => false,
        _ => true,
    }
}

impl Grouping for SubspaceRange {
    fn includes<Coord>(&self, coord: &Coord) -> bool
    where
        Coord: Coordinatelike + ?Sized,
    {
        self.contains(coord.subspace_id())
    }

    fn intersection(&self, other: &Self) -> Result<Self, EmptyGrouping> {
        self.intersection_willow_range(other)
    }
}

impl Grouping for PathRange {
    fn includes<Coord>(&self, coord: &Coord) -> bool
    where
        Coord: Coordinatelike + ?Sized,
    {
        self.contains(coord.path())
    }

    fn intersection(&self, other: &Self) -> Result<Self, EmptyGrouping> {
        self.intersection_willow_range(other)
    }
}

impl Grouping for TimeRange {
    fn includes<Coord>(&self, coord: &Coord) -> bool
    where
        Coord: Coordinatelike + ?Sized,
    {
        self.contains(&coord.timestamp())
    }

    fn intersection(&self, other: &Self) -> Result<Self, EmptyGrouping> {
        self.intersection_willow_range(other)
    }
}