use crate::argument::{
ArgumentError,
ArgumentErrorKind,
ArgumentResult,
LengthConstraint,
LengthMetric,
sealed::Sealed,
};
pub trait CollectionArgument: Sealed + Sized {
fn require_non_empty(self, path: &str) -> ArgumentResult<Self>;
fn require_len(self, path: &str, expected: usize) -> ArgumentResult<Self>;
fn require_len_at_least(
self,
path: &str,
min: usize,
) -> ArgumentResult<Self>;
fn require_len_at_most(
self,
path: &str,
max: usize,
) -> ArgumentResult<Self>;
fn require_len_in(
self,
path: &str,
min: usize,
max: usize,
) -> ArgumentResult<Self>;
}
impl<T> CollectionArgument for Vec<T> {
#[inline]
fn require_non_empty(self, path: &str) -> ArgumentResult<Self> {
if self.is_empty() {
return Err(ArgumentError::new(path, ArgumentErrorKind::Empty));
}
Ok(self)
}
#[inline]
fn require_len(self, path: &str, expected: usize) -> ArgumentResult<Self> {
validate_length(path, self.len(), LengthConstraint::Exact(expected))?;
Ok(self)
}
#[inline]
fn require_len_at_least(
self,
path: &str,
min: usize,
) -> ArgumentResult<Self> {
validate_length(path, self.len(), LengthConstraint::AtLeast(min))?;
Ok(self)
}
#[inline]
fn require_len_at_most(
self,
path: &str,
max: usize,
) -> ArgumentResult<Self> {
validate_length(path, self.len(), LengthConstraint::AtMost(max))?;
Ok(self)
}
#[inline]
fn require_len_in(
self,
path: &str,
min: usize,
max: usize,
) -> ArgumentResult<Self> {
validate_length(
path,
self.len(),
LengthConstraint::InRange { min, max },
)?;
Ok(self)
}
}
impl<T> CollectionArgument for &[T] {
#[inline]
fn require_non_empty(self, path: &str) -> ArgumentResult<Self> {
if self.is_empty() {
return Err(ArgumentError::new(path, ArgumentErrorKind::Empty));
}
Ok(self)
}
#[inline]
fn require_len(self, path: &str, expected: usize) -> ArgumentResult<Self> {
validate_length(path, self.len(), LengthConstraint::Exact(expected))?;
Ok(self)
}
#[inline]
fn require_len_at_least(
self,
path: &str,
min: usize,
) -> ArgumentResult<Self> {
validate_length(path, self.len(), LengthConstraint::AtLeast(min))?;
Ok(self)
}
#[inline]
fn require_len_at_most(
self,
path: &str,
max: usize,
) -> ArgumentResult<Self> {
validate_length(path, self.len(), LengthConstraint::AtMost(max))?;
Ok(self)
}
#[inline]
fn require_len_in(
self,
path: &str,
min: usize,
max: usize,
) -> ArgumentResult<Self> {
validate_length(
path,
self.len(),
LengthConstraint::InRange { min, max },
)?;
Ok(self)
}
}
impl<T, const N: usize> CollectionArgument for [T; N] {
#[inline]
fn require_non_empty(self, path: &str) -> ArgumentResult<Self> {
if N == 0 {
return Err(ArgumentError::new(path, ArgumentErrorKind::Empty));
}
Ok(self)
}
#[inline]
fn require_len(self, path: &str, expected: usize) -> ArgumentResult<Self> {
validate_length(path, N, LengthConstraint::Exact(expected))?;
Ok(self)
}
#[inline]
fn require_len_at_least(
self,
path: &str,
min: usize,
) -> ArgumentResult<Self> {
validate_length(path, N, LengthConstraint::AtLeast(min))?;
Ok(self)
}
#[inline]
fn require_len_at_most(
self,
path: &str,
max: usize,
) -> ArgumentResult<Self> {
validate_length(path, N, LengthConstraint::AtMost(max))?;
Ok(self)
}
#[inline]
fn require_len_in(
self,
path: &str,
min: usize,
max: usize,
) -> ArgumentResult<Self> {
validate_length(path, N, LengthConstraint::InRange { min, max })?;
Ok(self)
}
}
fn validate_length(
path: &str,
actual: usize,
constraint: LengthConstraint,
) -> ArgumentResult<()> {
if let LengthConstraint::InRange { min, max } = &constraint
&& min > max
{
return Err(ArgumentError::new(
path,
ArgumentErrorKind::InvalidLengthConstraint {
constraint,
metric: LengthMetric::Elements,
},
));
}
let is_valid = match &constraint {
LengthConstraint::Exact(expected) => actual == *expected,
LengthConstraint::AtLeast(min) => actual >= *min,
LengthConstraint::AtMost(max) => actual <= *max,
LengthConstraint::InRange { min, max } => {
actual >= *min && actual <= *max
}
};
if is_valid {
Ok(())
} else {
Err(ArgumentError::new(
path,
ArgumentErrorKind::Length {
actual,
constraint,
metric: LengthMetric::Elements,
},
))
}
}