Skip to content

Commit

Permalink
add support for validation callbacks
Browse files Browse the repository at this point in the history
close #197
  • Loading branch information
jordens committed Apr 12, 2024
1 parent 08a68fc commit 2d7229a
Show file tree
Hide file tree
Showing 4 changed files with 72 additions and 3 deletions.
13 changes: 13 additions & 0 deletions miniconf/src/tree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,11 @@ pub enum Error<E> {
/// The [`TreeDeserialize::deserialize_by_key()`] update takes place but this
/// error will be returned.
PostDeserialization(E),

/// The value was found to be invalid after deserialization.
///
/// The validation callback returned an error.
Invalid(usize, &'static str),
}

impl<E: core::fmt::Display> Display for Error<E> {
Expand All @@ -68,6 +73,13 @@ impl<E: core::fmt::Display> Display for Error<E> {
write!(f, "Error after deserialization: ")?;
error.fmt(f)
}
Error::Invalid(index, msg) => {
write!(
f,
"The deserialized value is invalid (Key level: {}): {}",
index, msg
)
}
}
}
}
Expand All @@ -92,6 +104,7 @@ impl<E> Increment for Result<usize, Error<E>> {
Err(Error::TooShort(i)) => Err(Error::TooShort(i + 1)),
Err(Error::TooLong(i)) => Err(Error::TooLong(i + 1)),
Err(Error::Absent(i)) => Err(Error::Absent(i + 1)),
Err(Error::Invalid(i, msg)) => Err(Error::Invalid(i + 1, msg)),
e => e,
}
}
Expand Down
38 changes: 38 additions & 0 deletions miniconf/tests/validate.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
use miniconf::{Error, JsonCoreSlash, Tree};

#[derive(Tree, Default)]
struct Inner {
a: f32,
}

#[derive(Tree, Default)]
struct Settings {
#[tree(validate=Self::check_v)]
v: f32,
#[tree(validate=Self::check_i, depth=1)]
i: Inner,
}

impl Settings {
fn check_v(&self, _field: &str, new: &mut f32, _old: &f32) -> Result<(), &'static str> {
if new.is_sign_negative() {
Err("Must not be negative.")
} else {
Ok(())
}
}
fn check_i(_field: &str, _new: &mut Inner) -> Result<(), &'static str> {
Ok(())
}
}

#[test]
fn validate() {
let mut s = Settings::default();
s.set_json("/v", "1.0".as_bytes()).unwrap();
assert_eq!(s.v, 1.0);
s.set_json("/v", "-1.0".as_bytes()).unwrap_err();
assert_eq!(s.v, 1.0);
s.set_json("/i/a", "1.0".as_bytes()).unwrap();
assert_eq!(s.i.a, 1.0);
}
2 changes: 1 addition & 1 deletion miniconf_derive/src/field.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use darling::{ast, util::Flag, FromDeriveInput, FromField, FromMeta};
use darling::{ast, util::Flag, FromDeriveInput, FromField};
use syn::Path;

#[derive(Debug, FromField)]
Expand Down
22 changes: 20 additions & 2 deletions miniconf_derive/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -242,13 +242,31 @@ pub fn derive_tree_deserialize(input: TokenStream) -> TokenStream {
let ident = name_or_index(i, &field.ident);
let depth = field.depth();
if depth > 0 {
let validate = match &field.validate {
Some(validate) => quote!(
|i| #validate(stringify!(#ident), &mut self.#ident)
.and(Ok(i)).map_err(|msg| Error::Invalid(0, msg))
),
None => quote!(|i| Ok(i)),
};
quote! {
#i => ::miniconf::TreeDeserialize::<'de, #depth>::deserialize_by_key(&mut self.#ident, keys, de)
#i => {
::miniconf::TreeDeserialize::<'de, #depth>::deserialize_by_key(&mut self.#ident, keys, de)
.and_then(#validate)
}
}
} else {
let validate = match &field.validate {
Some(validate) => quote!(
#validate(&self, stringify!(#ident), &mut value, &self.#ident)
.map_err(|msg| Error::Invalid(0,msg))?;),
None => quote!(),
};
quote! {
#i => {
self.#ident = ::miniconf::Deserialize::deserialize(de)?;
let mut value = ::miniconf::Deserialize::deserialize(de)?;
#validate
self.#ident = value;
Ok(0)
}
}
Expand Down

0 comments on commit 2d7229a

Please sign in to comment.