#[derive(Debug, Clone)]
pub enum ValidatorKind {
Length(prove::length::Length),
Match(prove::r#match::Match),
Url(prove::url::Url),
Ip(prove::ip::Ip),
IpV4(prove::ip::IpV4),
IpV6(prove::ip::IpV6),
Email(prove::email::Email),
Nested,
}
#[derive(Debug)]
pub enum SimpleValidatorKind {
Url,
Ip,
IpV4,
IpV6,
Email,
}
#[derive(Debug)]
pub struct Validator {
pub validator: ValidatorKind,
pub message: String,
pub code: u64,
}
pub fn extract_nested_validator() -> Validator {
Validator {
validator: ValidatorKind::Nested,
message: "Nested validator error".to_owned(),
code: 0,
}
}
pub fn extract_length_validator(field_name: &str, metas: &Vec<syn::NestedMeta>) -> Validator {
let mut min = None;
let mut max = None;
let mut eq = None;
let (message, code) = extract_message_and_code("length", field_name, metas);
let error = |msg: &str| -> ! {
panic!(
"Invalid attribute #[prove] on field `{}`: {}",
field_name, msg
);
};
for meta in metas {
if let syn::NestedMeta::Meta(ref item) = *meta {
if let syn::Meta::NameValue(syn::MetaNameValue {
ref ident, ref lit, ..
}) = *item
{
match ident.to_string().as_ref() {
"message" | "code" => continue,
"min" => {
min = match *lit {
syn::Lit::Int(ref min) => Some(min.value()),
_ => error("invalid argument type for `min` of `length` validator: only u64 are allowed"),
};
},
"max" => {
max = match *lit {
syn::Lit::Int(ref max) => Some(max.value()),
_ => error("invalid argument type for `max` of `length` validator: only u64 are allowed"),
};
},
"eq" => {
eq= match *lit {
syn::Lit::Int(ref eq) => Some(eq.value()),
_ => error("invalid argument type for `eq` of `length` validator: only u64 are allowed"),
};
},
v => error(&format!(
"unknown argument `{}` for validator `length` (it only has `message`, `code`, `min`, `max`, `eq`)",
v
))
}
} else {
panic!(
"unexpected item {:?} while parsing `length` validator of field {}",
item, field_name
)
}
}
}
if eq.is_some() && (min.is_some() || max.is_some()) {
error(
"both `eq` and `min` or `max` have been set in `length` validator: probably a mistake",
);
}
if min.is_none() && max.is_none() && eq.is_none() {
error("Validator `length` requires at least 1 argument out of `message`, `min`, `max` and `eq`");
}
if min.is_some() && max.is_some() {
if min.unwrap() > max.unwrap() {
error("min must less then or equal max");
}
}
Validator {
validator: ValidatorKind::Length(prove::length::Length { min, max, eq }),
message: message.unwrap_or("Invalid length".to_owned()),
code: code.unwrap_or(0),
}
}
pub fn extract_match_validator(field_name: &str, metas: &Vec<syn::NestedMeta>) -> Validator {
let mut regex = None;
let (message, code) = extract_message_and_code("match", field_name, metas);
let error = |msg: &str| -> ! {
panic!(
"Invalid attribute #[prove] on field `{}`: {}",
field_name, msg
);
};
for meta in metas {
if let syn::NestedMeta::Meta(ref item) = *meta {
if let syn::Meta::NameValue(syn::MetaNameValue {
ref ident, ref lit, ..
}) = *item
{
match ident.to_string().as_ref() {
"message" | "code" => continue,
"regex" => {
regex = match *lit {
syn::Lit::Str(ref min) => Some(min.value()),
_ => error("invalid argument type for `regex` of `match` validator: only regex string are allowed"),
};
},
v => error(&format!(
"unknown argument `{}` for validator `match` (it only has `message`, `code`, `regex`)",
v
))
}
} else {
panic!(
"unexpected item {:?} while parsing `match` validator of field {}",
item, field_name
)
}
}
}
match regex {
Some(regex) => Validator {
validator: ValidatorKind::Match(prove::r#match::Match { regex }),
message: message.unwrap_or("Must match".to_owned()),
code: code.unwrap_or(0),
},
_ => error("Validator `match` require regex parameter"),
}
}
pub fn extract_simple_validator(
validator_kind: SimpleValidatorKind,
field_name: &str,
metas: &Vec<syn::NestedMeta>,
) -> Validator {
let (message, code) =
extract_message_and_code(&format!("{:?}", validator_kind), field_name, metas);
let error = |msg: &str| -> ! {
panic!(
"Invalid attribute #[prove] on field `{}`: {}",
field_name, msg
);
};
for meta in metas {
if let syn::NestedMeta::Meta(ref item) = *meta {
if let syn::Meta::NameValue(syn::MetaNameValue { ref ident, .. }) = *item {
match ident.to_string().as_ref() {
"message" | "code" => continue,
v => error(&format!(
"unknown argument `{}` for validator `{:?}` (it only has `message`, `code`)",
v, validator_kind
))
}
} else {
panic!(
"unexpected item {:?} while parsing `{:?}` validator of field {}",
item, validator_kind, field_name
)
}
}
}
Validator {
validator: match validator_kind {
SimpleValidatorKind::Url => ValidatorKind::Url(prove::url::Url),
SimpleValidatorKind::Ip => ValidatorKind::Ip(prove::ip::Ip),
SimpleValidatorKind::IpV4 => ValidatorKind::IpV4(prove::ip::IpV4),
SimpleValidatorKind::IpV6 => ValidatorKind::IpV6(prove::ip::IpV6),
SimpleValidatorKind::Email => ValidatorKind::Email(prove::email::Email),
},
message: message.unwrap_or(format!("Invalid {:?}", validator_kind)),
code: code.unwrap_or(0),
}
}
fn extract_message_and_code(
validator_name: &str,
field_name: &str,
metas: &Vec<syn::NestedMeta>,
) -> (Option<String>, Option<u64>) {
let mut message = None;
let mut code = None;
for meta_item in metas {
if let syn::NestedMeta::Meta(syn::Meta::NameValue(syn::MetaNameValue {
ref ident,
ref lit,
..
})) = *meta_item
{
match ident.to_string().as_ref() {
"message" => {
message = match lit {
syn::Lit::Str(s) => Some(s.value()),
_ => panic!(
"Invalid argument type for `message` for validator `{}` on field `{}`: only a string is allowed",
validator_name, field_name
),
};
}
"code" => {
code = match lit {
syn::Lit::Int(s) => Some(s.value()),
_ => panic!(
"Invalid argument type for `code` for validator `{}` on field `{}`: only a u64 is allowed",
validator_name, field_name
),
}
}
_ => continue,
}
}
}
(message, code)
}