use std::any::Any;
use std::collections::HashSet;
use async_trait::async_trait;
use mongodb::bson::{doc, Bson, Document};
use mongodb::{Collection, Database};
use mongodb::bson::oid::ObjectId;
use regex::Regex;
use serde_json::Value;
use crate::error::ValidationError;
use crate::traits::{ValidationResult, Validator};
pub struct Rule;
impl Rule {
pub fn required() -> impl Validator {
move |value: &Value| {
if value.is_null() {
Err(ValidationError::Required)
} else {
Ok(())
}
}
}
pub fn string() -> impl Validator {
move |value: &Value| {
if value.is_null() {
return Ok(())
}
if !value.is_string() {
Err(ValidationError::TypeError {
expected: "string".to_string(),
got: value.to_string()
})
} else {
Ok(())
}
}
}
pub fn array() -> impl Validator {
move |value: &Value| {
if value.is_null() {
return Ok(())
}
if !value.is_array() {
Err(ValidationError::TypeError {
expected: "array".to_string(),
got: value.to_string()
})
} else {
Ok(())
}
}
}
pub fn object() -> impl Validator {
move |value: &Value| {
if value.is_null() {
return Ok(())
}
if !value.is_object() {
Err(ValidationError::TypeError {
expected: "object".to_string(),
got: value.to_string()
})
} else {
Ok(())
}
}
}
pub fn boolean() -> impl Validator {
move |value: &Value| {
if value.is_null() {
return Ok(())
}
if !value.is_boolean() {
Err(ValidationError::TypeError {
expected: "bool".to_string(),
got: value.to_string()
})
} else {
Ok(())
}
}
}
pub fn float() -> impl Validator {
move |value: &Value| {
if value.is_null() {
return Ok(())
}
if !value.is_f64() {
Err(ValidationError::TypeError {
expected: "float".to_string(),
got: value.to_string()
})
} else {
Ok(())
}
}
}
pub fn integer() -> impl Validator {
move |value: &Value| {
if value.is_null() {
return Ok(())
}
if !value.is_i64() {
Err(ValidationError::TypeError {
expected: "int".to_string(),
got: value.to_string()
})
} else {
Ok(())
}
}
}
pub fn length(len: usize) -> impl Validator {
LengthValidator { length: len }
}
pub fn min_length(min: usize) -> impl Validator {
MinLengthValidator { min }
}
pub fn max_length(max: usize) -> impl Validator {
MaxLengthValidator { max }
}
pub fn equal(value: Value) -> impl Validator {
EqualValidator { value }
}
pub fn min_value(min: f64) -> impl Validator {
MinValueValidator { min }
}
pub fn max_value(max: f64) -> impl Validator {
MaxValueValidator { max }
}
pub fn numeric() -> impl Validator {
let numeric_regex = Regex::new(r"^-?\d+(?:\.\d+)?$").unwrap();
move |value: &Value| {
if value.is_null() {
return Ok(());
}
if let Value::String(s) = value {
let s = s.trim();
if numeric_regex.is_match(s) {
return Ok(());
}
}
Err(ValidationError::NumericError(value.to_string()))
}
}
pub fn accepted() -> impl Validator {
move |value: &Value| {
if value.is_null() {
return Ok(())
}
let s = match value {
Value::String(s) => s.to_lowercase(),
Value::Bool(b) => b.to_string(),
Value::Number(n) => n.to_string(),
_ => return Err(ValidationError::TypeError {
expected: "string, bool, or number".to_string(),
got: value.to_string(),
}),
};
if matches!(s.as_str(), "yes" | "on" | "1" | "true") {
Ok(())
} else {
Err(ValidationError::AcceptedError(value.to_string()))
}
}
}
pub fn email(allowed_domains: Option<Vec<String>>) -> impl Validator {
EmailValidator {
allowed_domains: allowed_domains.map(|v| v.into_iter().collect()),
}
}
pub fn in_values(values: Vec<Value>) -> impl Validator {
InValidator {
values
}
}
pub fn not_in_values(values: Vec<Value>) -> impl Validator {
NotInValidator {
values
}
}
pub fn regex(pattern: &str, message: Option<String>) -> Result<impl Validator, regex::Error> {
Ok(RegexValidator {
pattern: Regex::new(pattern)?,
message,
})
}
pub fn url() -> impl Validator {
move |value: &Value| {
if value.is_null() {
return Ok(())
}
let s = match value {
Value::String(s) => s,
_ => return Err(ValidationError::TypeError {
expected: "string".to_string(),
got: value.to_string(),
}),
};
let pattern = r#"(?i)\b((?:https?://|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}/)(?:[^\s()<>]+|\(([^\s()<>]+|(\([^\s()<>]+\)))*\))+(?:\(([^\s()<>]+|(\([^\s()<>]+\)))*\)|[^\s`!()\[\]{};:'\".,<>?«»“”‘’]))"#;
let re = Regex::new(pattern).unwrap();
if re.is_match(s) {
Ok(())
} else {
Err(ValidationError::UrlError(s.clone()))
}
}
}
pub fn ip() -> impl Validator {
move |value: &Value| {
if value.is_null() {
return Ok(())
}
let s = match value {
Value::String(s) => s,
_ => return Err(ValidationError::TypeError {
expected: "string".to_string(),
got: value.to_string(),
}),
};
let re = Regex::new(r"^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$").unwrap();
if let Some(caps) = re.captures(s) {
if caps.iter().skip(1).all(|m| m.unwrap().as_str().parse::<u8>().is_ok()) {
return Ok(());
}
}
Err(ValidationError::IpError(s.clone()))
}
}
pub fn extensions(allowed: Vec<String>) -> impl Validator {
ExtensionValidator {
allowed: allowed.into_iter().collect(),
}
}
pub fn custom<F>(validator: F) -> impl Validator
where
F: Fn(&Value) -> ValidationResult+Send+Sync+ 'static
{
validator
}
pub fn unique(collection: &str, field: &str,exclude:Option<ObjectId>) -> impl Validator {
UniqueValidator::new(collection, field,exclude)
}
}
struct UniqueValidator {
collection: String,
field: String,
current_id: Option<ObjectId>,
}
impl UniqueValidator {
pub fn new(collection: &str, field: &str,exclude:Option<ObjectId>) -> Self {
Self {
collection: collection.to_string(),
field: field.to_string(),
current_id: exclude,
}
}
}
#[async_trait]
impl Validator for UniqueValidator {
fn validate(&self, value: &Value) -> ValidationResult {
if value.is_null() {
return Ok(());
}
Err(ValidationError::Custom("Async validation required".to_string()))
}
async fn validate_async(&self, db: &Database, value: &Value) -> ValidationResult {
if value.is_null() {
return Ok(());
}
let collection: Collection<Document> = db.collection(&self.collection);
let field_value = match value {
Value::String(s) => Bson::String(s.clone()),
Value::Number(n) if n.is_i64() => Bson::Int64(n.as_i64().unwrap()),
Value::Number(n) if n.is_f64() => Bson::Double(n.as_f64().unwrap()),
_ => return Err(ValidationError::TypeError {
expected: "string or number".to_string(),
got: value.to_string(),
}),
};
let mut filter = doc! { &self.field: field_value };
if let Some(current_id) = &self.current_id {
filter.insert("_id", doc! { "$ne": current_id });
}
match collection.count_documents(filter).await {
Ok(count) if count > 0 => {
Err(ValidationError::UniqueError)
}
Ok(_) => Ok(()),
Err(_) => {
Err(ValidationError::Custom("Database error".to_string()))
}
}
}
fn as_any(&self) -> &dyn Any {
self
}
}
struct ExtensionValidator {
allowed: HashSet<String>,
}
impl Validator for ExtensionValidator {
fn validate(&self, value: &Value) -> ValidationResult {
if value.is_null() {
return Ok(())
}
let s = match value {
Value::String(s) => s,
_ => return Err(ValidationError::TypeError {
expected: "string".to_string(),
got: value.to_string(),
}),
};
if let Some(ext) = s.split('.').last() {
if self.allowed.contains(ext) {
return Ok(());
}
}
Err(ValidationError::ExtensionError(
self.allowed.iter().cloned().collect(),
))
}
fn as_any(&self) -> &dyn Any {
self
}
}
struct RegexValidator {
pattern: Regex,
message: Option<String>,
}
impl Validator for RegexValidator {
fn validate(&self, value: &Value) -> ValidationResult {
if value.is_null() {
return Ok(())
}
let s = match value {
Value::String(s) => s,
_ => return Err(ValidationError::TypeError {
expected: "string".to_string(),
got: value.to_string(),
}),
};
if self.pattern.is_match(s) {
Ok(())
} else {
Err(ValidationError::RegexError(
self.message.as_ref().map_or(s.clone(), |m| m.clone()),
))
}
}
fn as_any(&self) -> &dyn Any {
self
}
}
struct NotInValidator {
values: Vec<Value>,
}
impl Validator for NotInValidator {
fn validate(&self, value: &Value) -> ValidationResult {
if value.is_null() {
return Ok(())
}
if !self.values.contains(value) {
return Ok(())
}
Err(ValidationError::NotInError(format!("{:?}", self.values)))
}
fn as_any(&self) -> &dyn Any {
self
}
}
struct InValidator {
values: Vec<Value>,
}
impl Validator for InValidator {
fn validate(&self, value: &Value) -> ValidationResult {
if value.is_null() {
return Ok(())
}
if self.values.contains(value) {
return Ok(())
}
Err(ValidationError::InError(format!("{:?}", self.values)))
}
fn as_any(&self) -> &dyn Any {
self
}
}
struct EmailValidator {
allowed_domains: Option<HashSet<String>>,
}
impl Validator for EmailValidator {
fn validate(&self, value: &Value) -> ValidationResult {
if value.is_null() {
return Ok(())
}
let email = match value {
Value::String(s) => s,
_ => return Err(ValidationError::TypeError {
expected: "string".to_string(),
got: value.to_string(),
}),
};
let parts: Vec<&str> = email.split('@').collect();
if parts.len() != 2 {
return Err(ValidationError::EmailError(email.clone()));
}
let name = parts[0];
let domain = parts[1];
let domain_parts: Vec<&str> = domain.split('.').collect();
if domain_parts.len() < 2 {
return Err(ValidationError::EmailError(email.clone()));
}
if domain_parts[1].len() < 2 {
return Err(ValidationError::EmailError(email.clone()));
}
if let Some(allowed) = &self.allowed_domains {
if !allowed.contains(domain) {
return Err(ValidationError::EmailDomainError(domain.to_string()));
}
}
if name.len() < 3 {
return Err(ValidationError::EmailError(email.clone()));
}
Ok(())
}
fn as_any(&self) -> &dyn Any {
self
}
}
struct MaxValueValidator {
max: f64,
}
impl Validator for MaxValueValidator {
fn validate(&self, value: &Value) -> ValidationResult {
if value.is_null() {
return Ok(())
}
let num = match value {
Value::Number(n) => n.as_f64().ok_or(ValidationError::TypeError {
expected: "number".to_string(),
got: value.to_string(),
})?,
_ => return Err(ValidationError::TypeError {
expected: "number".to_string(),
got: value.to_string(),
}),
};
if num <= self.max {
Ok(())
} else {
Err(ValidationError::MaxValueError {
expected: self.max,
got: num,
})
}
}
fn as_any(&self) -> &dyn Any {
self
}
}
struct MinValueValidator {
min: f64,
}
impl Validator for MinValueValidator {
fn validate(&self, value: &Value) -> ValidationResult {
if value.is_null() {
return Ok(())
}
let num = match value {
Value::Number(n) => n.as_f64().ok_or(ValidationError::TypeError {
expected: "number".to_string(),
got: value.to_string(),
})?,
_ => return Err(ValidationError::TypeError {
expected: "number".to_string(),
got: value.to_string(),
}),
};
if num >= self.min {
Ok(())
} else {
Err(ValidationError::MinValueError {
expected: self.min,
got: num,
})
}
}
fn as_any(&self) -> &dyn Any {
self
}
}
struct EqualValidator {
value: Value,
}
impl Validator for EqualValidator {
fn validate(&self, value: &Value) -> ValidationResult {
if value.is_null() {
return Ok(())
}
if value == &self.value {
Ok(())
} else {
Err(ValidationError::EqualError {
expected: self.value.to_string(),
got: value.to_string(),
})
}
}
fn as_any(&self) -> &dyn Any {
self
}
}
struct MaxLengthValidator {
max: usize,
}
impl Validator for MaxLengthValidator {
fn validate(&self, value: &Value) -> ValidationResult {
if value.is_null() {
return Ok(())
}
let len = match value {
Value::String(s) => s.len(),
Value::Array(a) => a.len(),
Value::Object(o) => o.len(),
_ => return Err(ValidationError::TypeError {
expected: "string, array, or object".to_string(),
got: value.to_string(),
}),
};
if len <= self.max {
Ok(())
} else {
Err(ValidationError::MaxLengthError {
expected: self.max,
got: len,
})
}
}
fn as_any(&self) -> &dyn Any {
self
}
}
struct MinLengthValidator {
min: usize,
}
impl Validator for MinLengthValidator {
fn validate(&self, value: &Value) -> ValidationResult {
if value.is_null() {
return Ok(())
}
let len = match value {
Value::String(s) => s.len(),
Value::Array(a) => a.len(),
Value::Object(o) => o.len(),
_ => return Err(ValidationError::TypeError {
expected: "string, array, or object".to_string(),
got: value.to_string(),
}),
};
if len >= self.min {
Ok(())
} else {
Err(ValidationError::MinLengthError {
expected: self.min,
got: len,
})
}
}
fn as_any(&self) -> &dyn Any {
self
}
}
struct LengthValidator {
length: usize,
}
impl Validator for LengthValidator {
fn validate(&self, value: &Value) -> ValidationResult {
if value.is_null() {
return Ok(())
}
let len = match value {
Value::String(s) => s.len(),
Value::Array(a) => a.len(),
Value::Object(o) => o.len(),
_ => return Err(ValidationError::TypeError {
expected: "string, array, or object".to_string(),
got: value.to_string(),
}),
};
if len == self.length {
Ok(())
} else {
Err(ValidationError::LengthError {
expected: self.length,
got: len,
})
}
}
fn as_any(&self) -> &dyn Any {
self
}
}