Add proc_macro

This commit is contained in:
Billy Chan 2021-05-17 16:59:34 +08:00
parent dc4bb3075f
commit 07b58551af
No known key found for this signature in database
GPG Key ID: A2D690CAC7DF3CC7
14 changed files with 333 additions and 418 deletions

View File

@ -1,6 +1,7 @@
[workspace]
members = [
".",
"sea-orm-macros",
"examples/sqlx-mysql",
]
@ -25,6 +26,7 @@ path = "src/lib.rs"
async-trait = "^0.1"
futures = { version = "^0.3" }
sea-query = { path = "../sea-query", version = "^0.10", features = [ "sqlx-mysql" ] }
sea-orm-macros = { path = "sea-orm-macros", optional = true }
# sea-schema = { path = "../sea-schema" }
serde = { version = "^1.0", features = [ "derive" ] }
sqlx = { version = "^0.5", optional = true }
@ -32,7 +34,7 @@ strum = { version = "^0.20", features = [ "derive" ] }
[features]
debug-print = []
default = [ "sqlx-mysql", "runtime-async-std-native-tls" ]
default = [ "macros", "sqlx-mysql", "runtime-async-std-native-tls" ]
sqlx-dep = [ "sqlx" ]
sqlx-mysql = [ "sqlx-dep", "sea-query/sqlx-mysql", "sqlx/mysql" ]
sqlx-postgres = [ "sqlx-dep", "sea-query/sqlx-postgres", "sqlx/postgres" ]
@ -42,3 +44,4 @@ runtime-tokio-native-tls = [ "sqlx/runtime-tokio-native-tls" ]
runtime-actix-rustls = [ "sqlx/runtime-actix-rustls" ]
runtime-async-std-rustls = [ "sqlx/runtime-async-std-rustls" ]
runtime-tokio-rustls = [ "sqlx/runtime-tokio-rustls" ]
macros = [ "sea-orm-macros" ]

View File

@ -1,21 +1,22 @@
use sea_orm::entity::prelude::*;
#[derive(Copy, Clone, Default, Debug)]
#[derive(Copy, Clone, Default, Debug, DeriveEntity)]
#[entity = "cake"]
pub struct Entity;
#[derive(Clone, Debug, Default, PartialEq)]
#[derive(Clone, Debug, Default, PartialEq, DeriveModel)]
pub struct Model {
pub id: i32,
pub name: String,
}
#[derive(Copy, Clone, Debug, EnumIter)]
#[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)]
pub enum Column {
Id,
Name,
}
#[derive(Copy, Clone, Debug, EnumIter)]
#[derive(Copy, Clone, Debug, EnumIter, DerivePrimaryKey)]
pub enum PrimaryKey {
Id,
}
@ -25,16 +26,6 @@ pub enum Relation {
Fruit,
}
impl EntityTrait for Entity {
type Model = Model;
type Column = Column;
type PrimaryKey = PrimaryKey;
type Relation = Relation;
}
impl ColumnTrait for Column {
type EntityName = Entity;
@ -68,91 +59,3 @@ impl Model {
Entity::find_related().belongs_to::<Entity>(self)
}
}
// TODO: implement with derive macro
impl EntityName for Entity {}
// TODO: implement with derive macro
impl IdenStatic for Entity {
fn as_str(&self) -> &str {
"cake"
}
}
// TODO: implement with derive macro
impl Iden for Entity {
fn unquoted(&self, s: &mut dyn std::fmt::Write) {
write!(s, "{}", self.as_str()).unwrap();
}
}
// TODO: implement with derive macro
impl ModelTrait for Model {
type Column = Column;
fn get(&self, c: Self::Column) -> Value {
match c {
Column::Id => self.id.clone().into(),
Column::Name => self.name.clone().into(),
}
}
fn set(&mut self, c: Self::Column, v: Value) {
match c {
Column::Id => self.id = v.unwrap(),
Column::Name => self.name = v.unwrap(),
}
}
fn from_query_result(row: &QueryResult, pre: &str) -> Result<Self, TypeErr> {
Ok(Self {
id: row.try_get(pre, Column::Id.as_str())?,
name: row.try_get(pre, Column::Name.as_str())?,
})
}
}
// TODO: implement with derive macro
impl Iden for Column {
fn unquoted(&self, s: &mut dyn std::fmt::Write) {
write!(s, "{}", self.as_str()).unwrap();
}
}
// TODO: implement with derive macro
impl IdenStatic for Column {
fn as_str(&self) -> &str {
match self {
Self::Id => "id",
Self::Name => "name",
}
}
}
// TODO: implement with derive macro
impl Iden for PrimaryKey {
fn unquoted(&self, s: &mut dyn std::fmt::Write) {
write!(s, "{}", self.as_str()).unwrap();
}
}
// TODO: implement with derive macro
impl IdenStatic for PrimaryKey {
fn as_str(&self) -> &str {
match self {
Self::Id => "id",
}
}
}
// TODO: implement with derive macro
impl PrimaryKeyTrait for PrimaryKey {}
// TODO: implement with derive macro
impl PrimaryKeyOfModel<Model> for PrimaryKey {
fn into_column(self) -> <Model as ModelTrait>::Column {
match self {
Self::Id => Column::Id,
}
}
}

