use crate::ts_syn::TsStream;
use super::super::{Validator, ValidatorSpec};
pub fn generate_validation_condition(validator: &Validator, value_var: &str) -> String {
match validator {
Validator::Email => {
format!(r#"!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test({value_var})"#)
}
Validator::Url => {
format!(
r#"(() => {{ try {{ new URL({value_var}); return false; }} catch {{ return true; }} }})()"#
)
}
Validator::Uuid => {
format!(
r#"!/^[0-9a-f]{{8}}-[0-9a-f]{{4}}-[1-5][0-9a-f]{{3}}-[89ab][0-9a-f]{{3}}-[0-9a-f]{{12}}$/i.test({value_var})"#
)
}
Validator::MaxLength(n) => format!("{value_var}.length > {n}"),
Validator::MinLength(n) => format!("{value_var}.length < {n}"),
Validator::Length(n) => format!("{value_var}.length !== {n}"),
Validator::LengthRange(min, max) => {
format!("{value_var}.length < {min} || {value_var}.length > {max}")
}
Validator::Pattern(regex) => {
let escaped = regex.replace('/', "\\/");
format!("!/{escaped}/.test({value_var})")
}
Validator::NonEmpty => format!("{value_var} != null && {value_var}.length === 0"),
Validator::Trimmed => format!("{value_var} !== {value_var}.trim()"),
Validator::Lowercase => format!("{value_var} !== {value_var}.toLowerCase()"),
Validator::Uppercase => format!("{value_var} !== {value_var}.toUpperCase()"),
Validator::Capitalized => {
format!("{value_var}.length > 0 && {value_var}[0] !== {value_var}[0].toUpperCase()")
}
Validator::Uncapitalized => {
format!("{value_var}.length > 0 && {value_var}[0] !== {value_var}[0].toLowerCase()")
}
Validator::StartsWith(prefix) => format!(r#"!{value_var}.startsWith("{prefix}")"#),
Validator::EndsWith(suffix) => format!(r#"!{value_var}.endsWith("{suffix}")"#),
Validator::Includes(substr) => format!(r#"!{value_var}.includes("{substr}")"#),
Validator::GreaterThan(n) => format!("{value_var} <= {n}"),
Validator::GreaterThanOrEqualTo(n) => format!("{value_var} < {n}"),
Validator::LessThan(n) => format!("{value_var} >= {n}"),
Validator::LessThanOrEqualTo(n) => format!("{value_var} > {n}"),
Validator::Between(min, max) => format!("{value_var} < {min} || {value_var} > {max}"),
Validator::Int => format!("!Number.isInteger({value_var})"),
Validator::NonNaN => format!("Number.isNaN({value_var})"),
Validator::Finite => format!("!Number.isFinite({value_var})"),
Validator::Positive => format!("{value_var} <= 0"),
Validator::NonNegative => format!("{value_var} < 0"),
Validator::Negative => format!("{value_var} >= 0"),
Validator::NonPositive => format!("{value_var} > 0"),
Validator::MultipleOf(n) => format!("{value_var} % {n} !== 0"),
Validator::Uint8 => {
format!("!Number.isInteger({value_var}) || {value_var} < 0 || {value_var} > 255")
}
Validator::MaxItems(n) => format!("{value_var}.length > {n}"),
Validator::MinItems(n) => format!("{value_var}.length < {n}"),
Validator::ItemsCount(n) => format!("{value_var}.length !== {n}"),
Validator::ValidDate => format!("{value_var} == null || isNaN({value_var}.getTime())"),
Validator::GreaterThanDate(date) => {
format!(
r#"{value_var} == null || {value_var}.getTime() <= new Date("{date}").getTime()"#
)
}
Validator::GreaterThanOrEqualToDate(date) => {
format!(
r#"{value_var} == null || {value_var}.getTime() < new Date("{date}").getTime()"#
)
}
Validator::LessThanDate(date) => {
format!(
r#"{value_var} == null || {value_var}.getTime() >= new Date("{date}").getTime()"#
)
}
Validator::LessThanOrEqualToDate(date) => {
format!(
r#"{value_var} == null || {value_var}.getTime() > new Date("{date}").getTime()"#
)
}
Validator::BetweenDate(min, max) => {
format!(
r#"{value_var} == null || {value_var}.getTime() < new Date("{min}").getTime() || {value_var}.getTime() > new Date("{max}").getTime()"#
)
}
Validator::GreaterThanBigInt(n) => format!("{value_var} <= BigInt({n})"),
Validator::GreaterThanOrEqualToBigInt(n) => format!("{value_var} < BigInt({n})"),
Validator::LessThanBigInt(n) => format!("{value_var} >= BigInt({n})"),
Validator::LessThanOrEqualToBigInt(n) => format!("{value_var} > BigInt({n})"),
Validator::BetweenBigInt(min, max) => {
format!("{value_var} < BigInt({min}) || {value_var} > BigInt({max})")
}
Validator::PositiveBigInt => format!("{value_var} <= 0n"),
Validator::NonNegativeBigInt => format!("{value_var} < 0n"),
Validator::NegativeBigInt => format!("{value_var} >= 0n"),
Validator::NonPositiveBigInt => format!("{value_var} > 0n"),
Validator::Custom(_) => String::new(),
}
}
pub(super) fn generate_field_validations(
validators: &[ValidatorSpec],
value_var: &str,
field_name: &str,
context_name: &str,
) -> TsStream {
let mut code = String::new();
for spec in validators {
let condition = match &spec.validator {
Validator::Email => format!("!/^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test({value_var})"),
Validator::Url => format!("!/^https?:\\/\\/.+/.test({value_var})"),
Validator::Uuid => format!(
"!/^[0-9a-f]{{8}}-[0-9a-f]{{4}}-[0-9a-f]{{4}}-[0-9a-f]{{4}}-[0-9a-f]{{12}}$/i.test({value_var})"
),
Validator::MaxLength(n) => format!("{value_var}.length > {n}"),
Validator::MinLength(n) => format!("{value_var}.length < {n}"),
Validator::Length(n) => format!("{value_var}.length !== {n}"),
Validator::LengthRange(min, max) => {
format!("{value_var}.length < {min} || {value_var}.length > {max}")
}
Validator::Pattern(pattern) => {
let escaped = pattern.replace('/', "\\/");
format!("!/{escaped}/.test({value_var})")
}
Validator::NonEmpty => {
format!("{value_var} !== null && {value_var}.trim().length === 0")
}
Validator::Trimmed => format!("{value_var} !== {value_var}.trim()"),
Validator::Lowercase => format!("{value_var} !== {value_var}.toLowerCase()"),
Validator::Uppercase => format!("{value_var} !== {value_var}.toUpperCase()"),
Validator::Capitalized => format!(
"{value_var}.length === 0 || {value_var}[0] !== {value_var}[0].toUpperCase() || {value_var}.slice(1) !== {value_var}.slice(1).toLowerCase()"
),
Validator::Uncapitalized => {
format!("{value_var}.length > 0 && {value_var}[0] !== {value_var}[0].toLowerCase()")
}
Validator::StartsWith(prefix) => format!("!{value_var}.startsWith(\"{prefix}\")"),
Validator::EndsWith(suffix) => format!("!{value_var}.endsWith(\"{suffix}\")"),
Validator::Includes(text) => format!("!{value_var}.includes(\"{text}\")"),
Validator::GreaterThan(n) => format!("{value_var} <= {n}"),
Validator::GreaterThanOrEqualTo(n) => format!("{value_var} < {n}"),
Validator::LessThan(n) => format!("{value_var} >= {n}"),
Validator::LessThanOrEqualTo(n) => format!("{value_var} > {n}"),
Validator::Between(min, max) => format!("{value_var} < {min} || {value_var} > {max}"),
Validator::Int => format!("!Number.isInteger({value_var})"),
Validator::NonNaN => format!("Number.isNaN({value_var})"),
Validator::Finite => format!("!Number.isFinite({value_var})"),
Validator::Positive => format!("{value_var} <= 0"),
Validator::NonNegative => format!("{value_var} < 0"),
Validator::Negative => format!("{value_var} >= 0"),
Validator::NonPositive => format!("{value_var} > 0"),
Validator::MultipleOf(n) => format!("{value_var} % {n} !== 0"),
Validator::Uint8 => {
format!("!Number.isInteger({value_var}) || {value_var} < 0 || {value_var} > 255")
}
Validator::MaxItems(n) => format!("{value_var}.length > {n}"),
Validator::MinItems(n) => format!("{value_var}.length < {n}"),
Validator::ItemsCount(n) => format!("{value_var}.length !== {n}"),
Validator::ValidDate => format!("isNaN({value_var}.getTime())"),
Validator::GreaterThanDate(date) => {
format!("{value_var}.getTime() <= new Date(\"{date}\").getTime()")
}
Validator::GreaterThanOrEqualToDate(date) => {
format!("{value_var}.getTime() < new Date(\"{date}\").getTime()")
}
Validator::LessThanDate(date) => {
format!("{value_var}.getTime() >= new Date(\"{date}\").getTime()")
}
Validator::LessThanOrEqualToDate(date) => {
format!("{value_var}.getTime() > new Date(\"{date}\").getTime()")
}
Validator::BetweenDate(min, max) => format!(
"{value_var}.getTime() < new Date(\"{min}\").getTime() || {value_var}.getTime() > new Date(\"{max}\").getTime()"
),
Validator::GreaterThanBigInt(n) => format!("{value_var} <= {n}n"),
Validator::GreaterThanOrEqualToBigInt(n) => format!("{value_var} < {n}n"),
Validator::LessThanBigInt(n) => format!("{value_var} >= {n}n"),
Validator::LessThanOrEqualToBigInt(n) => format!("{value_var} > {n}n"),
Validator::BetweenBigInt(min, max) => {
format!("{value_var} < {min}n || {value_var} > {max}n")
}
Validator::PositiveBigInt => format!("{value_var} <= 0n"),
Validator::NonNegativeBigInt => format!("{value_var} < 0n"),
Validator::NegativeBigInt => format!("{value_var} >= 0n"),
Validator::NonPositiveBigInt => format!("{value_var} > 0n"),
Validator::Custom(fn_name) => format!("!{fn_name}({value_var})"),
};
let default_message =
get_default_validator_message(&spec.validator, field_name, context_name);
let message = spec.custom_message.as_ref().unwrap_or(&default_message);
let escaped_message = message.replace('\\', "\\\\").replace('"', "\\\"");
code.push_str(&format!(
" if ({condition}) {{ errors.push({{ field: \"{field_name}\", message: \"{escaped_message}\" }}); }}\n"
));
}
TsStream::from_string(code)
}
pub(super) fn get_default_validator_message(
validator: &Validator,
field_name: &str,
context_name: &str,
) -> String {
match validator {
Validator::Email => format!("{context_name}.{field_name} must be a valid email"),
Validator::Url => format!("{context_name}.{field_name} must be a valid URL"),
Validator::Uuid => format!("{context_name}.{field_name} must be a valid UUID"),
Validator::MaxLength(n) => {
format!("{context_name}.{field_name} must have at most {n} characters")
}
Validator::MinLength(n) => {
format!("{context_name}.{field_name} must have at least {n} characters")
}
Validator::Length(n) => {
format!("{context_name}.{field_name} must have exactly {n} characters")
}
Validator::LengthRange(min, max) => {
format!("{context_name}.{field_name} must have between {min} and {max} characters")
}
Validator::Pattern(pattern) => {
format!("{context_name}.{field_name} must match pattern {pattern}")
}
Validator::NonEmpty => format!("{context_name}.{field_name} must not be empty"),
Validator::Trimmed => format!("{context_name}.{field_name} must be trimmed"),
Validator::Lowercase => format!("{context_name}.{field_name} must be lowercase"),
Validator::Uppercase => format!("{context_name}.{field_name} must be uppercase"),
Validator::Capitalized => format!("{context_name}.{field_name} must be capitalized"),
Validator::Uncapitalized => format!("{context_name}.{field_name} must be uncapitalized"),
Validator::StartsWith(prefix) => {
format!("{context_name}.{field_name} must start with '{prefix}'")
}
Validator::EndsWith(suffix) => {
format!("{context_name}.{field_name} must end with '{suffix}'")
}
Validator::Includes(text) => format!("{context_name}.{field_name} must include '{text}'"),
Validator::GreaterThan(n) => {
format!("{context_name}.{field_name} must be greater than {n}")
}
Validator::GreaterThanOrEqualTo(n) => {
format!("{context_name}.{field_name} must be greater than or equal to {n}")
}
Validator::LessThan(n) => format!("{context_name}.{field_name} must be less than {n}"),
Validator::LessThanOrEqualTo(n) => {
format!("{context_name}.{field_name} must be less than or equal to {n}")
}
Validator::Between(min, max) => {
format!("{context_name}.{field_name} must be between {min} and {max}")
}
Validator::Int => format!("{context_name}.{field_name} must be an integer"),
Validator::NonNaN => format!("{context_name}.{field_name} must not be NaN"),
Validator::Finite => format!("{context_name}.{field_name} must be finite"),
Validator::Positive => format!("{context_name}.{field_name} must be positive"),
Validator::NonNegative => format!("{context_name}.{field_name} must be non-negative"),
Validator::Negative => format!("{context_name}.{field_name} must be negative"),
Validator::NonPositive => format!("{context_name}.{field_name} must be non-positive"),
Validator::MultipleOf(n) => {
format!("{context_name}.{field_name} must be a multiple of {n}")
}
Validator::Uint8 => format!("{context_name}.{field_name} must be a uint8"),
Validator::MaxItems(n) => {
format!("{context_name}.{field_name} must have at most {n} items")
}
Validator::MinItems(n) => {
format!("{context_name}.{field_name} must have at least {n} items")
}
Validator::ItemsCount(n) => {
format!("{context_name}.{field_name} must have exactly {n} items")
}
Validator::ValidDate => format!("{context_name}.{field_name} must be a valid date"),
Validator::GreaterThanDate(date) => {
format!("{context_name}.{field_name} must be after {date}")
}
Validator::GreaterThanOrEqualToDate(date) => {
format!("{context_name}.{field_name} must be on or after {date}")
}
Validator::LessThanDate(date) => {
format!("{context_name}.{field_name} must be before {date}")
}
Validator::LessThanOrEqualToDate(date) => {
format!("{context_name}.{field_name} must be on or before {date}")
}
Validator::BetweenDate(min, max) => {
format!("{context_name}.{field_name} must be between {min} and {max}")
}
Validator::GreaterThanBigInt(n) => {
format!("{context_name}.{field_name} must be greater than {n}")
}
Validator::GreaterThanOrEqualToBigInt(n) => {
format!("{context_name}.{field_name} must be greater than or equal to {n}")
}
Validator::LessThanBigInt(n) => {
format!("{context_name}.{field_name} must be less than {n}")
}
Validator::LessThanOrEqualToBigInt(n) => {
format!("{context_name}.{field_name} must be less than or equal to {n}")
}
Validator::BetweenBigInt(min, max) => {
format!("{context_name}.{field_name} must be between {min} and {max}")
}
Validator::PositiveBigInt => format!("{context_name}.{field_name} must be positive"),
Validator::NonNegativeBigInt => format!("{context_name}.{field_name} must be non-negative"),
Validator::NegativeBigInt => format!("{context_name}.{field_name} must be negative"),
Validator::NonPositiveBigInt => format!("{context_name}.{field_name} must be non-positive"),
Validator::Custom(fn_name) => {
format!("{context_name}.{field_name} failed custom validation ({fn_name})")
}
}
}