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 {
input.chars().all(|ch| self.valid_chars.contains(ch))
}
fn error(&self) {
}
fn options(&self) -> u16 {
self.options
}
}
pub type ValidatorRef = Rc<RefCell<dyn Validator>>;
#[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")); }
}