better validation

This commit is contained in:
JMARyA 2024-07-19 10:38:39 +02:00
parent d473a32820
commit 3f251a224f
Signed by: jmarya
GPG key ID: 901B2ADDF27C2263
3 changed files with 41 additions and 20 deletions

View file

@ -24,7 +24,7 @@ pub enum UpdateError {
/// Database related error /// Database related error
Database(mongodb::error::Error), Database(mongodb::error::Error),
/// Validation failed /// Validation failed
Validation, Validation(String),
} }
pub trait Model: pub trait Model:
@ -203,18 +203,18 @@ pub trait Model:
self.update_values(obj, &mut update).await; self.update_values(obj, &mut update).await;
// validate and update // validate and update
if self.validate().await { if let Err(msg) = self.validate().await {
collection return Err(UpdateError::Validation(msg));
.update_one(
id_of!(self.id()),
mongodb::bson::doc! {"$set": update },
None,
)
.await
.map_err(UpdateError::Database)?;
return Ok(());
} }
return Err(UpdateError::Validation); collection
.update_one(
id_of!(self.id()),
mongodb::bson::doc! {"$set": update },
None,
)
.await
.map_err(UpdateError::Database)?;
return Ok(());
} }
Err(UpdateError::NoObject) Err(UpdateError::NoObject)

View file

@ -13,7 +13,7 @@ impl Reference {
/// Create a new reference from String /// Create a new reference from String
pub async fn new_raw(reference: &str) -> Option<Self> { pub async fn new_raw(reference: &str) -> Option<Self> {
let r = Self(reference.to_string()); let r = Self(reference.to_string());
if r.validate().await { if r.validate().await.is_ok() {
Some(r) Some(r)
} else { } else {
None None
@ -63,12 +63,20 @@ impl Reference {
} }
impl Validate for Reference { impl Validate for Reference {
async fn validate(&self) -> bool { async fn validate(&self) -> Result<(), String> {
// cheap // cheap
//self.0.split_once("::").is_some() //self.0.split_once("::").is_some()
// right // right
self.exists().await.unwrap_or(false) if let Some(res) = self.exists().await {
if res {
Ok(())
} else {
Err(format!("Reference '{}' does not exist", self.0))
}
} else {
Err("Database error".to_string())
}
} }
} }

View file

@ -1,19 +1,29 @@
/// This trait allows a `Model` to be validated. /// This trait allows a `Model` to be validated.
pub trait Validate { pub trait Validate {
/// Validate the `Model` /// Validate the `Model`
fn validate(&self) -> impl std::future::Future<Output = bool> + Send; fn validate(&self) -> impl std::future::Future<Output = Result<(), String>> + Send;
} }
/// Validate a value and return `false` if validation fails. /// Validate a value and return an Error if validation fails.
#[macro_export] #[macro_export]
macro_rules! validate { macro_rules! validate {
($val:expr) => { ($val:expr) => {
if !$val.validate().await { if let Err(err) = $val.validate().await {
return false; return Err(format!("{}: {}", stringify!($val), err));
} }
}; };
} }
#[macro_export]
macro_rules! count_items {
// Base case: single item
($single:ident) => { 1 };
// Recursive case: count each item
($head:ident, $($tail:ident),*) => {
1 + mongod::count_items!($($tail),*)
};
}
/// This macro checks for the type of a reference and is useful for validation. /// This macro checks for the type of a reference and is useful for validation.
/// It will check all supplied types and return `false` if none are matching. /// It will check all supplied types and return `false` if none are matching.
/// ///
@ -34,7 +44,10 @@ macro_rules! assert_reference_of {
} }
)* )*
if !match_found { if !match_found {
return false; let possible_types: [&str; mongod::count_items!($($struct_name),*)] = [
$(stringify!($struct_name)),*
];
return Err(format!("{} is not of any type: {:?}", stringify!($var), possible_types));
} }
}; };
} }