use serde_json::Value;
use super::{Validator, ValidatorOptions, value_is_blank};
use crate::errors::{ErrorType, Errors};
#[derive(Debug, Clone, Default)]
pub struct PresenceValidator {
pub message: Option<String>,
pub(crate) options: ValidatorOptions,
}
impl PresenceValidator {
#[must_use]
pub fn new() -> Self {
Self::default()
}
crate::validations::impl_common_validator_methods!();
#[must_use]
pub fn message(mut self, message: impl Into<String>) -> Self {
self.message = Some(message.into());
self
}
fn error_message(&self) -> String {
self.message
.clone()
.unwrap_or_else(|| "can't be blank".to_string())
}
}
impl Validator for PresenceValidator {
fn validate(&self, attribute: &str, value: Option<&Value>, errors: &mut Errors) {
if value_is_blank(value) {
errors.add(attribute, ErrorType::Blank, self.error_message());
}
}
fn name(&self) -> &str {
"presence"
}
fn options(&self) -> &ValidatorOptions {
&self.options
}
}
#[cfg(test)]
mod tests {
use serde_json::json;
use super::PresenceValidator;
use crate::{
errors::{ErrorType, Errors},
validations::{ValidationSet, Validator},
};
fn validate_value(validator: PresenceValidator, value: Option<serde_json::Value>) -> Errors {
let mut errors = Errors::new();
validator.validate("field", value.as_ref(), &mut errors);
errors
}
#[test]
fn nil_fails() {
let validator = PresenceValidator::new();
let mut errors = Errors::new();
validator.validate("name", None, &mut errors);
assert_eq!(errors.on("name")[0].error_type, ErrorType::Blank);
}
#[test]
fn null_fails() {
let validator = PresenceValidator::new();
let mut errors = Errors::new();
validator.validate("name", Some(&json!(null)), &mut errors);
assert_eq!(errors.on("name")[0].error_type, ErrorType::Blank);
}
#[test]
fn empty_string_fails() {
let validator = PresenceValidator::new();
let mut errors = Errors::new();
validator.validate("name", Some(&json!("")), &mut errors);
assert_eq!(errors.on("name")[0].message, "can't be blank");
}
#[test]
fn whitespace_string_fails() {
let validator = PresenceValidator::new();
let mut errors = Errors::new();
validator.validate("name", Some(&json!(" ")), &mut errors);
assert_eq!(errors.on("name")[0].error_type, ErrorType::Blank);
}
#[test]
fn false_is_treated_as_blank() {
let validator = PresenceValidator::new();
let mut errors = Errors::new();
validator.validate("published", Some(&json!(false)), &mut errors);
assert_eq!(errors.on("published")[0].error_type, ErrorType::Blank);
}
#[test]
fn present_string_passes() {
let validator = PresenceValidator::new();
let mut errors = Errors::new();
validator.validate("name", Some(&json!("Alice")), &mut errors);
assert!(errors.is_empty());
}
#[test]
fn number_passes() {
let validator = PresenceValidator::new();
let mut errors = Errors::new();
validator.validate("age", Some(&json!(42)), &mut errors);
assert!(errors.is_empty());
}
#[test]
fn custom_message_is_used() {
let validator = PresenceValidator::new().message("must exist");
let mut errors = Errors::new();
validator.validate("name", None, &mut errors);
assert_eq!(errors.on("name")[0].message, "must exist");
}
#[test]
fn empty_array_fails() {
let errors = validate_value(PresenceValidator::new(), Some(json!([])));
assert_eq!(errors.on("field")[0].error_type, ErrorType::Blank);
}
#[test]
fn empty_object_fails() {
let errors = validate_value(PresenceValidator::new(), Some(json!({})));
assert_eq!(errors.on("field")[0].error_type, ErrorType::Blank);
}
#[test]
fn non_empty_array_passes() {
let errors = validate_value(PresenceValidator::new(), Some(json!(["tag"])));
assert!(errors.is_empty());
}
#[test]
fn non_empty_object_passes() {
let errors = validate_value(PresenceValidator::new(), Some(json!({ "name": "Alice" })));
assert!(errors.is_empty());
}
#[test]
fn true_is_present() {
let errors = validate_value(PresenceValidator::new(), Some(json!(true)));
assert!(errors.is_empty());
}
#[test]
fn tab_and_newline_only_string_fails() {
let errors = validate_value(PresenceValidator::new(), Some(json!("\t\n")));
assert_eq!(errors.on("field")[0].error_type, ErrorType::Blank);
}
#[test]
fn allow_nil_skips_none_in_validation_set() {
let mut set = ValidationSet::new();
set.add("nickname", PresenceValidator::new().allow_nil());
let mut errors = Errors::new();
let _ = set.validate(&|_| None, &mut errors);
assert!(errors.is_empty());
}
#[test]
fn allow_blank_skips_whitespace_in_validation_set() {
let mut set = ValidationSet::new();
set.add("nickname", PresenceValidator::new().allow_blank());
let mut errors = Errors::new();
let _ = set.validate(&|_| Some(json!(" ")), &mut errors);
assert!(errors.is_empty());
}
#[test]
fn allow_blank_skips_empty_collection_in_validation_set() {
let mut set = ValidationSet::new();
set.add("tags", PresenceValidator::new().allow_blank());
let mut errors = Errors::new();
let _ = set.validate(&|_| Some(json!([])), &mut errors);
assert!(errors.is_empty());
}
#[test]
fn allow_blank_skips_empty_object_in_validation_set() {
let mut set = ValidationSet::new();
set.add("profile", PresenceValidator::new().allow_blank());
let mut errors = Errors::new();
let _ = set.validate(&|_| Some(json!({})), &mut errors);
assert!(errors.is_empty());
}
#[test]
fn full_message_is_humanized() {
let errors = validate_value(PresenceValidator::new(), None);
assert_eq!(
errors.full_messages(),
vec!["Field can't be blank".to_string()]
);
}
}