use crate::core::validators::{
EmailValidator, MaxLengthValidator, ProhibitNullCharactersValidator, URLValidator,
ValidationError, Validator, validate_ipv4_address, validate_ipv46_address, validate_slug,
};
use super::FieldType;
pub fn validate_char_field(value: &str, max_length: usize) -> Result<(), ValidationError> {
ProhibitNullCharactersValidator::default().validate(&value)?;
MaxLengthValidator::new(max_length).validate(&value)
}
pub fn validate_email_field(value: &str) -> Result<(), ValidationError> {
EmailValidator::default().validate(&value)
}
pub fn validate_url_field(value: &str) -> Result<(), ValidationError> {
URLValidator::default().validate(&value)
}
pub fn validate_slug_field(value: &str, max_length: usize) -> Result<(), ValidationError> {
validate_char_field(value, max_length)?;
validate_slug(value)
}
pub fn validate_ip_field(value: &str) -> Result<(), ValidationError> {
validate_ipv4_address(value)
}
pub fn validate_generic_ip_field(value: &str) -> Result<(), ValidationError> {
validate_ipv46_address(value)
}
pub fn validate_comma_separated_integer_field(
value: &str,
max_length: usize,
) -> Result<(), ValidationError> {
validate_char_field(value, max_length)?;
if value
.split(',')
.filter(|part| !part.is_empty())
.any(|part| part.trim().parse::<i64>().is_err())
{
return Err(
ValidationError::new("Enter only comma separated integers.", "invalid")
.with_param("value", value),
);
}
Ok(())
}
#[must_use]
pub fn db_type(field: &FieldType, vendor: &str) -> Option<String> {
let _ = vendor;
match field {
FieldType::Char { max_length }
| FieldType::Slug { max_length }
| FieldType::CommaSeparatedInteger { max_length } => Some(format!("varchar({max_length})")),
FieldType::Email => Some("varchar(254)".to_string()),
FieldType::Url => Some("varchar(200)".to_string()),
FieldType::Ip => Some("char(15)".to_string()),
FieldType::GenericIp => Some("char(39)".to_string()),
_ => None,
}
}
pub fn get_prep_value(field: &FieldType, value: &str) -> Result<String, ValidationError> {
let prepared = if let Some(max_length) = field.max_length() {
value.chars().take(max_length).collect::<String>()
} else {
value.to_string()
};
match field {
FieldType::Char { max_length } => validate_char_field(&prepared, *max_length)?,
FieldType::Email => validate_email_field(&prepared)?,
FieldType::Url => validate_url_field(&prepared)?,
FieldType::Slug { max_length } => validate_slug_field(&prepared, *max_length)?,
FieldType::Ip => validate_ip_field(&prepared)?,
FieldType::GenericIp => validate_generic_ip_field(&prepared)?,
FieldType::CommaSeparatedInteger { max_length } => {
validate_comma_separated_integer_field(&prepared, *max_length)?;
}
_ => {
return Err(ValidationError::new(
"Field type is not handled by char preparation.",
"invalid",
));
}
}
Ok(prepared)
}
pub fn from_db_value(value: &str) -> Result<String, ValidationError> {
ProhibitNullCharactersValidator::default().validate(&value)?;
Ok(value.to_string())
}
#[must_use]
pub fn formfield(field: &FieldType) -> Option<&'static str> {
match field {
FieldType::Char { .. }
| FieldType::CommaSeparatedInteger { .. }
| FieldType::Email
| FieldType::Url
| FieldType::Slug { .. }
| FieldType::Ip
| FieldType::GenericIp => Some("CharField"),
_ => None,
}
}
#[cfg(test)]
mod tests {
use super::{FieldType, db_type, formfield, from_db_value, get_prep_value};
#[test]
fn db_type_for_sqlite_uses_varchar_with_max_length() {
let field = FieldType::Char { max_length: 12 };
assert_eq!(db_type(&field, "sqlite").as_deref(), Some("varchar(12)"));
}
#[test]
fn get_prep_value_truncates_to_max_length() {
let field = FieldType::Char { max_length: 5 };
let prepared = get_prep_value(&field, "abcdef").expect("char preparation should succeed");
assert_eq!(prepared, "abcde");
}
#[test]
fn from_db_value_returns_identity() {
let value = from_db_value("stored value").expect("db value should round-trip");
assert_eq!(value, "stored value");
}
#[test]
fn formfield_returns_correct_type() {
let field = FieldType::Char { max_length: 32 };
assert_eq!(formfield(&field), Some("CharField"));
}
}