View File

@ -1,23 +1,24 @@
use sea_orm::entity::prelude::*;
#[derive(Copy, Clone, Default, Debug)]
#[derive(Copy, Clone, Default, Debug, DeriveEntity)]
#[entity = "fruit"]
pub struct Entity;
#[derive(Clone, Debug, Default, PartialEq)]
#[derive(Clone, Debug, Default, PartialEq, DeriveModel)]
pub struct Model {
pub id: i32,
pub name: String,
pub cake_id: Option<i32>,
}
#[derive(Copy, Clone, Debug, EnumIter)]
#[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)]
pub enum Column {
Id,
Name,
CakeId,
}
#[derive(Copy, Clone, Debug, EnumIter)]
#[derive(Copy, Clone, Debug, EnumIter, DerivePrimaryKey)]
pub enum PrimaryKey {
Id,
}
@ -25,16 +26,6 @@ pub enum PrimaryKey {
#[derive(Copy, Clone, Debug, EnumIter)]
pub enum Relation {}
impl EntityTrait for Entity {
type Model = Model;
type Column = Column;
type PrimaryKey = PrimaryKey;
type Relation = Relation;
}
impl ColumnTrait for Column {
type EntityName = Entity;
@ -52,95 +43,3 @@ impl RelationTrait for Relation {
panic!()
}
}
// TODO: implement with derive macro
impl EntityName for Entity {}
// TODO: implement with derive macro
impl IdenStatic for Entity {
fn as_str(&self) -> &str {
"fruit"
}
}
// TODO: implement with derive macro
impl Iden for Entity {
fn unquoted(&self, s: &mut dyn std::fmt::Write) {
write!(s, "{}", self.as_str()).unwrap();
}
}
// TODO: implement with derive macro
impl ModelTrait for Model {
type Column = Column;
fn get(&self, c: Self::Column) -> Value {
match c {
Column::Id => self.id.clone().into(),
Column::Name => self.name.clone().into(),
Column::CakeId => self.cake_id.clone().into(),
}
}
fn set(&mut self, c: Self::Column, v: Value) {
match c {
Column::Id => self.id = v.unwrap(),
Column::Name => self.name = v.unwrap(),
Column::CakeId => self.cake_id = v.unwrap(),
}
}
fn from_query_result(row: &QueryResult, pre: &str) -> Result<Self, TypeErr> {
Ok(Self {
id: row.try_get(pre, Column::Id.as_str())?,
name: row.try_get(pre, Column::Name.as_str())?,
cake_id: row.try_get(pre, Column::CakeId.as_str())?,
})
}
}
// TODO: implement with derive macro
impl Iden for Column {
fn unquoted(&self, s: &mut dyn std::fmt::Write) {
write!(s, "{}", self.as_str()).unwrap();
}
}
// TODO: implement with derive macro
impl IdenStatic for Column {
fn as_str(&self) -> &str {
match self {
Self::Id => "id",
Self::Name => "name",
Self::CakeId => "cake_id",
}
}
}
// TODO: implement with derive macro
impl Iden for PrimaryKey {
fn unquoted(&self, s: &mut dyn std::fmt::Write) {
write!(s, "{}", self.as_str()).unwrap();
}
}
// TODO: implement with derive macro
impl IdenStatic for PrimaryKey {
fn as_str(&self) -> &str {
match self {
Self::Id => "id",
}
}
}
// TODO: implement with derive macro
impl PrimaryKeyTrait for PrimaryKey {}
// TODO: implement with derive macro
impl PrimaryKeyOfModel<Model> for PrimaryKey {
fn into_column(self) -> <Model as ModelTrait>::Column {
match self {
Self::Id => Column::Id,
}
}
}

23
sea-orm-macros/Cargo.toml Normal file
View File

@ -0,0 +1,23 @@
[package]
name = "sea-orm-macros"
version = "0.1.0"
authors = [ "Billy Chan <ccw.billy.123@gmail.com>" ]
edition = "2018"
description = ""
license = "MIT OR Apache-2.0"
documentation = "https://docs.rs/sea-orm"
repository = "https://github.com/SeaQL/sea-orm"
categories = [ "database" ]
keywords = [ "orm", "database", "sql", "mysql", "postgres", "sqlite" ]
publish = false
[lib]
name = "sea_orm_macros"
path = "src/lib.rs"
proc-macro = true
[dependencies]
syn = { version = "1", default-features = false, features = [ "derive", "parsing", "proc-macro", "printing" ] }
quote = "1"
heck = "0.3"
proc-macro2 = "1"

View File

@ -0,0 +1,48 @@
use heck::SnakeCase;
use proc_macro2::{Ident, TokenStream};
use syn::{Data, DataEnum, Fields, Variant};
use quote::{quote, quote_spanned};
pub fn expend_derive_column(ident: Ident, data: Data) -> syn::Result<TokenStream> {
let variants = match data {
syn::Data::Enum(DataEnum { variants, .. }) => variants,
_ => return Ok(quote_spanned! {
ident.span() => compile_error!("you can only derive DeriveColumn on enums");
}),
};
let variant: Vec<TokenStream> = variants
.iter()
.map(|Variant { ident, fields, .. }| {
match fields {
Fields::Named(_) => quote! { #ident{..} },
Fields::Unnamed(_) => quote! { #ident(..) },
Fields::Unit => quote! { #ident },
}
})
.collect();
let name: Vec<TokenStream> = variants
.iter()
.map(|v| {
let ident = v.ident.to_string().to_snake_case();
quote! { #ident }
})
.collect();
Ok(quote!(
impl Iden for #ident {
fn unquoted(&self, s: &mut dyn std::fmt::Write) {
write!(s, "{}", self.as_str()).unwrap();
}
}
impl IdenStatic for #ident {
fn as_str(&self) -> &str {
match self {
#(Self::#variant => #name),*
}
}
}
))
}

View File

@ -0,0 +1,53 @@
use heck::SnakeCase;
use proc_macro2::{Ident, TokenStream};
use syn::{Attribute, Meta};
use quote::quote;
fn get_entity_attr(attrs: &[Attribute]) -> Option<syn::Lit> {
for attr in attrs {
let name_value = match attr.parse_meta() {
Ok(Meta::NameValue(nv)) => nv,
_ => continue,
};
if name_value.path.is_ident("entity") {
return Some(name_value.lit);
}
}
None
}
pub fn expend_derive_entity(ident: Ident, attrs: Vec<Attribute>) -> syn::Result<TokenStream> {
let entity_name = match get_entity_attr(&attrs) {
Some(lit) => quote! { #lit },
None => {
let normalized = ident.to_string().to_snake_case();
quote! { #normalized }
}
};
Ok(quote!(
impl EntityName for #ident {}
impl IdenStatic for #ident {
fn as_str(&self) -> &str {
#entity_name
}
}
impl Iden for #ident {
fn unquoted(&self, s: &mut dyn std::fmt::Write) {
write!(s, "{}", self.as_str()).unwrap();
}
}
impl EntityTrait for #ident {
type Model = Model;
type Column = Column;
type PrimaryKey = PrimaryKey;
type Relation = Relation;
}
))
}

View File

@ -0,0 +1,9 @@
mod entity;
mod primary_key;
mod column;
mod model;
pub use entity::*;
pub use primary_key::*;
pub use column::*;
pub use model::*;

View File

@ -0,0 +1,57 @@
use heck::CamelCase;
use proc_macro2::{Ident, TokenStream};
use syn::{Data, DataStruct, Field, Fields};
use quote::{format_ident, quote, quote_spanned};
pub fn expend_derive_model(ident: Ident, data: Data) -> syn::Result<TokenStream> {
let fields = match data {
Data::Struct(DataStruct {
fields: Fields::Named(named),
..
}) => {
named.named
},
_ => return Ok(quote_spanned! {
ident.span() => compile_error!("you can only derive DeriveModel on structs");
}),
};
let field: Vec<Ident> = fields
.clone()
.into_iter()
.map(|Field { ident, .. }| {
format_ident!("{}", ident.unwrap().to_string())
})
.collect();
let name: Vec<Ident> = fields
.into_iter()
.map(|Field { ident, .. }| {
format_ident!("{}", ident.unwrap().to_string().to_camel_case())
})
.collect();
Ok(quote!(
impl ModelTrait for #ident {
type Column = Column;
fn get(&self, c: Self::Column) -> Value {
match c {
#(Self::Column::#name => self.#field.clone().into()),*
}
}
fn set(&mut self, c: Self::Column, v: Value) {
match c {
#(Self::Column::#name => self.#field = v.unwrap()),*
}
}
fn from_query_result(row: &QueryResult, pre: &str) -> Result<Self, TypeErr> {
Ok(Self {
#(#field: row.try_get(pre, Self::Column::#name.as_str().into())?),*
})
}
}
))
}

View File

@ -0,0 +1,58 @@
use heck::SnakeCase;
use proc_macro2::{Ident, TokenStream};
use syn::{Data, DataEnum, Fields, Variant};
use quote::{quote, quote_spanned};
pub fn expend_derive_primary_key(ident: Ident, data: Data) -> syn::Result<TokenStream> {
let variants = match data {
syn::Data::Enum(DataEnum { variants, .. }) => variants,
_ => return Ok(quote_spanned! {
ident.span() => compile_error!("you can only derive DerivePrimaryKey on enums");
}),
};
let variant: Vec<TokenStream> = variants
.iter()
.map(|Variant { ident, fields, .. }| {
match fields {
Fields::Named(_) => quote! { #ident{..} },
Fields::Unnamed(_) => quote! { #ident(..) },
Fields::Unit => quote! { #ident },
}
})
.collect();
let name: Vec<TokenStream> = variants
.iter()
.map(|v| {
let ident = v.ident.to_string().to_snake_case();
quote! { #ident }
})
.collect();
Ok(quote!(
impl Iden for #ident {
fn unquoted(&self, s: &mut dyn std::fmt::Write) {
write!(s, "{}", self.as_str()).unwrap();
}
}
impl IdenStatic for #ident {
fn as_str(&self) -> &str {
match self {
#(Self::#variant => #name),*
}
}
}
impl PrimaryKeyTrait for #ident {}
impl PrimaryKeyOfModel<Model> for #ident {
fn into_column(self) -> <Model as ModelTrait>::Column {
match self {
#(Self::#variant => Column::#variant),*
}
}
}
))
}

54
sea-orm-macros/src/lib.rs Normal file
View File

@ -0,0 +1,54 @@
extern crate proc_macro;
use proc_macro::TokenStream;
use syn::{DeriveInput, parse_macro_input};
mod derives;
#[proc_macro_derive(DeriveEntity, attributes(entity))]
pub fn derive_entity(input: TokenStream) -> TokenStream {
let DeriveInput {
ident, attrs, ..
} = parse_macro_input!(input);
match derives::expend_derive_entity(ident, attrs) {
Ok(ts) => ts.into(),
Err(e) => e.to_compile_error().into(),
}
}
#[proc_macro_derive(DerivePrimaryKey)]
pub fn derive_primary_key(input: TokenStream) -> TokenStream {
let DeriveInput {
ident, data, ..
} = parse_macro_input!(input);
match derives::expend_derive_primary_key(ident, data) {
Ok(ts) => ts.into(),
Err(e) => e.to_compile_error().into(),
}
}
#[proc_macro_derive(DeriveColumn)]
pub fn derive_column(input: TokenStream) -> TokenStream {
let DeriveInput {
ident, data, ..
} = parse_macro_input!(input);
match derives::expend_derive_column(ident, data) {
Ok(ts) => ts.into(),
Err(e) => e.to_compile_error().into(),
}
}
#[proc_macro_derive(DeriveModel)]
pub fn derive_model(input: TokenStream) -> TokenStream {
let DeriveInput {
ident, data, ..
} = parse_macro_input!(input);
match derives::expend_derive_model(ident, data) {
Ok(ts) => ts.into(),
Err(e) => e.to_compile_error().into(),
}
}

View File

@ -1,5 +1,5 @@
pub use crate::{
ColumnTrait, ColumnType, EntityName, EntityTrait, EnumIter, Iden, IdenStatic, ModelTrait,
PrimaryKeyOfModel, PrimaryKeyTrait, QueryResult, Related, RelationDef, RelationTrait, Select,
TypeErr, Value,
TypeErr, Value, DeriveEntity, DerivePrimaryKey, DeriveColumn, DeriveModel,
};

View File

@ -15,3 +15,9 @@ pub use query::*;
pub use sea_query;
pub use sea_query::Iden;
pub use strum::EnumIter;
pub use sea_orm_macros::{
DeriveEntity,
DerivePrimaryKey,
DeriveColumn,
DeriveModel,
};

View File

@ -1,21 +1,22 @@
use crate::entity::prelude::*;
#[derive(Copy, Clone, Default, Debug)]
#[derive(Copy, Clone, Default, Debug, DeriveEntity)]
#[entity = "cake"]
pub struct Entity;
#[derive(Clone, Debug, Default, PartialEq)]
#[derive(Clone, Debug, Default, PartialEq, DeriveModel)]
pub struct Model {
pub id: i32,
pub name: String,
}
#[derive(Copy, Clone, Debug, EnumIter)]
#[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)]
pub enum Column {
Id,
Name,
}
#[derive(Copy, Clone, Debug, EnumIter)]
#[derive(Copy, Clone, Debug, EnumIter, DerivePrimaryKey)]
pub enum PrimaryKey {
Id,
}
@ -25,16 +26,6 @@ pub enum Relation {
Fruit,
}
impl EntityTrait for Entity {
type Model = Model;
type Column = Column;
type PrimaryKey = PrimaryKey;
type Relation = Relation;
}
impl ColumnTrait for Column {
type EntityName = Entity;
@ -68,91 +59,3 @@ impl Model {
Entity::find_related().belongs_to::<Entity>(self)
}
}
// TODO: implement with derive macro
impl EntityName for Entity {}
// TODO: implement with derive macro
impl IdenStatic for Entity {
fn as_str(&self) -> &str {
"cake"
}
}
// TODO: implement with derive macro
impl Iden for Entity {
fn unquoted(&self, s: &mut dyn std::fmt::Write) {
write!(s, "{}", self.as_str()).unwrap();
}
}
// TODO: implement with derive macro
impl ModelTrait for Model {
type Column = Column;
fn get(&self, c: Self::Column) -> Value {
match c {
Column::Id => self.id.clone().into(),
Column::Name => self.name.clone().into(),
}
}
fn set(&mut self, c: Self::Column, v: Value) {
match c {
Column::Id => self.id = v.unwrap(),
Column::Name => self.name = v.unwrap(),
}
}
fn from_query_result(row: &QueryResult, pre: &str) -> Result<Self, TypeErr> {
Ok(Self {
id: row.try_get(pre, Column::Id.as_str())?,
name: row.try_get(pre, Column::Name.as_str())?,
})
}
}
// TODO: implement with derive macro
impl Iden for Column {
fn unquoted(&self, s: &mut dyn std::fmt::Write) {
write!(s, "{}", self.as_str()).unwrap();
}
}
// TODO: implement with derive macro
impl IdenStatic for Column {
fn as_str(&self) -> &str {
match self {
Self::Id => "id",
Self::Name => "name",
}
}
}
// TODO: implement with derive macro
impl Iden for PrimaryKey {
fn unquoted(&self, s: &mut dyn std::fmt::Write) {
write!(s, "{}", self.as_str()).unwrap();
}
}
// TODO: implement with derive macro
impl IdenStatic for PrimaryKey {
fn as_str(&self) -> &str {
match self {
Self::Id => "id",
}
}
}
// TODO: implement with derive macro
impl PrimaryKeyTrait for PrimaryKey {}
// TODO: implement with derive macro
impl PrimaryKeyOfModel<Model> for PrimaryKey {
fn into_column(self) -> <Model as ModelTrait>::Column {
match self {
Self::Id => Column::Id,
}
}
}

View File

@ -1,23 +1,24 @@
use crate::entity::prelude::*;
#[derive(Copy, Clone, Default, Debug)]
#[derive(Copy, Clone, Default, Debug, DeriveEntity)]
#[entity = "fruit"]
pub struct Entity;
#[derive(Clone, Debug, Default, PartialEq)]
#[derive(Clone, Debug, Default, PartialEq, DeriveModel)]
pub struct Model {
pub id: i32,
pub name: String,
pub cake_id: Option<i32>,
}
#[derive(Copy, Clone, Debug, EnumIter)]
#[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)]
pub enum Column {
Id,
Name,
CakeId,
}
#[derive(Copy, Clone, Debug, EnumIter)]
#[derive(Copy, Clone, Debug, EnumIter, DerivePrimaryKey)]
pub enum PrimaryKey {
Id,
}
@ -25,16 +26,6 @@ pub enum PrimaryKey {
#[derive(Copy, Clone, Debug, EnumIter)]
pub enum Relation {}
impl EntityTrait for Entity {
type Model = Model;
type Column = Column;
type PrimaryKey = PrimaryKey;
type Relation = Relation;
}
impl ColumnTrait for Column {
type EntityName = Entity;
@ -52,95 +43,3 @@ impl RelationTrait for Relation {
panic!()
}
}
// TODO: implement with derive macro
impl EntityName for Entity {}
// TODO: implement with derive macro
impl IdenStatic for Entity {
fn as_str(&self) -> &str {
"fruit"
}
}
// TODO: implement with derive macro
impl Iden for Entity {
fn unquoted(&self, s: &mut dyn std::fmt::Write) {
write!(s, "{}", self.as_str()).unwrap();
}
}
// TODO: implement with derive macro
impl ModelTrait for Model {
type Column = Column;
fn get(&self, c: Self::Column) -> Value {
match c {
Column::Id => self.id.clone().into(),
Column::Name => self.name.clone().into(),
Column::CakeId => self.cake_id.clone().into(),
}
}
fn set(&mut self, c: Self::Column, v: Value) {
match c {
Column::Id => self.id = v.unwrap(),
Column::Name => self.name = v.unwrap(),
Column::CakeId => self.cake_id = v.unwrap(),
}
}
fn from_query_result(row: &QueryResult, pre: &str) -> Result<Self, TypeErr> {
Ok(Self {
id: row.try_get(pre, Column::Id.as_str())?,
name: row.try_get(pre, Column::Name.as_str())?,
cake_id: row.try_get(pre, Column::CakeId.as_str())?,
})
}
}
// TODO: implement with derive macro
impl Iden for Column {
fn unquoted(&self, s: &mut dyn std::fmt::Write) {
write!(s, "{}", self.as_str()).unwrap();
}
}
// TODO: implement with derive macro
impl IdenStatic for Column {
fn as_str(&self) -> &str {
match self {
Self::Id => "id",
Self::Name => "name",
Self::CakeId => "cake_id",
}
}
}
// TODO: implement with derive macro
impl Iden for PrimaryKey {
fn unquoted(&self, s: &mut dyn std::fmt::Write) {
write!(s, "{}", self.as_str()).unwrap();
}
}
// TODO: implement with derive macro
impl IdenStatic for PrimaryKey {
fn as_str(&self) -> &str {
match self {
Self::Id => "id",
}
}
}
// TODO: implement with derive macro
impl PrimaryKeyTrait for PrimaryKey {}
// TODO: implement with derive macro
impl PrimaryKeyOfModel<Model> for PrimaryKey {
fn into_column(self) -> <Model as ModelTrait>::Column {
match self {
Self::Id => Column::Id,
}
}
}