diff --git a/sea-orm-macros/Cargo.toml b/sea-orm-macros/Cargo.toml index 1b8ed548..8e2e7d7c 100644 --- a/sea-orm-macros/Cargo.toml +++ b/sea-orm-macros/Cargo.toml @@ -23,6 +23,7 @@ syn = { version = "2", default-features = false, features = ["parsing", "proc-ma quote = { version = "1", default-features = false } heck = { version = "0.4", default-features = false } proc-macro2 = { version = "1", default-features = false } +proc-macro-crate = { version = "3.2.0", optional = true } unicode-ident = { version = "1" } [dev-dependencies] @@ -34,4 +35,4 @@ default = ["derive"] postgres-array = [] derive = ["bae"] strum = [] -seaography = [] +seaography = ["proc-macro-crate"] diff --git a/sea-orm-macros/src/derives/attributes.rs b/sea-orm-macros/src/derives/attributes.rs index 4f46f55a..708fe371 100644 --- a/sea-orm-macros/src/derives/attributes.rs +++ b/sea-orm-macros/src/derives/attributes.rs @@ -38,6 +38,7 @@ pub mod field_attr { } } +#[cfg(feature = "seaography")] pub mod related_attr { use bae::FromAttributes; diff --git a/sea-orm-macros/src/derives/related_entity.rs b/sea-orm-macros/src/derives/related_entity.rs index bf74a48c..9db8b531 100644 --- a/sea-orm-macros/src/derives/related_entity.rs +++ b/sea-orm-macros/src/derives/related_entity.rs @@ -1,125 +1,152 @@ -use heck::ToLowerCamelCase; -use proc_macro2::TokenStream; -use quote::{quote, quote_spanned}; +#[cfg(feature = "seaography")] +mod private { + use heck::ToLowerCamelCase; + use proc_macro2::{Ident, Span, TokenStream}; + use proc_macro_crate::{crate_name, FoundCrate}; + use quote::{quote, quote_spanned}; -use crate::derives::attributes::related_attr; + use crate::derives::attributes::related_attr; -enum Error { - InputNotEnum, - InvalidEntityPath, - Syn(syn::Error), -} - -struct DeriveRelatedEntity { - entity_ident: TokenStream, - ident: syn::Ident, - variants: syn::punctuated::Punctuated, -} - -impl DeriveRelatedEntity { - fn new(input: syn::DeriveInput) -> Result { - let sea_attr = related_attr::SeaOrm::try_from_attributes(&input.attrs) - .map_err(Error::Syn)? - .unwrap_or_default(); - - let ident = input.ident; - let entity_ident = match sea_attr.entity.as_ref().map(Self::parse_lit_string) { - Some(entity_ident) => entity_ident.map_err(|_| Error::InvalidEntityPath)?, - None => quote! { Entity }, - }; - - let variants = match input.data { - syn::Data::Enum(syn::DataEnum { variants, .. }) => variants, - _ => return Err(Error::InputNotEnum), - }; - - Ok(DeriveRelatedEntity { - entity_ident, - ident, - variants, - }) + enum Error { + InputNotEnum, + InvalidEntityPath, + Syn(syn::Error), } - fn expand(&self) -> syn::Result { - let ident = &self.ident; - let entity_ident = &self.entity_ident; + struct DeriveRelatedEntity { + entity_ident: TokenStream, + ident: syn::Ident, + variants: syn::punctuated::Punctuated, + } - let variant_implementations: Vec = self - .variants - .iter() - .map(|variant| { - let attr = related_attr::SeaOrm::from_attributes(&variant.attrs)?; + impl DeriveRelatedEntity { + fn new(input: syn::DeriveInput) -> Result { + let sea_attr = related_attr::SeaOrm::try_from_attributes(&input.attrs) + .map_err(Error::Syn)? + .unwrap_or_default(); - let enum_name = &variant.ident; + let ident = input.ident; + let entity_ident = match sea_attr.entity.as_ref().map(Self::parse_lit_string) { + Some(entity_ident) => entity_ident.map_err(|_| Error::InvalidEntityPath)?, + None => quote! { Entity }, + }; - let target_entity = attr - .entity - .as_ref() - .map(Self::parse_lit_string) - .ok_or_else(|| { - syn::Error::new_spanned(variant, "Missing value for 'entity'") - })??; - - let def = match attr.def { - Some(def) => Some(Self::parse_lit_string(&def).map_err(|_| { - syn::Error::new_spanned(variant, "Missing value for 'def'") - })?), - None => None, - }; - - let name = enum_name.to_string().to_lower_camel_case(); - - if let Some(def) = def { - Result::<_, syn::Error>::Ok(quote! { - Self::#enum_name => builder.get_relation::<#entity_ident, #target_entity>(#name, #def) - }) - } else { - Result::<_, syn::Error>::Ok(quote! { - Self::#enum_name => via_builder.get_relation::<#entity_ident, #target_entity>(#name) - }) - } + let variants = match input.data { + syn::Data::Enum(syn::DataEnum { variants, .. }) => variants, + _ => return Err(Error::InputNotEnum), + }; + Ok(DeriveRelatedEntity { + entity_ident, + ident, + variants, }) - .collect::, _>>()?; + } - Ok(quote! { - impl seaography::RelationBuilder for #ident { - fn get_relation(&self, context: & 'static seaography::BuilderContext) -> async_graphql::dynamic::Field { - let builder = seaography::EntityObjectRelationBuilder { context }; - let via_builder = seaography::EntityObjectViaRelationBuilder { context }; - match self { - #(#variant_implementations,)* - _ => panic!("No relations for this entity"), + fn expand(&self) -> syn::Result { + let ident = &self.ident; + let entity_ident = &self.entity_ident; + + let variant_implementations: Vec = self + .variants + .iter() + .map(|variant| { + let attr = related_attr::SeaOrm::from_attributes(&variant.attrs)?; + + let enum_name = &variant.ident; + + let target_entity = attr + .entity + .as_ref() + .map(Self::parse_lit_string) + .ok_or_else(|| { + syn::Error::new_spanned(variant, "Missing value for 'entity'") + })??; + + let def = match attr.def { + Some(def) => Some(Self::parse_lit_string(&def).map_err(|_| { + syn::Error::new_spanned(variant, "Missing value for 'def'") + })?), + None => None, + }; + + let name = enum_name.to_string().to_lower_camel_case(); + + if let Some(def) = def { + Result::<_, syn::Error>::Ok(quote! { + Self::#enum_name => builder.get_relation::<#entity_ident, #target_entity>(#name, #def) + }) + } else { + Result::<_, syn::Error>::Ok(quote! { + Self::#enum_name => via_builder.get_relation::<#entity_ident, #target_entity>(#name) + }) } - } + }) + .collect::, _>>()?; + + // Get the path of the `async-graphql` on the application's Cargo.toml + let async_graphql_crate = match crate_name("async-graphql") { + // if found, use application's `async-graphql` + Ok(FoundCrate::Name(name)) => { + let ident = Ident::new(&name, Span::call_site()); + quote! { #ident } + } + Ok(FoundCrate::Itself) => quote! { async_graphql }, + // if not, then use the `async-graphql` re-exported by `seaography` + Err(_) => quote! { seaography::async_graphql }, + }; + + Ok(quote! { + impl seaography::RelationBuilder for #ident { + fn get_relation(&self, context: & 'static seaography::BuilderContext) -> #async_graphql_crate::dynamic::Field { + let builder = seaography::EntityObjectRelationBuilder { context }; + let via_builder = seaography::EntityObjectViaRelationBuilder { context }; + match self { + #(#variant_implementations,)* + _ => panic!("No relations for this entity"), + } + } + + } + }) + } + + fn parse_lit_string(lit: &syn::Lit) -> syn::Result { + match lit { + syn::Lit::Str(lit_str) => lit_str + .value() + .parse() + .map_err(|_| syn::Error::new_spanned(lit, "attribute not valid")), + _ => Err(syn::Error::new_spanned(lit, "attribute must be a string")), } - }) + } } - fn parse_lit_string(lit: &syn::Lit) -> syn::Result { - match lit { - syn::Lit::Str(lit_str) => lit_str - .value() - .parse() - .map_err(|_| syn::Error::new_spanned(lit, "attribute not valid")), - _ => Err(syn::Error::new_spanned(lit, "attribute must be a string")), + /// Method to derive a Related enumeration + pub fn expand_derive_related_entity(input: syn::DeriveInput) -> syn::Result { + let ident_span = input.ident.span(); + + match DeriveRelatedEntity::new(input) { + Ok(model) => model.expand(), + Err(Error::InputNotEnum) => Ok(quote_spanned! { + ident_span => compile_error!("you can only derive DeriveRelation on enums"); + }), + Err(Error::InvalidEntityPath) => Ok(quote_spanned! { + ident_span => compile_error!("invalid attribute value for 'entity'"); + }), + Err(Error::Syn(err)) => Err(err), } } } -/// Method to derive a Related enumeration -pub fn expand_derive_related_entity(input: syn::DeriveInput) -> syn::Result { - let ident_span = input.ident.span(); +#[cfg(not(feature = "seaography"))] +mod private { + use proc_macro2::TokenStream; - match DeriveRelatedEntity::new(input) { - Ok(model) => model.expand(), - Err(Error::InputNotEnum) => Ok(quote_spanned! { - ident_span => compile_error!("you can only derive DeriveRelation on enums"); - }), - Err(Error::InvalidEntityPath) => Ok(quote_spanned! { - ident_span => compile_error!("invalid attribute value for 'entity'"); - }), - Err(Error::Syn(err)) => Err(err), + pub fn expand_derive_related_entity(_: syn::DeriveInput) -> syn::Result { + Ok(TokenStream::new()) } } + +pub use private::*; diff --git a/sea-orm-macros/src/lib.rs b/sea-orm-macros/src/lib.rs index 70895849..536165c0 100644 --- a/sea-orm-macros/src/lib.rs +++ b/sea-orm-macros/src/lib.rs @@ -675,13 +675,9 @@ pub fn derive_relation(input: TokenStream) -> TokenStream { #[proc_macro_derive(DeriveRelatedEntity, attributes(sea_orm))] pub fn derive_related_entity(input: TokenStream) -> TokenStream { let input = parse_macro_input!(input as DeriveInput); - if cfg!(feature = "seaography") { - derives::expand_derive_related_entity(input) - .unwrap_or_else(Error::into_compile_error) - .into() - } else { - TokenStream::new() - } + derives::expand_derive_related_entity(input) + .unwrap_or_else(Error::into_compile_error) + .into() } /// The DeriveMigrationName derive macro will implement `sea_orm_migration::MigrationName` for a migration.