#[derive(Debug, Eq, PartialEq)]
#[must_use = "every destination must be conducted or explicitly settled"]
pub enum Route<Destination> {
None,
One(Destination),
Two([Destination; 2]),
Three([Destination; 3]),
Four([Destination; 4]),
}
impl<Destination> Route<Destination> {
pub fn map<Mapped>(self, mut map: impl FnMut(Destination) -> Mapped) -> Route<Mapped> {
match self {
Self::None => Route::None,
Self::One(value) => Route::One(map(value)),
Self::Two(values) => Route::Two(values.map(map)),
Self::Three(values) => Route::Three(values.map(map)),
Self::Four(values) => Route::Four(values.map(map)),
}
}
pub fn discard(self) {}
pub const fn none() -> Self {
Self::None
}
pub const fn one(destination: Destination) -> Self {
Self::One(destination)
}
pub fn fanout(destinations: impl Into<Self>) -> Self {
destinations.into()
}
pub const fn len(&self) -> usize {
match self {
Self::None => 0,
Self::One(_) => 1,
Self::Two(_) => 2,
Self::Three(_) => 3,
Self::Four(_) => 4,
}
}
pub const fn is_empty(&self) -> bool {
matches!(self, Self::None)
}
pub fn into_single(self) -> Result<Destination, Self> {
match self {
Self::One(destination) => Ok(destination),
other @ (Self::None | Self::Two(_) | Self::Three(_) | Self::Four(_)) => Err(other),
}
}
}
impl<Destination> From<(Destination, Destination)> for Route<Destination> {
fn from((first, second): (Destination, Destination)) -> Self {
Self::Two([first, second])
}
}
impl<Destination> From<(Destination, Destination, Destination)> for Route<Destination> {
fn from((first, second, third): (Destination, Destination, Destination)) -> Self {
Self::Three([first, second, third])
}
}
impl<Destination> From<(Destination, Destination, Destination, Destination)>
for Route<Destination>
{
fn from(
(first, second, third, fourth): (Destination, Destination, Destination, Destination),
) -> Self {
Self::Four([first, second, third, fourth])
}
}
impl<Destination> From<(Destination, (Destination, Destination))> for Route<Destination> {
fn from((first, (second, third)): (Destination, (Destination, Destination))) -> Self {
Self::fanout((first, second, third))
}
}
impl<Destination> From<((Destination, Destination), Destination)> for Route<Destination> {
fn from(((first, second), third): ((Destination, Destination), Destination)) -> Self {
Self::fanout((first, second, third))
}
}
impl<Destination> From<((Destination, Destination), (Destination, Destination))>
for Route<Destination>
{
fn from(
((first, second), (third, fourth)): (
(Destination, Destination),
(Destination, Destination),
),
) -> Self {
Self::fanout((first, second, third, fourth))
}
}
impl<Destination> From<(Destination, (Destination, (Destination, Destination)))>
for Route<Destination>
{
fn from(
(first, (second, (third, fourth))): (
Destination,
(Destination, (Destination, Destination)),
),
) -> Self {
Self::fanout((first, second, third, fourth))
}
}
impl<Destination> From<(((Destination, Destination), Destination), Destination)>
for Route<Destination>
{
fn from(
(((first, second), third), fourth): (
((Destination, Destination), Destination),
Destination,
),
) -> Self {
Self::fanout((first, second, third, fourth))
}
}
impl<Destination> From<((Destination, (Destination, Destination)), Destination)>
for Route<Destination>
{
fn from(
((first, (second, third)), fourth): (
(Destination, (Destination, Destination)),
Destination,
),
) -> Self {
Self::fanout((first, second, third, fourth))
}
}
impl<Destination> From<(Destination, ((Destination, Destination), Destination))>
for Route<Destination>
{
fn from(
(first, ((second, third), fourth)): (
Destination,
((Destination, Destination), Destination),
),
) -> Self {
Self::fanout((first, second, third, fourth))
}
}
#[derive(Debug)]
pub struct PendingRoutes<Destination, const CAPACITY: usize> {
slots: [Option<Destination>; CAPACITY],
length: usize,
}
impl<Destination, const CAPACITY: usize> Default for PendingRoutes<Destination, CAPACITY> {
fn default() -> Self {
Self::new()
}
}
impl<Destination, const CAPACITY: usize> PendingRoutes<Destination, CAPACITY> {
pub fn discard(self) {}
pub const fn new() -> Self {
Self {
slots: [const { None }; CAPACITY],
length: 0,
}
}
pub const fn len(&self) -> usize {
self.length
}
pub const fn is_empty(&self) -> bool {
self.length == 0
}
pub const fn capacity(&self) -> usize {
CAPACITY
}
pub fn pop(&mut self) -> Option<Destination> {
let index = self.length.checked_sub(1)?;
self.slots.get_mut(index).and_then(|slot| {
self.length = index;
slot.take()
})
}
pub fn admit(
&mut self,
route: Route<Destination>,
) -> Result<Option<Destination>, Route<Destination>> {
let required = match &route {
Route::None | Route::One(_) => 0,
Route::Two(_) => 1,
Route::Three(_) => 2,
Route::Four(_) => 3,
};
let Some(new_length) = self
.length
.checked_add(required)
.filter(|length| *length <= CAPACITY)
else {
return Err(route);
};
let (first, siblings) = match route {
Route::None => return Ok(None),
Route::One(destination) => return Ok(Some(destination)),
Route::Two([first, second]) => (first, [Some(second), None, None]),
Route::Three([first, second, third]) => (first, [Some(third), Some(second), None]),
Route::Four([first, second, third, fourth]) => {
(first, [Some(fourth), Some(third), Some(second)])
}
};
for (slot, sibling) in self
.slots
.iter_mut()
.skip(self.length)
.zip(siblings.into_iter().flatten())
{
*slot = Some(sibling);
}
self.length = new_length;
Ok(Some(first))
}
}
#[derive(Eq, PartialEq)]
#[must_use = "settle the rejected route and retained siblings"]
pub enum FlowFailure<Failure, Destination> {
Dispatch(Failure),
Capacity {
rejected: Route<Destination>,
maximum_pending: usize,
},
}
impl<Failure: core::fmt::Debug, Destination> core::fmt::Debug
for FlowFailure<Failure, Destination>
{
fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
Self::Dispatch(failure) => formatter.debug_tuple("Dispatch").field(failure).finish(),
Self::Capacity {
rejected,
maximum_pending,
} => formatter
.debug_struct("Capacity")
.field("destinations", &rejected.len())
.field("maximum_pending", maximum_pending)
.finish(),
}
}
}
#[cfg(test)]
mod tests;
#[cfg(kani)]
mod proofs;