add derive + docs
This commit is contained in:
parent
5da65fb603
commit
ca2b0036f0
11 changed files with 400 additions and 43 deletions
17
Cargo.lock
generated
17
Cargo.lock
generated
|
@ -164,6 +164,12 @@ version = "1.6.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a12916984aab3fa6e39d655a33e09c0071eb36d6ab3aea5c2d78551f1df6d952"
|
checksum = "a12916984aab3fa6e39d655a33e09c0071eb36d6ab3aea5c2d78551f1df6d952"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "case"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fd6c0e7b807d60291f42f33f58480c0bfafe28ed08286446f45e463728cf9c1c"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cc"
|
name = "cc"
|
||||||
version = "1.1.5"
|
version = "1.1.5"
|
||||||
|
@ -695,6 +701,7 @@ version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"chrono",
|
"chrono",
|
||||||
"futures",
|
"futures",
|
||||||
|
"mongod_derive",
|
||||||
"mongodb",
|
"mongodb",
|
||||||
"regex",
|
"regex",
|
||||||
"serde",
|
"serde",
|
||||||
|
@ -703,6 +710,16 @@ dependencies = [
|
||||||
"uuid",
|
"uuid",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "mongod_derive"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"case",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 1.0.109",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "mongodb"
|
name = "mongodb"
|
||||||
version = "2.8.2"
|
version = "2.8.2"
|
||||||
|
|
|
@ -12,3 +12,4 @@ serde = { version = "1.0.195", features = ["derive"] }
|
||||||
serde_json = "1.0.111"
|
serde_json = "1.0.111"
|
||||||
tokio = { version = "1.35.1", features = ["full"] }
|
tokio = { version = "1.35.1", features = ["full"] }
|
||||||
uuid = { version = "1.8.0", features = ["v4"] }
|
uuid = { version = "1.8.0", features = ["v4"] }
|
||||||
|
mongod_derive = { path = "./mongod_derive" }
|
95
README.md
95
README.md
|
@ -1,2 +1,97 @@
|
||||||
# mongod
|
# mongod
|
||||||
mongod is a rust crate for a model based database on top of MongoDB.
|
mongod is a rust crate for a model based database on top of MongoDB.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
`mongod` allows you to use structs as `models` with data. it is build upon a MongoDB database. You need to pass a Connection URI for the database to use via the `$DB_URI` environment variable.
|
||||||
|
|
||||||
|
### Models
|
||||||
|
You can derive the `Model` and `Referencable` traits for your struct. This will provide you with functions like `insert()`, `delete()`, `update()`, `get()`, etc for your struct. Additionally you have to manually implement the `Validate` trait which ensures a consistent valid state of your struct in order to never insert invalid data into the database.
|
||||||
|
|
||||||
|
```rust
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use mongod::Historic;
|
||||||
|
use mongod::Validate;
|
||||||
|
use mongod::Reference;
|
||||||
|
use mongod::derive::{Module, Referencable};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize, Module, Referencable)]
|
||||||
|
struct MyStruct {
|
||||||
|
_id: String,
|
||||||
|
name: String,
|
||||||
|
other: Option<Reference>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Validate for MyStruct {
|
||||||
|
async fn validate(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This allows you to use your struct like this:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
let a = MyStruct{
|
||||||
|
_id: "someid".to_string(),
|
||||||
|
name: "hello".to_string(),
|
||||||
|
other: None
|
||||||
|
};
|
||||||
|
|
||||||
|
a.insert().await;
|
||||||
|
a.update(serde_json::json!({"name": "bye"})).await.unwrap();
|
||||||
|
|
||||||
|
let new_a = MyStruct::get("someid");
|
||||||
|
```
|
||||||
|
|
||||||
|
### Historic data
|
||||||
|
If you want certain fields to remember a history of changes you can use the `Historic<T>` type.
|
||||||
|
This type will retain previous changes along with a timestamp when updates happen.
|
||||||
|
|
||||||
|
### References
|
||||||
|
A Models field can be a reference to another model using the `Reference` type.
|
||||||
|
|
||||||
|
```rust
|
||||||
|
let m = MyStruct::get("someid").await.unwrap();
|
||||||
|
|
||||||
|
// get a reference from model
|
||||||
|
// `reference()` is a convenience function and is exactly the same as the two references below
|
||||||
|
let myref = m.reference();
|
||||||
|
let sec_ref = Reference::new(MyStruct::collection_name(), "someid").await.unwrap();
|
||||||
|
let third_ref = Reference::new("my_struct", "someid").await.unwrap();
|
||||||
|
|
||||||
|
|
||||||
|
struct OtherStruct {
|
||||||
|
link: Reference
|
||||||
|
}
|
||||||
|
|
||||||
|
// store a reference
|
||||||
|
let other = OtherStruct{
|
||||||
|
link: myref
|
||||||
|
}
|
||||||
|
|
||||||
|
let m: MyStruct = other.link.get().await; // get back a model from reference
|
||||||
|
```
|
||||||
|
|
||||||
|
With the `assert_reference_of!()` macro you can limit the models your reference can represent at validation. You can specify as many models as you want, but the reference must match one of them.
|
||||||
|
|
||||||
|
```rust
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize, Module, Referencable)]
|
||||||
|
struct OtherStruct {
|
||||||
|
/* *** */
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize, Module, Referencable)]
|
||||||
|
struct YetAnotherStruct {
|
||||||
|
/* *** */
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Validate for MyStruct {
|
||||||
|
async fn validate(&self) -> bool {
|
||||||
|
if let Some(other) = self.other {
|
||||||
|
assert_reference_of!(other, OtherStruct, YetAnotherStruct);
|
||||||
|
}
|
||||||
|
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
54
mongod_derive/Cargo.lock
generated
Normal file
54
mongod_derive/Cargo.lock
generated
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 3
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "case"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fd6c0e7b807d60291f42f33f58480c0bfafe28ed08286446f45e463728cf9c1c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "mongod_derive"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"case",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro2"
|
||||||
|
version = "1.0.86"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quote"
|
||||||
|
version = "1.0.36"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "syn"
|
||||||
|
version = "1.0.109"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-ident"
|
||||||
|
version = "1.0.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
13
mongod_derive/Cargo.toml
Normal file
13
mongod_derive/Cargo.toml
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
[package]
|
||||||
|
name = "mongod_derive"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
syn = { version = "1.0", features = ["full"] }
|
||||||
|
quote = "1.0"
|
||||||
|
proc-macro2 = "1.0"
|
||||||
|
case = "1.0.0"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
proc-macro = true
|
135
mongod_derive/src/lib.rs
Normal file
135
mongod_derive/src/lib.rs
Normal file
|
@ -0,0 +1,135 @@
|
||||||
|
extern crate proc_macro;
|
||||||
|
use case::CaseExt;
|
||||||
|
|
||||||
|
use proc_macro::TokenStream;
|
||||||
|
use quote::quote;
|
||||||
|
use syn::{parse_macro_input, Data, DeriveInput, Fields, Type, TypePath};
|
||||||
|
|
||||||
|
/// Get inner type. Example: Returns `T` for `Option<T>`.
|
||||||
|
fn extract_inner_type<'a>(ty: &'a Type, parent: &'a str) -> Option<&'a Type> {
|
||||||
|
if let Type::Path(type_path) = ty {
|
||||||
|
if type_path.path.segments.len() == 1 {
|
||||||
|
let segment = &type_path.path.segments[0];
|
||||||
|
if segment.ident == parent {
|
||||||
|
if let syn::PathArguments::AngleBracketed(ref args) = segment.arguments {
|
||||||
|
if args.args.len() == 1 {
|
||||||
|
if let syn::GenericArgument::Type(ref inner_type) = args.args[0] {
|
||||||
|
return Some(inner_type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn type_path(ty: &syn::Type) -> TypePath {
|
||||||
|
if let syn::Type::Path(type_path) = ty {
|
||||||
|
return type_path.clone();
|
||||||
|
}
|
||||||
|
unreachable!();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_type(ty: &syn::Type, t: &str) -> bool {
|
||||||
|
let type_path = type_path(ty);
|
||||||
|
let id = type_path.path.segments.first().unwrap().ident.to_string();
|
||||||
|
id == t
|
||||||
|
}
|
||||||
|
|
||||||
|
#[proc_macro_derive(Model)]
|
||||||
|
pub fn model_derive(input: TokenStream) -> TokenStream {
|
||||||
|
let input = parse_macro_input!(input as DeriveInput);
|
||||||
|
|
||||||
|
let name = input.ident;
|
||||||
|
|
||||||
|
// Generate code for each field
|
||||||
|
let field_code = if let Data::Struct(data_struct) = input.data {
|
||||||
|
match data_struct.fields {
|
||||||
|
Fields::Named(fields_named) => {
|
||||||
|
let field_process_code: Vec<_> = fields_named.named.iter().map(|field| {
|
||||||
|
let field_name = &field.ident.as_ref().unwrap();
|
||||||
|
let field_type = &field.ty;
|
||||||
|
let field_name_str = field_name.to_string();
|
||||||
|
|
||||||
|
if field_name_str == "_id" {
|
||||||
|
return quote! {};
|
||||||
|
}
|
||||||
|
|
||||||
|
if is_type(field_type, "Historic") {
|
||||||
|
let inner_field_type = extract_inner_type(field_type, "Historic").unwrap();
|
||||||
|
if is_type(inner_field_type, "Vec") {
|
||||||
|
return quote! {
|
||||||
|
mongod::update_historic_vec!(self, obj, #field_name_str, #field_name, update);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return quote! {
|
||||||
|
mongod::update_historic_str!(self, obj, #field_name_str, #field_name, update);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if is_type(field_type, "Option") {
|
||||||
|
let inner_field_type = extract_inner_type(field_type, "Option").unwrap();
|
||||||
|
|
||||||
|
if is_type(inner_field_type, "Historic") {
|
||||||
|
let sub_inner_field_type = extract_inner_type(inner_field_type, "Historic").unwrap();
|
||||||
|
if is_type(sub_inner_field_type, "Reference") {
|
||||||
|
return quote! {
|
||||||
|
mongod::update_historic_ref_option!(self, obj, #field_name_str, #field_name, update);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return quote! {
|
||||||
|
mongod::update_value_option!(self, obj, #field_name_str, #field_name, update, #inner_field_type);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
mongod::update_value!(self, obj, #field_name_str, #field_name, update, #field_type);
|
||||||
|
}
|
||||||
|
}).collect();
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
impl mongod::model::Model for #name {
|
||||||
|
async fn update_values(
|
||||||
|
&mut self,
|
||||||
|
obj: &serde_json::Map<String, serde_json::Value>,
|
||||||
|
update: &mut mongodb::bson::Document,
|
||||||
|
) {
|
||||||
|
#( #field_process_code )*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => unimplemented!(),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
unimplemented!()
|
||||||
|
};
|
||||||
|
|
||||||
|
TokenStream::from(field_code)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[proc_macro_derive(Referencable)]
|
||||||
|
pub fn referencable_derive(input: TokenStream) -> TokenStream {
|
||||||
|
let input = parse_macro_input!(input as DeriveInput);
|
||||||
|
|
||||||
|
let name = input.ident;
|
||||||
|
let name_str = name.to_string().to_snake();
|
||||||
|
|
||||||
|
let code = quote! {
|
||||||
|
impl mongod::model::reference::Referencable for MyStruct {
|
||||||
|
fn collection_name() -> &'static str {
|
||||||
|
#name_str
|
||||||
|
}
|
||||||
|
|
||||||
|
fn id(&self) -> &str {
|
||||||
|
&self._id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
TokenStream::from(code)
|
||||||
|
}
|
|
@ -1,4 +1,9 @@
|
||||||
pub mod model;
|
pub mod model;
|
||||||
|
pub use model::historic::Historic;
|
||||||
|
pub use model::reference::*;
|
||||||
|
pub use model::valid::Validate;
|
||||||
|
pub use model::Model;
|
||||||
|
pub use mongod_derive as derive;
|
||||||
|
|
||||||
/// Get a `MongoDB` Client from the environment
|
/// Get a `MongoDB` Client from the environment
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
|
|
109
src/model/mod.rs
109
src/model/mod.rs
|
@ -23,52 +23,83 @@ pub enum UpdateError {
|
||||||
pub trait Model:
|
pub trait Model:
|
||||||
Sized + Referencable + Validate + serde::Serialize + for<'a> serde::Deserialize<'a>
|
Sized + Referencable + Validate + serde::Serialize + for<'a> serde::Deserialize<'a>
|
||||||
{
|
{
|
||||||
/// Insert the model into the database
|
/// Insert the `Model` into the database
|
||||||
async fn insert(&self) {
|
fn insert(&self) -> impl std::future::Future<Output = ()> + Send
|
||||||
let db = get_mongo!();
|
where
|
||||||
let collection = col!(db, Self::collection_name());
|
Self: Sync,
|
||||||
collection
|
{
|
||||||
.insert_one(mongodb::bson::to_document(self).unwrap(), None)
|
async {
|
||||||
.await
|
let db = get_mongo!();
|
||||||
.unwrap();
|
let collection = col!(db, Self::collection_name());
|
||||||
|
collection
|
||||||
|
.insert_one(mongodb::bson::to_document(self).unwrap(), None)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a model by id from the database
|
/// Remove a `Model` from the database.
|
||||||
async fn get(id: &str) -> Option<Self> {
|
fn remove(id: &str) -> impl std::future::Future<Output = ()> + Send {
|
||||||
let db = get_mongo!();
|
async move {
|
||||||
let collection = col!(db, Self::collection_name());
|
let db = get_mongo!();
|
||||||
let doc = collection.find_one(id_of!(id), None).await.ok()??;
|
let collection = col!(db, Self::collection_name());
|
||||||
mongodb::bson::from_document(doc).ok()
|
collection.delete_one(id_of!(id), None).await.unwrap();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Update values of model into database
|
/// Remove a `Model` from the database.
|
||||||
async fn update(&mut self, data: &serde_json::Value) -> Result<(), UpdateError> {
|
///
|
||||||
// get db collection
|
/// This is a convenience function to let you call `remove()` on a `Model` you have at hand.
|
||||||
let db = get_mongo!();
|
fn delete(&self) -> impl std::future::Future<Output = ()> + Send
|
||||||
let collection = col!(db, Self::collection_name());
|
where
|
||||||
let mut update = mongodb::bson::Document::new();
|
Self: Sync,
|
||||||
|
{
|
||||||
|
async { Self::remove(self.id()).await }
|
||||||
|
}
|
||||||
|
|
||||||
if let Some(obj) = data.as_object() {
|
/// Get a `Model` by id from the database
|
||||||
// run model specific update
|
#[must_use]
|
||||||
self.update_values(obj, &mut update).await;
|
fn get(id: &str) -> impl std::future::Future<Output = Option<Self>> {
|
||||||
|
async move {
|
||||||
|
let db = get_mongo!();
|
||||||
|
let collection = col!(db, Self::collection_name());
|
||||||
|
let doc = collection.find_one(id_of!(id), None).await.ok()??;
|
||||||
|
mongodb::bson::from_document(doc).ok()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// validate and update
|
/// Update values of `Model` into database
|
||||||
if self.validate().await {
|
fn update(
|
||||||
collection
|
&mut self,
|
||||||
.update_one(
|
data: &serde_json::Value,
|
||||||
id_of!(self.id()),
|
) -> impl std::future::Future<Output = Result<(), UpdateError>> {
|
||||||
mongodb::bson::doc! {"$set": update },
|
async {
|
||||||
None,
|
// get db collection
|
||||||
)
|
let db = get_mongo!();
|
||||||
.await
|
let collection = col!(db, Self::collection_name());
|
||||||
.map_err(|_| UpdateError::Database)?;
|
let mut update = mongodb::bson::Document::new();
|
||||||
return Ok(());
|
|
||||||
} else {
|
if let Some(obj) = data.as_object() {
|
||||||
|
// run model specific update
|
||||||
|
self.update_values(obj, &mut update).await;
|
||||||
|
|
||||||
|
// validate and update
|
||||||
|
if self.validate().await {
|
||||||
|
collection
|
||||||
|
.update_one(
|
||||||
|
id_of!(self.id()),
|
||||||
|
mongodb::bson::doc! {"$set": update },
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.map_err(|_| UpdateError::Database)?;
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
return Err(UpdateError::Validation);
|
return Err(UpdateError::Validation);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
Err(UpdateError::NoObject)
|
Err(UpdateError::NoObject)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Update the `Model` based on the provided JSON Object.
|
/// Update the `Model` based on the provided JSON Object.
|
||||||
|
@ -77,9 +108,9 @@ pub trait Model:
|
||||||
/// For every updated value in the JSON Object the `Model`s fields should be updated and the value should be put in the `update` `Document`
|
/// For every updated value in the JSON Object the `Model`s fields should be updated and the value should be put in the `update` `Document`
|
||||||
///
|
///
|
||||||
/// To avoid making mistakes in the Update logic use the macros from `update.rs`
|
/// To avoid making mistakes in the Update logic use the macros from `update.rs`
|
||||||
async fn update_values(
|
fn update_values(
|
||||||
&mut self,
|
&mut self,
|
||||||
obj: &Map<String, Value>,
|
obj: &Map<String, Value>,
|
||||||
update: &mut mongodb::bson::Document,
|
update: &mut mongodb::bson::Document,
|
||||||
);
|
) -> impl std::future::Future<Output = ()> + Send;
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,8 +9,8 @@ use super::valid::Validate;
|
||||||
pub struct Reference(String);
|
pub struct Reference(String);
|
||||||
|
|
||||||
impl Reference {
|
impl Reference {
|
||||||
/// Create a new reference
|
/// Create a new reference from String
|
||||||
pub async fn new(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 {
|
||||||
Some(r)
|
Some(r)
|
||||||
|
@ -19,7 +19,13 @@ impl Reference {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a new reference
|
||||||
|
pub async fn new(model: &str, id: &str) -> Option<Self> {
|
||||||
|
Self::new_raw(&format!("{model}::{id}")).await
|
||||||
|
}
|
||||||
|
|
||||||
/// Checks if a reference is part of a model collection.
|
/// Checks if a reference is part of a model collection.
|
||||||
|
#[must_use]
|
||||||
pub fn is_of_collection(&self, col: &str) -> bool {
|
pub fn is_of_collection(&self, col: &str) -> bool {
|
||||||
self.0.starts_with(col)
|
self.0.starts_with(col)
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,7 +43,7 @@ macro_rules! update_historic_ref_option {
|
||||||
($entity:ident, $json:ident, $key:literal, $field:ident, $update:ident) => {
|
($entity:ident, $json:ident, $key:literal, $field:ident, $update:ident) => {
|
||||||
if let Some(val) = $json.get($key) {
|
if let Some(val) = $json.get($key) {
|
||||||
if let Some(val_str) = val.as_str() {
|
if let Some(val_str) = val.as_str() {
|
||||||
let value = Historic::new(Reference::new(val_str).await.unwrap());
|
let value = Historic::new(Reference::new_raw(val_str).await.unwrap());
|
||||||
let field = $entity.$field.clone().unwrap_or_else(|| value);
|
let field = $entity.$field.clone().unwrap_or_else(|| value);
|
||||||
$entity.$field = Some(field);
|
$entity.$field = Some(field);
|
||||||
$update.insert($key, mongodb::bson::to_bson(&$entity.$field).unwrap());
|
$update.insert($key, mongodb::bson::to_bson(&$entity.$field).unwrap());
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
/// 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`
|
||||||
async fn validate(&self) -> bool;
|
fn validate(&self) -> impl std::future::Future<Output = bool> + Send;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Validate a value and return `false` if validation fails.
|
/// Validate a value and return `false` if validation fails.
|
||||||
|
|
Loading…
Reference in a new issue