use reliakit_primitives::PrimitiveError;
use reliakit_validate::{ValidateResult, ValidationError, Violation};
use crate::primitives::JsonExtractErrorKind;
use crate::JsonObject;
pub struct JsonForm<'a> {
object: &'a JsonObject,
errors: ValidationError,
}
impl<'a> JsonForm<'a> {
pub fn new(object: &'a JsonObject) -> Self {
Self {
object,
errors: ValidationError::empty(),
}
}
pub fn str_field<T>(&mut self, field: &'static str) -> Option<T>
where
T: TryFrom<&'a str, Error = PrimitiveError>,
{
let object = self.object;
match object.get_str_as::<T>(field) {
Ok(value) => Some(value),
Err(error) => {
let message = match error.kind() {
JsonExtractErrorKind::Missing => "is required",
JsonExtractErrorKind::WrongType { .. } => "must be a string",
_ => "is invalid",
};
self.errors.push(Violation::with_field(field, message));
None
}
}
}
pub fn is_valid(&self) -> bool {
self.errors.is_empty()
}
pub fn errors(&self) -> &ValidationError {
&self.errors
}
pub fn finish(self) -> ValidateResult {
self.errors.finish()
}
}
#[cfg(all(test, feature = "validate"))]
mod tests {
use super::JsonForm;
use crate::parse_str;
use reliakit_primitives::{Email, Hostname};
fn obj(input: &str) -> crate::JsonObject {
parse_str(input).unwrap().as_object().unwrap().clone()
}
#[test]
fn all_fields_valid_finishes_ok() {
let o = obj(r#"{ "email": "ops@example.com", "host": "api.example.com" }"#);
let mut form = JsonForm::new(&o);
let email: Option<Email> = form.str_field("email");
let host: Option<Hostname> = form.str_field("host");
assert_eq!(email.unwrap().as_str(), "ops@example.com");
assert_eq!(host.unwrap().as_str(), "api.example.com");
assert!(form.is_valid());
assert!(form.finish().is_ok());
}
#[test]
fn collects_every_failure_together() {
let o = obj(r#"{ "email": "nope", "host": 42 }"#);
let mut form = JsonForm::new(&o);
assert!(form.str_field::<Email>("email").is_none());
assert!(form.str_field::<Hostname>("host").is_none());
assert!(form.str_field::<Email>("missing").is_none());
assert!(!form.is_valid());
let errors = form.finish().unwrap_err();
let v = errors.violations();
assert_eq!(v.len(), 3);
assert_eq!(v[0].field, Some("email"));
assert_eq!(v[0].message, "is invalid");
assert_eq!(v[1].field, Some("host"));
assert_eq!(v[1].message, "must be a string");
assert_eq!(v[2].field, Some("missing"));
assert_eq!(v[2].message, "is required");
}
#[test]
fn errors_accessor_tracks_progress() {
let o = obj(r#"{ "email": "ops@example.com" }"#);
let mut form = JsonForm::new(&o);
let _: Option<Email> = form.str_field("email");
assert!(form.errors().is_empty());
let _: Option<Hostname> = form.str_field("host"); assert_eq!(form.errors().len(), 1);
}
}