keyfork/keyfork-prompt/src/validators.rs

55 lines
1.5 KiB
Rust
Raw Normal View History

2024-01-09 07:21:46 +00:00
#![allow(clippy::type_complexity)]
pub trait Validator {
type Output;
type Error;
fn to_fn(&self) -> Box<dyn Fn(String) -> Result<Self::Output, Self::Error>>;
}
#[derive(thiserror::Error, Debug)]
pub enum PinError {
#[error("PIN too short: {0} < {1}")]
TooShort(usize, usize),
#[error("PIN too long: {0} > {1}")]
TooLong(usize, usize),
#[error("PIN contained invalid characters (found {0} at position {1})")]
InvalidCharacters(char, usize),
}
#[derive(Default, Clone)]
pub struct PinValidator {
pub min_length: Option<usize>,
pub max_length: Option<usize>,
pub range: Option<std::ops::RangeInclusive<char>>,
}
impl Validator for PinValidator {
type Output = String;
type Error = PinError;
fn to_fn(&self) -> Box<dyn Fn(String) -> Result<String, PinError>> {
let min_len = self.min_length.unwrap_or(usize::MIN);
let max_len = self.max_length.unwrap_or(usize::MAX);
let range = self.range.clone().unwrap_or('0'..='9');
Box::new(move |mut s: String| {
s.truncate(s.trim_end().len());
let len = s.len();
if len < min_len {
return Err(PinError::TooShort(len, min_len));
}
if len > max_len {
return Err(PinError::TooLong(len, max_len));
}
for (index, ch) in s.chars().enumerate() {
if !range.contains(&ch) {
return Err(PinError::InvalidCharacters(ch, index));
}
}
Ok(s)
})
}
}