use core::{error::Error, fmt, num::NonZeroUsize, ptr::NonNull};
use crate::node::Node;
#[derive(Debug)]
pub(crate) struct Link<V, const N: usize> {
node: NonNull<Node<V, N>>,
distance: NonZeroUsize,
}
impl<V, const N: usize> Link<V, N> {
pub(crate) fn new(node: NonNull<Node<V, N>>, distance: usize) -> Result<Self, LinkError> {
Ok(Link {
node,
distance: NonZeroUsize::new(distance).ok_or(LinkError::InvalidDistance)?,
})
}
pub(crate) fn node(&self) -> NonNull<Node<V, N>> {
self.node
}
pub(crate) fn distance(&self) -> NonZeroUsize {
self.distance
}
pub(crate) fn increment_distance(&mut self) -> Result<NonZeroUsize, LinkError> {
self.distance = self
.distance
.checked_add(1)
.ok_or(LinkError::DistanceOverflow)?;
Ok(self.distance)
}
pub(crate) fn decrement_distance(&mut self) -> Result<NonZeroUsize, LinkError> {
self.distance = self
.distance
.get()
.checked_sub(1)
.and_then(NonZeroUsize::new)
.ok_or(LinkError::DistanceUnderflow)?;
Ok(self.distance)
}
}
#[derive(Debug)]
pub(crate) enum LinkError {
DistanceOverflow,
DistanceUnderflow,
InvalidDistance,
}
impl fmt::Display for LinkError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::DistanceOverflow => write!(f, "distance overflow"),
Self::DistanceUnderflow => write!(f, "distance underflow"),
Self::InvalidDistance => write!(f, "invalid distance"),
}
}
}
impl Error for LinkError {}
#[cfg(test)]
mod tests {
use std::{num::NonZeroUsize, ptr::NonNull};
use anyhow::{Result, anyhow};
use pretty_assertions::assert_eq;
use super::{Link, LinkError};
use crate::node::Node;
#[test]
fn link_new() -> Result<()> {
let node: Node<i32, 3> = Node::new(3);
let link = Link::new(NonNull::from(&node), 1)?;
assert_eq!(link.node(), NonNull::from(&node));
assert_eq!(
link.distance(),
NonZeroUsize::new(1).ok_or(anyhow!("Invalid distance"))?
);
Ok(())
}
#[test]
fn link_increment_distance() -> Result<()> {
let node: Node<i32, 3> = Node::new(3);
let mut link = Link::new(NonNull::from(&node), 1)?;
assert_eq!(
link.increment_distance()?,
NonZeroUsize::new(2).expect("NonZeroUsize::new failed")
);
Ok(())
}
#[test]
fn link_decrement_distance() -> Result<()> {
let node: Node<i32, 3> = Node::new(3);
let mut link = Link::new(NonNull::from(&node), 2)?;
assert_eq!(
link.decrement_distance()?,
NonZeroUsize::new(1).expect("NonZeroUsize::new failed")
);
Ok(())
}
#[test]
fn link_decrement_distance_underflow() -> Result<()> {
let node: Node<i32, 3> = Node::new(3);
let mut link = Link::new(NonNull::from(&node), 1)?;
assert!(matches!(
link.decrement_distance(),
Err(LinkError::DistanceUnderflow)
));
Ok(())
}
#[test]
fn link_increment_distance_overflow() -> Result<()> {
let node: Node<i32, 3> = Node::new(3);
let mut link = Link::new(NonNull::from(&node), usize::MAX)?;
assert!(matches!(
link.increment_distance(),
Err(LinkError::DistanceOverflow)
));
Ok(())
}
}