use crate::argument::ArgumentError;
use crate::argument::ArgumentErrorKind;
use crate::argument::ArgumentResult;
use crate::argument::LengthConstraint;
use crate::argument::LengthMetric;
use crate::argument::internal::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,
},
))
}
}