sea-orm/sea-orm-macros/src/derives/related_entity.rs
Panagiotis Karatakis 3300336b1a
Expand SeaORM entity generator with Seaography related data (#1599)
* Add DeriveRelatedEntity macro

* Add generation for related enum and seaography

* Add seaography cli param

* update codegen tests

* Fix DeriveRelatedEntity macro doc and includes

* Fix all RelatedEntity variants for RelationBuilder

* Add tests for code

* Cargo format

* Fix clippy code

* Fix format

* Fix unit tests

* Fix unit tests

* Provide default for seaography::RelationBuilder

* Update changelog

* Update tests

* Modify code to match feedback
* Bring old Related Impl trait generation
* Modify DeriveRelatedEntity to gen impl seaography::RelationBuilder
* Generate RelatedEntity enum when seaography flag is enabled

* Update documentation

* Update Changelog

* Fix format errors

* Fix code generation
* relations with suffix are definition based
* Rev => Reverse easier to read
* snake_case to cameCase for name generation

* Fix unit tests

* Update lib.rs

* derive `seaography::RelationBuilder` only when `seaography` feature is enabled

* Try constructing async-graphql root for "related entity" and "entity" without relation

* Update demo

* CHANGELOG

* Update Cargo.toml

Co-authored-by: Chris Tsang <chris.2y3@outlook.com>

* Revert "Update Cargo.toml"

This reverts commit 6b1669836a4fb5040bfb08999f0cf640c74dc64d.

---------

Co-authored-by: Billy Chan <ccw.billy.123@gmail.com>
Co-authored-by: Chris Tsang <chris.2y3@outlook.com>
2023-05-19 22:14:46 +08:00

126 lines
4.3 KiB
Rust

use heck::ToLowerCamelCase;
use proc_macro2::TokenStream;
use quote::{quote, quote_spanned};
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<syn::Variant, syn::token::Comma>,
}
impl DeriveRelatedEntity {
fn new(input: syn::DeriveInput) -> Result<Self, Error> {
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,
})
}
fn expand(&self) -> syn::Result<TokenStream> {
let ident = &self.ident;
let entity_ident = &self.entity_ident;
let variant_implementations: Vec<TokenStream> = 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::<Result<Vec<_>, _>>()?;
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 parse_lit_string(lit: &syn::Lit) -> syn::Result<TokenStream> {
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<TokenStream> {
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),
}
}