Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add support for additional derive macros #57

Merged
merged 1 commit into from
Dec 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading