add partials
This commit is contained in:
parent
0cb1e28c20
commit
c7f0caf34a
3 changed files with 139 additions and 2 deletions
13
README.md
13
README.md
|
@ -94,4 +94,15 @@ impl Validate for MyStruct {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Partials
|
||||||
|
Sometimes you need only some fields of your `Model`. When deriving the `Model` trait you automatically get a `Partial` of your model. Partials are only meant for reading values so you cant call functions like `update()`, but you can still get a `Reference` to the original `Model`. This saves bandwidth and memory. You can access it like this:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
// say you are only interested in the reference field `other` of `MyStruct`
|
||||||
|
let ms = MyStruct::get_partial("someid", &serde_json::json!({"other": 1})).await.unwrap();
|
||||||
|
|
||||||
|
let myref = ms.other.unwrap(); // will be there
|
||||||
|
let name = ms.name.unwrap() // will panic!
|
||||||
|
```
|
||||||
|
|
|
@ -42,6 +42,8 @@ pub fn model_derive(input: TokenStream) -> TokenStream {
|
||||||
let input = parse_macro_input!(input as DeriveInput);
|
let input = parse_macro_input!(input as DeriveInput);
|
||||||
|
|
||||||
let name = input.ident;
|
let name = input.ident;
|
||||||
|
let name_str = name.to_string().to_snake();
|
||||||
|
let partial_name = syn::Ident::new(&format!("Partial{}", name), name.span());
|
||||||
|
|
||||||
// Generate code for each field
|
// Generate code for each field
|
||||||
let field_code = if let Data::Struct(data_struct) = input.data {
|
let field_code = if let Data::Struct(data_struct) = input.data {
|
||||||
|
@ -91,8 +93,36 @@ pub fn model_derive(input: TokenStream) -> TokenStream {
|
||||||
}
|
}
|
||||||
}).collect();
|
}).collect();
|
||||||
|
|
||||||
|
let partial_struct: 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! {
|
||||||
|
pub _id: String,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if is_type(field_type, "Option") {
|
||||||
|
return quote! {
|
||||||
|
pub #field_name: #field_type,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
pub #field_name: Option<#field_type>,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
quote! {
|
quote! {
|
||||||
impl mongod::model::Model for #name {
|
impl mongod::model::Model for #name {
|
||||||
|
type Partial = #partial_name;
|
||||||
|
|
||||||
async fn update_values(
|
async fn update_values(
|
||||||
&mut self,
|
&mut self,
|
||||||
obj: &serde_json::Map<String, serde_json::Value>,
|
obj: &serde_json::Map<String, serde_json::Value>,
|
||||||
|
@ -101,6 +131,21 @@ pub fn model_derive(input: TokenStream) -> TokenStream {
|
||||||
#( #field_process_code )*
|
#( #field_process_code )*
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
||||||
|
pub struct #partial_name {
|
||||||
|
#( #partial_struct )*
|
||||||
|
}
|
||||||
|
|
||||||
|
impl mongod::model::reference::Referencable for #partial_name {
|
||||||
|
fn collection_name() -> &'static str {
|
||||||
|
#name_str
|
||||||
|
}
|
||||||
|
|
||||||
|
fn id(&self) -> &str {
|
||||||
|
&self._id
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => unimplemented!(),
|
_ => unimplemented!(),
|
||||||
|
|
|
@ -1,5 +1,9 @@
|
||||||
use mongodb::results::{DeleteResult, InsertOneResult};
|
use mongodb::{
|
||||||
|
options::{FindOneOptions, FindOptions},
|
||||||
|
results::{DeleteResult, InsertOneResult},
|
||||||
|
};
|
||||||
use reference::Referencable;
|
use reference::Referencable;
|
||||||
|
use serde::de::DeserializeOwned;
|
||||||
use serde_json::{Map, Value};
|
use serde_json::{Map, Value};
|
||||||
use valid::Validate;
|
use valid::Validate;
|
||||||
|
|
||||||
|
@ -26,6 +30,8 @@ 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>
|
||||||
{
|
{
|
||||||
|
type Partial: DeserializeOwned;
|
||||||
|
|
||||||
/// Insert the `Model` into the database
|
/// Insert the `Model` into the database
|
||||||
fn insert(
|
fn insert(
|
||||||
&self,
|
&self,
|
||||||
|
@ -77,8 +83,56 @@ pub trait Model:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get a partial `Model` by id from the database with only some fields retrieved.
|
||||||
|
#[must_use]
|
||||||
|
fn get_partial(
|
||||||
|
id: &str,
|
||||||
|
part: &serde_json::Value,
|
||||||
|
) -> impl std::future::Future<Output = Option<Self::Partial>> {
|
||||||
|
async move {
|
||||||
|
let db = get_mongo!();
|
||||||
|
let collection = col!(db, Self::collection_name());
|
||||||
|
let doc = collection
|
||||||
|
.find_one(
|
||||||
|
id_of!(id),
|
||||||
|
Some(
|
||||||
|
FindOneOptions::builder()
|
||||||
|
.projection(Some(mongodb::bson::to_document(part).unwrap()))
|
||||||
|
.build(),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.ok()??;
|
||||||
|
mongodb::bson::from_document(doc).ok()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Get a `Model` by using a filter from the database
|
/// Get a `Model` by using a filter from the database
|
||||||
#[must_use]
|
#[must_use]
|
||||||
|
fn find_one_partial(
|
||||||
|
filter: mongodb::bson::Document,
|
||||||
|
part: &serde_json::Value,
|
||||||
|
) -> impl std::future::Future<Output = Option<Self::Partial>> {
|
||||||
|
async move {
|
||||||
|
let db = get_mongo!();
|
||||||
|
let collection = col!(db, Self::collection_name());
|
||||||
|
let doc = collection
|
||||||
|
.find_one(
|
||||||
|
filter,
|
||||||
|
Some(
|
||||||
|
FindOneOptions::builder()
|
||||||
|
.projection(Some(mongodb::bson::to_document(part).unwrap()))
|
||||||
|
.build(),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.ok()??;
|
||||||
|
mongodb::bson::from_document(doc).ok()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get a partial `Model` by using a filter from the database
|
||||||
|
#[must_use]
|
||||||
fn find_one(
|
fn find_one(
|
||||||
filter: mongodb::bson::Document,
|
filter: mongodb::bson::Document,
|
||||||
) -> impl std::future::Future<Output = Option<Self>> {
|
) -> impl std::future::Future<Output = Option<Self>> {
|
||||||
|
@ -106,6 +160,33 @@ pub trait Model:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get multiple partial `Model`s by using a filter from the database
|
||||||
|
#[must_use]
|
||||||
|
fn find_partial(
|
||||||
|
filter: mongodb::bson::Document,
|
||||||
|
part: &serde_json::Value,
|
||||||
|
) -> impl std::future::Future<Output = Option<Vec<Self>>> {
|
||||||
|
async move {
|
||||||
|
let db = get_mongo!();
|
||||||
|
let collection = col!(db, Self::collection_name());
|
||||||
|
let mut results = collection
|
||||||
|
.find(
|
||||||
|
filter,
|
||||||
|
Some(
|
||||||
|
FindOptions::builder()
|
||||||
|
.projection(Some(mongodb::bson::to_document(part).unwrap()))
|
||||||
|
.build(),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.ok()?;
|
||||||
|
let docs = collect_results!(results);
|
||||||
|
docs.into_iter()
|
||||||
|
.map(|x| mongodb::bson::from_document(x).unwrap())
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Update values of `Model` into database
|
/// Update values of `Model` into database
|
||||||
fn update(
|
fn update(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
|
Loading…
Reference in a new issue