Skip to content

Commit

Permalink
feat: add support for additional derive macros (#57)
Browse files Browse the repository at this point in the history
  • Loading branch information
Phosphorus-M authored Dec 5, 2024
1 parent bc4d5d7 commit 16b5db8
Show file tree
Hide file tree
Showing 6 changed files with 78 additions and 6 deletions.
11 changes: 11 additions & 0 deletions attr/src/metadata/model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use syn::DeriveInput;
pub struct ModelMeta {
pub table: TableMeta,
pub insert_struct: Option<Ident>,
pub extra_derives: Option<Vec<Ident>>,
pub pkey: ColumnMeta,
}

Expand All @@ -32,19 +33,28 @@ impl ModelMeta {
table.name,
));
let mut insert_struct = None;
let mut extra_derives: Option<Vec<syn::Ident>> = None;
for attr in attrs {
if let Some(v) = attr.insert {
insert_struct = Some(v.value());
}
if let Some(v) = attr.insertable {
insert_struct = Some(v.to_string());
}
if let Some(v) = attr.extra_derives {
if !v.is_empty() {
extra_derives = Some(v);
}
}
}
let pkey = table.columns.iter().find(|&c| c.name == pkey).unwrap().clone();
let insert_struct = insert_struct.map(|v| Ident::from(v));
let extra_derives = extra_derives.take().map(|vec| vec.into_iter().map(|v| v.to_string()).map(Ident::from).collect());

Self {
table,
insert_struct,
extra_derives,
pkey,
}
}
Expand All @@ -55,6 +65,7 @@ impl ModelMeta {
Self {
pkey: inner.columns.iter().find(|c| c.name == "id").unwrap().clone(),
table: inner,
extra_derives: None,
insert_struct: None,
}
}
Expand Down
9 changes: 9 additions & 0 deletions attr/src/metadata/table.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,15 @@ pub struct TableAttr {
///
pub insert: Option<LitStr>,

/// Add extra derives to the insertion structs.
/// Example:
/// #[ormlite(insert = "InsertUser", extra_derives(Serialize, Deserialize))]
/// pub struct User {
/// pub id: i32,
/// }
///
pub extra_derives: Option<Vec<syn::Ident>>,

/// Only used for derive(Insert)
/// Example:
/// #[ormlite(returns = "User")]
Expand Down
21 changes: 21 additions & 0 deletions core/src/join.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
use crate::model::Model;
use async_trait::async_trait;
use serde::Deserialize;
use serde::{Serialize, Serializer};
use sqlmo::query::Join as JoinQueryFragment;
use sqlmo::query::SelectColumn;
use sqlx::{Database, Decode, Encode, Type};
use std::ops::{Deref, DerefMut};
use serde::de::Error;

pub trait JoinMeta {
type IdType: Clone + Send + Eq + PartialEq + std::hash::Hash;
Expand Down Expand Up @@ -224,3 +226,22 @@ impl<T: JoinMeta + Serialize> Serialize for Join<T> {
}
}
}

impl<'de, T> Deserialize<'de> for Join<T>
where
T: JoinMeta + Deserialize<'de>,
{
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let data = Option::<T>::deserialize(deserializer)?;

let (id_type, join_data) = match data {
Some(value) => (T::_id(&value), JoinData::QueryResult(value)),
None => return Err(D::Error::custom("Invalid value"))
};

Ok(Join { id: id_type, data: join_data })
}
}
18 changes: 14 additions & 4 deletions macro/src/codegen/insert_model.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use itertools::Itertools;
use ormlite_attr::Ident;
use ormlite_attr::ModelMeta;
use proc_macro2::TokenStream;
Expand All @@ -16,10 +17,19 @@ pub fn struct_InsertModel(ast: &DeriveInput, attr: &ModelMeta) -> TokenStream {
pub #id: #ty
}
});
quote! {
#[derive(Debug)]
#vis struct #insert_model {
#(#struct_fields,)*
if let Some(extra_derives) = &attr.extra_derives {
quote! {
#[derive(Debug, #(#extra_derives,)*)]
#vis struct #insert_model {
#(#struct_fields,)*
}
}
} else {
quote! {
#[derive(Debug)]
#vis struct #insert_model {
#(#struct_fields,)*
}
}
}
}
1 change: 1 addition & 0 deletions ormlite/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -67,4 +67,5 @@ trybuild = { version = "1.0.99", features = ["diff"] }
env_logger = "0.11.5"
uuid = { version = "1.10.0", features = ["serde", "v4"] }
serde = { version = "1.0.210", features = ["derive"] }
serde_json = { version = "1.0.128" }
chrono = { version = "0.4.38", features = ["serde"] }
24 changes: 22 additions & 2 deletions ormlite/tests/sqlite/06-insert.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
use ormlite::model::{Insert, Join, JoinMeta, Model};
use sqlmo::ToSql;
use serde::{Serialize, Deserialize};
use serde_json::json;

use ormlite::Connection;
#[path = "../setup.rs"]
mod setup;

#[derive(Debug, Model, Clone)]
#[derive(Debug, Model, Clone, Serialize, Deserialize)]
pub struct Organization {
id: i32,
name: String,
}

#[derive(Model)]
#[ormlite(insert = "InsertUser")]
#[ormlite(insert = "InsertUser", extra_derives(Serialize, Deserialize))]
// Note the previous syntax, #[ormlite(insertable = InsertUser)] still works, but the new syntax is preferred.
pub struct User {
id: i32,
Expand Down Expand Up @@ -66,6 +68,24 @@ async fn main() {
assert_eq!(champ.organization.id, 12321);
assert_eq!(champ.organization.name, "my org");

let champ_copy = InsertUser {
name: "Champ".to_string(),
organization: Join::new(org.clone()),
ty: 12,
};
let champ_json = json!(champ_copy).to_string();

assert_eq!(champ_json, r#"{"name":"Champ","organization":{"id":12321,"name":"my org"},"ty":12}"#);

let champ_deserializing = serde_json::from_str::<InsertUser>(r#"{"name":"Champ","organization":{"id":12321,"name":"my org"},"ty":12}"#);

let Ok(champ_deserialized) = champ_deserializing else {
panic!("Deserialize failing");
};

assert_eq!(champ_deserialized.name, champ_copy.name);
assert_eq!(champ_deserialized.organization.name, champ_copy.organization.name);

let millie = InsertUser {
name: "Millie".to_string(),
organization: Join::new(org),
Expand Down

0 comments on commit 16b5db8

Please sign in to comment.