use std::rc::Rc;
use std::cell::RefCell;
pub const VO_FILL: u16 = 0x0001; pub const VO_TRANSFER: u16 = 0x0002; pub const VO_ON_APPEND: u16 = 0x0004;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ValidatorStatus {
Ok = 0,
Syntax = 1,
}
pub trait Validator {
fn is_valid(&self, input: &str) -> bool;
fn is_valid_input(&self, input: &str, _append: bool) -> bool {
self.is_valid(input)
}
fn error(&self);
fn options(&self) -> u16 {
0
}
fn valid(&self, input: &str) -> bool {
if self.is_valid(input) {
true
} else {
self.error();
false
}
}
}
pub struct FilterValidator {
valid_chars: String,
options: u16,
}
impl FilterValidator {
pub fn new(valid_chars: &str) -> Self {
Self {
valid_chars: valid_chars.to_string(),
options: 0,
}
}
pub fn with_options(valid_chars: &str, options: u16) -> Self {
Self {
valid_chars: valid_chars.to_string(),
options,
}
}
}
impl Validator for FilterValidator {
fn is_valid(&self, input: &str) -> bool {
input.chars().all(|ch| self.valid_chars.contains(ch))
}
fn is_valid_input(&self, input: &str, _append: bool) -> bool {
self.is_valid(input)
}
fn error(&self) {
}
fn options(&self) -> u16 {
self.options
}
}
pub struct RangeValidator {
min: i64,
max: i64,
valid_chars: String,
options: u16,
}
impl RangeValidator {
pub fn new(min: i64, max: i64) -> Self {
let valid_chars = if min >= 0 && max >= 0 {
"+0123456789xXABCDEFabcdef".to_string()
} else if min < 0 && max < 0 {
"-0123456789xXABCDEFabcdef".to_string()
} else {
"-+0123456789xXABCDEFabcdef".to_string()
};
Self {
min,
max,
valid_chars,
options: 0,
}
}
pub fn with_options(min: i64, max: i64, options: u16) -> Self {
let mut validator = Self::new(min, max);
validator.options = options;
validator
}
fn parse_value(&self, input: &str) -> Result<i64, std::num::ParseIntError> {
let trimmed = input.trim();
if trimmed.starts_with("0x") || trimmed.starts_with("0X") {
return i64::from_str_radix(&trimmed[2..], 16);
}
if trimmed.starts_with('0') && trimmed.len() > 1 && !trimmed.contains(|c: char| c == '8' || c == '9') {
return i64::from_str_radix(&trimmed[1..], 8);
}
trimmed.parse::<i64>()
}
}
impl Validator for RangeValidator {
fn is_valid(&self, input: &str) -> bool {
if !input.chars().all(|ch| self.valid_chars.contains(ch)) {
return false;
}
match self.parse_value(input) {
Ok(value) => {
value >= self.min && value <= self.max
}
Err(_) => false,
}
}
fn is_valid_input(&self, input: &str, _append: bool) -> bool {
let is_hex_input = input.starts_with("0x") || input.starts_with("0X") ||
input.starts_with("+0x") || input.starts_with("+0X") ||
input.starts_with("-0x") || input.starts_with("-0X");
for ch in input.chars() {
if self.valid_chars.contains(ch) &&
(ch.is_ascii_digit() || ch == '+' || ch == '-' || ch == 'x' || ch == 'X') {
continue;
}
if ch.is_ascii_alphabetic() && ch != 'x' && ch != 'X' {
if !is_hex_input {
return false;
}
if !matches!(ch.to_ascii_lowercase(), 'a'..='f') {
return false;
}
} else if !self.valid_chars.contains(ch) {
return false;
}
}
true
}
fn error(&self) {
}
fn options(&self) -> u16 {
self.options
}
}
pub type ValidatorRef = Rc<RefCell<dyn Validator>>;
pub struct FilterValidatorBuilder {
valid_chars: Option<String>,
}
impl FilterValidatorBuilder {
pub fn new() -> Self {
Self { valid_chars: None }
}
#[must_use]
pub fn valid_chars(mut self, chars: impl Into<String>) -> Self {
self.valid_chars = Some(chars.into());
self
}
pub fn build(self) -> FilterValidator {
let valid_chars = self.valid_chars.expect("FilterValidator valid_chars must be set");
FilterValidator::new(&valid_chars)
}
pub fn build_ref(self) -> ValidatorRef {
Rc::new(RefCell::new(self.build()))
}
}
impl Default for FilterValidatorBuilder {
fn default() -> Self {
Self::new()
}
}
pub struct RangeValidatorBuilder {
min: i64,
max: i64,
}
impl RangeValidatorBuilder {
pub fn new() -> Self {
Self { min: 0, max: 100 }
}
#[must_use]
pub fn min(mut self, min: i64) -> Self {
self.min = min;
self
}
#[must_use]
pub fn max(mut self, max: i64) -> Self {
self.max = max;
self
}
#[must_use]
pub fn range(mut self, min: i64, max: i64) -> Self {
self.min = min;
self.max = max;
self
}
pub fn build(self) -> RangeValidator {
RangeValidator::new(self.min, self.max)
}
pub fn build_ref(self) -> ValidatorRef {
Rc::new(RefCell::new(self.build()))
}
}
impl Default for RangeValidatorBuilder {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_filter_validator_digits() {
let validator = FilterValidator::new("0123456789");
assert!(validator.is_valid("123"));
assert!(validator.is_valid("0"));
assert!(!validator.is_valid("12a3"));
assert!(!validator.is_valid("abc"));
}
#[test]
fn test_filter_validator_alphanumeric() {
let validator = FilterValidator::new("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789");
assert!(validator.is_valid("abc123"));
assert!(validator.is_valid("Test123"));
assert!(!validator.is_valid("test@example.com"));
}
#[test]
fn test_range_validator_positive() {
let validator = RangeValidator::new(0, 100);
assert!(validator.is_valid("0"));
assert!(validator.is_valid("50"));
assert!(validator.is_valid("100"));
assert!(!validator.is_valid("101"));
assert!(!validator.is_valid("-1"));
assert!(!validator.is_valid("abc"));
}
#[test]
fn test_range_validator_negative() {
let validator = RangeValidator::new(-100, -1);
assert!(validator.is_valid("-1"));
assert!(validator.is_valid("-50"));
assert!(validator.is_valid("-100"));
assert!(!validator.is_valid("0"));
assert!(!validator.is_valid("-101"));
}
#[test]
fn test_range_validator_mixed() {
let validator = RangeValidator::new(-50, 50);
assert!(validator.is_valid("-50"));
assert!(validator.is_valid("0"));
assert!(validator.is_valid("50"));
assert!(!validator.is_valid("-51"));
assert!(!validator.is_valid("51"));
}
#[test]
fn test_range_validator_hex() {
let validator = RangeValidator::new(0, 255);
assert!(validator.is_valid("0xFF"));
assert!(validator.is_valid("0x00"));
assert!(validator.is_valid("0xAB"));
assert!(!validator.is_valid("0x100")); }
#[test]
fn test_range_validator_octal() {
let validator = RangeValidator::new(0, 100);
assert!(validator.is_valid("077")); assert!(validator.is_valid("0100")); assert!(!validator.is_valid("0200")); }
#[test]
fn test_range_validator_no_hex_letters_without_prefix() {
let validator = RangeValidator::new(1, 12);
assert!(validator.is_valid_input("1", false));
assert!(validator.is_valid_input("12", false));
assert!(!validator.is_valid_input("a", false));
assert!(!validator.is_valid_input("1a", false));
assert!(!validator.is_valid_input("a1", false));
assert!(!validator.is_valid_input("f", false));
assert!(validator.is_valid_input("0x", false));
assert!(validator.is_valid_input("0xa", false));
assert!(validator.is_valid_input("0xA", false));
assert!(validator.is_valid_input("0xFF", false));
}
#[test]
fn test_range_validator_hex_letters_after_prefix() {
let validator = RangeValidator::new(0, 255);
assert!(validator.is_valid_input("0x1", false));
assert!(validator.is_valid_input("0xa", false));
assert!(validator.is_valid_input("0xAB", false));
assert!(validator.is_valid_input("0xFF", false));
assert!(!validator.is_valid_input("a", false));
assert!(!validator.is_valid_input("FF", false));
assert!(validator.is_valid_input("123", false));
assert!(validator.is_valid_input("255", false));
}
#[test]
fn test_range_validator_hex_with_sign() {
let validator = RangeValidator::new(-255, 255);
assert!(validator.is_valid_input("+0x", false));
assert!(validator.is_valid_input("+0xFF", false));
assert!(validator.is_valid_input("-0x", false));
assert!(validator.is_valid_input("-0xFF", false));
assert!(!validator.is_valid_input("+a", false));
assert!(!validator.is_valid_input("-f", false));
}
}