use std::fmt;
use super::value::OptionValue;
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct OptionConstraint {
pub min: Option<i64>,
pub max: Option<i64>,
pub min_length: Option<usize>,
pub max_length: Option<usize>,
}
impl OptionConstraint {
#[must_use]
pub const fn none() -> Self {
Self {
min: None,
max: None,
min_length: None,
max_length: None,
}
}
#[must_use]
pub const fn range(min: i64, max: i64) -> Self {
Self {
min: Some(min),
max: Some(max),
min_length: None,
max_length: None,
}
}
#[must_use]
pub const fn min(min: i64) -> Self {
Self {
min: Some(min),
max: None,
min_length: None,
max_length: None,
}
}
#[must_use]
pub const fn max(max: i64) -> Self {
Self {
min: None,
max: Some(max),
min_length: None,
max_length: None,
}
}
#[must_use]
pub const fn string_length(min_length: usize, max_length: usize) -> Self {
Self {
min: None,
max: None,
min_length: Some(min_length),
max_length: Some(max_length),
}
}
pub fn validate(&self, value: &OptionValue) -> Result<(), ConstraintError> {
match value {
OptionValue::Integer(i) => {
if let Some(min) = self.min
&& *i < min
{
return Err(ConstraintError::BelowMinimum { value: *i, min });
}
if let Some(max) = self.max
&& *i > max
{
return Err(ConstraintError::AboveMaximum { value: *i, max });
}
}
OptionValue::String(s) => {
if let Some(min_len) = self.min_length
&& s.len() < min_len
{
return Err(ConstraintError::StringTooShort {
len: s.len(),
min: min_len,
});
}
if let Some(max_len) = self.max_length
&& s.len() > max_len
{
return Err(ConstraintError::StringTooLong {
len: s.len(),
max: max_len,
});
}
}
OptionValue::Choice { value, choices } => {
if !choices.contains(value) {
return Err(ConstraintError::InvalidChoice {
value: value.clone(),
choices: choices.clone(),
});
}
}
OptionValue::Bool(_) => {
}
}
Ok(())
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ConstraintError {
BelowMinimum {
value: i64,
min: i64,
},
AboveMaximum {
value: i64,
max: i64,
},
StringTooShort {
len: usize,
min: usize,
},
StringTooLong {
len: usize,
max: usize,
},
InvalidChoice {
value: String,
choices: Vec<String>,
},
}
impl fmt::Display for ConstraintError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::BelowMinimum { value, min } => {
write!(f, "value {value} is below minimum {min}")
}
Self::AboveMaximum { value, max } => {
write!(f, "value {value} is above maximum {max}")
}
Self::StringTooShort { len, min } => {
write!(f, "string length {len} is below minimum {min}")
}
Self::StringTooLong { len, max } => {
write!(f, "string length {len} is above maximum {max}")
}
Self::InvalidChoice { value, choices } => {
write!(f, "'{value}' is not a valid choice (valid: {choices:?})")
}
}
}
}
impl std::error::Error for ConstraintError {}