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>
This commit is contained in:
parent
fd6c303740
commit
3300336b1a
29
CHANGELOG.md
29
CHANGELOG.md
@ -185,6 +185,33 @@ assert_eq!(
|
|||||||
```
|
```
|
||||||
* [sea-orm-cli] Added support for generating migration of space separated name, for example executing `sea-orm-cli migrate generate "create accounts table"` command will create `m20230503_000000_create_accounts_table.rs` for you https://github.com/SeaQL/sea-orm/pull/1570
|
* [sea-orm-cli] Added support for generating migration of space separated name, for example executing `sea-orm-cli migrate generate "create accounts table"` command will create `m20230503_000000_create_accounts_table.rs` for you https://github.com/SeaQL/sea-orm/pull/1570
|
||||||
|
|
||||||
|
* Add `seaography` flag to `sea-orm`, `sea-orm-orm-macros` and `sea-orm-cli` https://github.com/SeaQL/sea-orm/pull/1599
|
||||||
|
* Add generation of `seaography` related information to `sea-orm-codegen` https://github.com/SeaQL/sea-orm/pull/1599
|
||||||
|
|
||||||
|
The following information is added in entities files by `sea-orm-cli` when flag `seaography` is `true`
|
||||||
|
```rust
|
||||||
|
/// ... Entity File ...
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelatedEntity)]
|
||||||
|
pub enum RelatedEntity {
|
||||||
|
#[sea_orm(entity = "super::address::Entity")]
|
||||||
|
Address,
|
||||||
|
#[sea_orm(entity = "super::payment::Entity")]
|
||||||
|
Payment,
|
||||||
|
#[sea_orm(entity = "super::rental::Entity")]
|
||||||
|
Rental,
|
||||||
|
#[sea_orm(entity = "Entity", def = "Relation::SelfRef.def()")]
|
||||||
|
SelfRef,
|
||||||
|
#[sea_orm(entity = "super::store::Entity")]
|
||||||
|
Store,
|
||||||
|
#[sea_orm(entity = "Entity", def = "Relation::SelfRef.def().rev()")]
|
||||||
|
SelfRefRev,
|
||||||
|
}
|
||||||
|
```
|
||||||
|
* Add `DeriveEntityRelated` macro https://github.com/SeaQL/sea-orm/pull/1599
|
||||||
|
|
||||||
|
The DeriveRelatedEntity derive macro will implement `seaography::RelationBuilder` for `RelatedEntity` enumeration when the `seaography` feature is enabled
|
||||||
|
|
||||||
### Enhancements
|
### Enhancements
|
||||||
|
|
||||||
* Added `Migration::name()` and `Migration::status()` getters for the name and status of `sea_orm_migration::Migration` https://github.com/SeaQL/sea-orm/pull/1519
|
* Added `Migration::name()` and `Migration::status()` getters for the name and status of `sea_orm_migration::Migration` https://github.com/SeaQL/sea-orm/pull/1519
|
||||||
@ -457,7 +484,7 @@ impl ColumnTrait for Column {
|
|||||||
### Breaking Changes
|
### Breaking Changes
|
||||||
|
|
||||||
* [sea-orm-cli] Enable --universal-time by default https://github.com/SeaQL/sea-orm/pull/1420
|
* [sea-orm-cli] Enable --universal-time by default https://github.com/SeaQL/sea-orm/pull/1420
|
||||||
* Added `RecordNotInserted` and `RecordNotUpdated` to `DbErr`
|
* Added `RecordNotInserted` and `RecordNotUpdated` to `DbErr`
|
||||||
* Added `ConnectionTrait::execute_unprepared` method https://github.com/SeaQL/sea-orm/pull/1327
|
* Added `ConnectionTrait::execute_unprepared` method https://github.com/SeaQL/sea-orm/pull/1327
|
||||||
* As part of https://github.com/SeaQL/sea-orm/pull/1311, the required method of `TryGetable` changed:
|
* As part of https://github.com/SeaQL/sea-orm/pull/1311, the required method of `TryGetable` changed:
|
||||||
```rust
|
```rust
|
||||||
|
@ -122,3 +122,4 @@ runtime-tokio-rustls = [
|
|||||||
"runtime-tokio",
|
"runtime-tokio",
|
||||||
]
|
]
|
||||||
tests-cfg = ["serde/derive"]
|
tests-cfg = ["serde/derive"]
|
||||||
|
seaography = ["sea-orm-macros/seaography"]
|
||||||
|
2
issues/1599/Cargo.toml
Normal file
2
issues/1599/Cargo.toml
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
[workspace]
|
||||||
|
members = ["entity", "graphql"]
|
17
issues/1599/entity/Cargo.toml
Normal file
17
issues/1599/entity/Cargo.toml
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
[package]
|
||||||
|
name = "entity"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
publish = false
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
name = "entity"
|
||||||
|
path = "src/lib.rs"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
sea-orm = { path = "../../../" }
|
||||||
|
seaography = { path = "../../../../seaography", optional = true }
|
||||||
|
async-graphql = { version = "5", optional = true }
|
||||||
|
|
||||||
|
[features]
|
||||||
|
seaography = ["dep:seaography", "async-graphql", "sea-orm/seaography"]
|
60
issues/1599/entity/src/cake.rs
Normal file
60
issues/1599/entity/src/cake.rs
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
use sea_orm::entity::prelude::*;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)]
|
||||||
|
#[sea_orm(table_name = "cake")]
|
||||||
|
pub struct Model {
|
||||||
|
#[sea_orm(primary_key)]
|
||||||
|
pub id: i32,
|
||||||
|
#[sea_orm(column_name = "name", enum_name = "Name")]
|
||||||
|
pub name: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||||
|
pub enum Relation {
|
||||||
|
#[sea_orm(has_many = "super::fruit::Entity")]
|
||||||
|
Fruit,
|
||||||
|
#[sea_orm(
|
||||||
|
has_many = "super::fruit::Entity",
|
||||||
|
on_condition = r#"super::fruit::Column::Name.like("%tropical%")"#
|
||||||
|
)]
|
||||||
|
TropicalFruit,
|
||||||
|
#[sea_orm(
|
||||||
|
has_many = "super::fruit::Entity",
|
||||||
|
condition_type = "any",
|
||||||
|
on_condition = r#"super::fruit::Column::Name.like("%tropical%")"#
|
||||||
|
)]
|
||||||
|
OrTropicalFruit,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Related<super::fruit::Entity> for Entity {
|
||||||
|
fn to() -> RelationDef {
|
||||||
|
Relation::Fruit.def()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Related<super::filling::Entity> for Entity {
|
||||||
|
fn to() -> RelationDef {
|
||||||
|
super::cake_filling::Relation::Filling.def()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn via() -> Option<RelationDef> {
|
||||||
|
Some(super::cake_filling::Relation::Cake.def().rev())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelatedEntity)]
|
||||||
|
pub enum RelatedEntity {
|
||||||
|
#[sea_orm(entity = "super::fruit::Entity")]
|
||||||
|
Fruit,
|
||||||
|
#[sea_orm(entity = "super::filling::Entity")]
|
||||||
|
Filling,
|
||||||
|
#[sea_orm(entity = "super::fruit::Entity", def = "Relation::TropicalFruit.def()")]
|
||||||
|
TropicalFruit,
|
||||||
|
#[sea_orm(
|
||||||
|
entity = "super::fruit::Entity",
|
||||||
|
def = "Relation::OrTropicalFruit.def()"
|
||||||
|
)]
|
||||||
|
OrTropicalFruit,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ActiveModelBehavior for ActiveModel {}
|
70
issues/1599/entity/src/cake_filling.rs
Normal file
70
issues/1599/entity/src/cake_filling.rs
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
use sea_orm::entity::prelude::*;
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Default, Debug, DeriveEntity)]
|
||||||
|
pub struct Entity;
|
||||||
|
|
||||||
|
impl EntityName for Entity {
|
||||||
|
fn table_name(&self) -> &str {
|
||||||
|
"cake_filling"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, DeriveModel, DeriveActiveModel)]
|
||||||
|
pub struct Model {
|
||||||
|
pub cake_id: i32,
|
||||||
|
pub filling_id: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)]
|
||||||
|
pub enum Column {
|
||||||
|
CakeId,
|
||||||
|
FillingId,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, EnumIter, DerivePrimaryKey)]
|
||||||
|
pub enum PrimaryKey {
|
||||||
|
CakeId,
|
||||||
|
FillingId,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PrimaryKeyTrait for PrimaryKey {
|
||||||
|
type ValueType = (i32, i32);
|
||||||
|
|
||||||
|
fn auto_increment() -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, EnumIter)]
|
||||||
|
pub enum Relation {
|
||||||
|
Cake,
|
||||||
|
Filling,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ColumnTrait for Column {
|
||||||
|
type EntityName = Entity;
|
||||||
|
|
||||||
|
fn def(&self) -> ColumnDef {
|
||||||
|
match self {
|
||||||
|
Self::CakeId => ColumnType::Integer.def(),
|
||||||
|
Self::FillingId => ColumnType::Integer.def(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RelationTrait for Relation {
|
||||||
|
fn def(&self) -> RelationDef {
|
||||||
|
match self {
|
||||||
|
Self::Cake => Entity::belongs_to(super::cake::Entity)
|
||||||
|
.from(Column::CakeId)
|
||||||
|
.to(super::cake::Column::Id)
|
||||||
|
.into(),
|
||||||
|
Self::Filling => Entity::belongs_to(super::filling::Entity)
|
||||||
|
.from(Column::FillingId)
|
||||||
|
.to(super::filling::Column::Id)
|
||||||
|
.into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ActiveModelBehavior for ActiveModel {}
|
80
issues/1599/entity/src/filling.rs
Normal file
80
issues/1599/entity/src/filling.rs
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
use sea_orm::entity::prelude::*;
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Default, Debug, DeriveEntity)]
|
||||||
|
#[sea_orm(table_name = "filling")]
|
||||||
|
pub struct Entity;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, DeriveModel, DeriveActiveModel)]
|
||||||
|
pub struct Model {
|
||||||
|
pub id: i32,
|
||||||
|
pub name: String,
|
||||||
|
pub vendor_id: Option<i32>,
|
||||||
|
#[sea_orm(ignore)]
|
||||||
|
pub ignored_attr: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
// If your column names are not in snake-case, derive `DeriveCustomColumn` here.
|
||||||
|
#[derive(Copy, Clone, Debug, EnumIter, DeriveCustomColumn)]
|
||||||
|
pub enum Column {
|
||||||
|
Id,
|
||||||
|
Name,
|
||||||
|
VendorId,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then, customize each column names here.
|
||||||
|
impl IdenStatic for Column {
|
||||||
|
fn as_str(&self) -> &str {
|
||||||
|
match self {
|
||||||
|
// Override column names
|
||||||
|
Self::Id => "id",
|
||||||
|
// Leave all other columns using default snake-case values
|
||||||
|
_ => self.default_as_str(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, EnumIter, DerivePrimaryKey)]
|
||||||
|
pub enum PrimaryKey {
|
||||||
|
Id,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PrimaryKeyTrait for PrimaryKey {
|
||||||
|
type ValueType = i32;
|
||||||
|
|
||||||
|
fn auto_increment() -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, EnumIter)]
|
||||||
|
pub enum Relation {}
|
||||||
|
|
||||||
|
impl ColumnTrait for Column {
|
||||||
|
type EntityName = Entity;
|
||||||
|
|
||||||
|
fn def(&self) -> ColumnDef {
|
||||||
|
match self {
|
||||||
|
Self::Id => ColumnType::Integer.def(),
|
||||||
|
Self::Name => ColumnType::String(None).def(),
|
||||||
|
Self::VendorId => ColumnType::Integer.def().nullable(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RelationTrait for Relation {
|
||||||
|
fn def(&self) -> RelationDef {
|
||||||
|
panic!("No RelationDef")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Related<super::cake::Entity> for Entity {
|
||||||
|
fn to() -> RelationDef {
|
||||||
|
super::cake_filling::Relation::Cake.def()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn via() -> Option<RelationDef> {
|
||||||
|
Some(super::cake_filling::Relation::Filling.def().rev())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ActiveModelBehavior for ActiveModel {}
|
29
issues/1599/entity/src/fruit.rs
Normal file
29
issues/1599/entity/src/fruit.rs
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
use sea_orm::entity::prelude::*;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)]
|
||||||
|
#[sea_orm(table_name = "fruit")]
|
||||||
|
pub struct Model {
|
||||||
|
#[sea_orm(primary_key)]
|
||||||
|
#[cfg_attr(feature = "with-json", serde(skip_deserializing))]
|
||||||
|
pub id: i32,
|
||||||
|
pub name: String,
|
||||||
|
pub cake_id: Option<i32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||||
|
pub enum Relation {
|
||||||
|
#[sea_orm(
|
||||||
|
belongs_to = "super::cake::Entity",
|
||||||
|
from = "Column::CakeId",
|
||||||
|
to = "super::cake::Column::Id"
|
||||||
|
)]
|
||||||
|
Cake,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Related<super::cake::Entity> for Entity {
|
||||||
|
fn to() -> RelationDef {
|
||||||
|
Relation::Cake.def()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ActiveModelBehavior for ActiveModel {}
|
4
issues/1599/entity/src/lib.rs
Normal file
4
issues/1599/entity/src/lib.rs
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
pub mod cake;
|
||||||
|
pub mod cake_filling;
|
||||||
|
pub mod filling;
|
||||||
|
pub mod fruit;
|
20
issues/1599/graphql/Cargo.toml
Normal file
20
issues/1599/graphql/Cargo.toml
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
[package]
|
||||||
|
name = "graphql"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
publish = false
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
poem = { version = "1.3.55" }
|
||||||
|
async-graphql-poem = { version = "5.0.6" }
|
||||||
|
async-graphql = { version = "5.0.6", features = ["decimal", "chrono", "dataloader", "dynamic-schema"] }
|
||||||
|
async-trait = { version = "0.1.64" }
|
||||||
|
dotenv = "0.15.0"
|
||||||
|
tokio = { version = "1.26.0", features = ["macros", "rt-multi-thread"] }
|
||||||
|
tracing = { version = "0.1.37" }
|
||||||
|
tracing-subscriber = { version = "0.3.16" }
|
||||||
|
lazy_static = { version = "1.4.0" }
|
||||||
|
|
||||||
|
sea-orm = { path = "../../../" }
|
||||||
|
entity = { path = "../entity", features = ["seaography"] }
|
||||||
|
seaography = { path = "../../../../seaography" }
|
64
issues/1599/graphql/src/main.rs
Normal file
64
issues/1599/graphql/src/main.rs
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
use async_graphql::{
|
||||||
|
dataloader::DataLoader,
|
||||||
|
http::{playground_source, GraphQLPlaygroundConfig},
|
||||||
|
};
|
||||||
|
use async_graphql_poem::GraphQL;
|
||||||
|
use dotenv::dotenv;
|
||||||
|
use lazy_static::lazy_static;
|
||||||
|
use poem::{get, handler, listener::TcpListener, web::Html, IntoResponse, Route, Server};
|
||||||
|
use sea_orm::{prelude::*, Database};
|
||||||
|
use std::env;
|
||||||
|
|
||||||
|
pub mod query_root;
|
||||||
|
|
||||||
|
pub struct OrmDataloader {
|
||||||
|
pub db: DatabaseConnection,
|
||||||
|
}
|
||||||
|
|
||||||
|
lazy_static! {
|
||||||
|
static ref URL: String = env::var("URL").unwrap_or("0.0.0.0:8000".into());
|
||||||
|
static ref ENDPOINT: String = env::var("ENDPOINT").unwrap_or("/".into());
|
||||||
|
static ref DATABASE_URL: String =
|
||||||
|
env::var("DATABASE_URL").expect("DATABASE_URL environment variable not set");
|
||||||
|
static ref DEPTH_LIMIT: Option<usize> = env::var("DEPTH_LIMIT").map_or(None, |data| Some(
|
||||||
|
data.parse().expect("DEPTH_LIMIT is not a number")
|
||||||
|
));
|
||||||
|
static ref COMPLEXITY_LIMIT: Option<usize> = env::var("COMPLEXITY_LIMIT")
|
||||||
|
.map_or(None, |data| {
|
||||||
|
Some(data.parse().expect("COMPLEXITY_LIMIT is not a number"))
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[handler]
|
||||||
|
async fn graphql_playground() -> impl IntoResponse {
|
||||||
|
Html(playground_source(GraphQLPlaygroundConfig::new(&ENDPOINT)))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::main]
|
||||||
|
async fn main() {
|
||||||
|
dotenv().ok();
|
||||||
|
tracing_subscriber::fmt()
|
||||||
|
.with_max_level(tracing::Level::INFO)
|
||||||
|
.with_test_writer()
|
||||||
|
.init();
|
||||||
|
let database = Database::connect(&*DATABASE_URL)
|
||||||
|
.await
|
||||||
|
.expect("Fail to initialize database connection");
|
||||||
|
let orm_dataloader: DataLoader<OrmDataloader> = DataLoader::new(
|
||||||
|
OrmDataloader {
|
||||||
|
db: database.clone(),
|
||||||
|
},
|
||||||
|
tokio::spawn,
|
||||||
|
);
|
||||||
|
let schema =
|
||||||
|
query_root::schema(database, orm_dataloader, *DEPTH_LIMIT, *COMPLEXITY_LIMIT).unwrap();
|
||||||
|
let app = Route::new().at(
|
||||||
|
&*ENDPOINT,
|
||||||
|
get(graphql_playground).post(GraphQL::new(schema)),
|
||||||
|
);
|
||||||
|
println!("Visit GraphQL Playground at http://{}", *URL);
|
||||||
|
Server::new(TcpListener::bind(&*URL))
|
||||||
|
.run(app)
|
||||||
|
.await
|
||||||
|
.expect("Fail to start web server");
|
||||||
|
}
|
34
issues/1599/graphql/src/query_root.rs
Normal file
34
issues/1599/graphql/src/query_root.rs
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
use crate::OrmDataloader;
|
||||||
|
use async_graphql::{dataloader::DataLoader, dynamic::*};
|
||||||
|
use entity::*;
|
||||||
|
use sea_orm::DatabaseConnection;
|
||||||
|
use seaography::{Builder, BuilderContext};
|
||||||
|
|
||||||
|
lazy_static::lazy_static! { static ref CONTEXT : BuilderContext = BuilderContext :: default () ; }
|
||||||
|
|
||||||
|
pub fn schema(
|
||||||
|
database: DatabaseConnection,
|
||||||
|
orm_dataloader: DataLoader<OrmDataloader>,
|
||||||
|
depth: Option<usize>,
|
||||||
|
complexity: Option<usize>,
|
||||||
|
) -> Result<Schema, SchemaError> {
|
||||||
|
let mut builder = Builder::new(&CONTEXT);
|
||||||
|
|
||||||
|
// Register entity including relations
|
||||||
|
seaography::register_entities!(builder, [cake]);
|
||||||
|
// Register entity only, no relations
|
||||||
|
seaography::register_entities_without_relation!(builder, [cake_filling, filling, fruit]);
|
||||||
|
|
||||||
|
let schema = builder.schema_builder();
|
||||||
|
let schema = if let Some(depth) = depth {
|
||||||
|
schema.limit_depth(depth)
|
||||||
|
} else {
|
||||||
|
schema
|
||||||
|
};
|
||||||
|
let schema = if let Some(complexity) = complexity {
|
||||||
|
schema.limit_complexity(complexity)
|
||||||
|
} else {
|
||||||
|
schema
|
||||||
|
};
|
||||||
|
schema.data(database).data(orm_dataloader).finish()
|
||||||
|
}
|
@ -321,6 +321,14 @@ pub enum GenerateSubcommands {
|
|||||||
help = r#"Add extra attributes to generated model struct, no need for `#[]` (comma separated), e.g. `--model-extra-attributes 'serde(rename_all = "camelCase")','ts(export)'`"#
|
help = r#"Add extra attributes to generated model struct, no need for `#[]` (comma separated), e.g. `--model-extra-attributes 'serde(rename_all = "camelCase")','ts(export)'`"#
|
||||||
)]
|
)]
|
||||||
model_extra_attributes: Vec<String>,
|
model_extra_attributes: Vec<String>,
|
||||||
|
|
||||||
|
#[clap(
|
||||||
|
action,
|
||||||
|
long,
|
||||||
|
default_value = "false",
|
||||||
|
long_help = "Generate helper Enumerations that are used by Seaography."
|
||||||
|
)]
|
||||||
|
seaography: bool,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,6 +31,7 @@ pub async fn run_generate_command(
|
|||||||
lib,
|
lib,
|
||||||
model_extra_derives,
|
model_extra_derives,
|
||||||
model_extra_attributes,
|
model_extra_attributes,
|
||||||
|
seaography,
|
||||||
} => {
|
} => {
|
||||||
if verbose {
|
if verbose {
|
||||||
let _ = tracing_subscriber::fmt()
|
let _ = tracing_subscriber::fmt()
|
||||||
@ -172,6 +173,7 @@ pub async fn run_generate_command(
|
|||||||
serde_skip_hidden_column,
|
serde_skip_hidden_column,
|
||||||
model_extra_derives,
|
model_extra_derives,
|
||||||
model_extra_attributes,
|
model_extra_attributes,
|
||||||
|
seaography,
|
||||||
);
|
);
|
||||||
let output = EntityTransformer::transform(table_stmts)?.generate(&writer_context);
|
let output = EntityTransformer::transform(table_stmts)?.generate(&writer_context);
|
||||||
|
|
||||||
|
@ -92,6 +92,26 @@ impl Entity {
|
|||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Used to generate the names for the `enum RelatedEntity` that is useful to the Seaography project
|
||||||
|
pub fn get_related_entity_enum_name(&self) -> Vec<Ident> {
|
||||||
|
// 1st step get conjunct relations data
|
||||||
|
let conjunct_related_names = self.get_conjunct_relations_to_upper_camel_case();
|
||||||
|
|
||||||
|
// 2nd step get reverse self relations data
|
||||||
|
let self_relations_reverse = self
|
||||||
|
.relations
|
||||||
|
.iter()
|
||||||
|
.filter(|rel| rel.self_referencing)
|
||||||
|
.map(|rel| format_ident!("{}Reverse", rel.get_enum_name()));
|
||||||
|
|
||||||
|
// 3rd step get normal relations data
|
||||||
|
self.get_relation_enum_name()
|
||||||
|
.into_iter()
|
||||||
|
.chain(self_relations_reverse)
|
||||||
|
.chain(conjunct_related_names.into_iter())
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_relation_defs(&self) -> Vec<TokenStream> {
|
pub fn get_relation_defs(&self) -> Vec<TokenStream> {
|
||||||
self.relations.iter().map(|rel| rel.get_def()).collect()
|
self.relations.iter().map(|rel| rel.get_def()).collect()
|
||||||
}
|
}
|
||||||
@ -100,6 +120,64 @@ impl Entity {
|
|||||||
self.relations.iter().map(|rel| rel.get_attrs()).collect()
|
self.relations.iter().map(|rel| rel.get_attrs()).collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Used to generate the attributes for the `enum RelatedEntity` that is useful to the Seaography project
|
||||||
|
pub fn get_related_entity_attrs(&self) -> Vec<TokenStream> {
|
||||||
|
// 1st step get conjunct relations data
|
||||||
|
let conjunct_related_attrs = self.conjunct_relations.iter().map(|conj| {
|
||||||
|
let entity = format!("super::{}::Entity", conj.get_to_snake_case());
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
#[sea_orm(
|
||||||
|
entity = #entity
|
||||||
|
)]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// helper function that generates attributes for `Relation` data
|
||||||
|
let produce_relation_attrs = |rel: &Relation, reverse: bool| {
|
||||||
|
let entity = match rel.get_module_name() {
|
||||||
|
Some(module_name) => format!("super::{}::Entity", module_name),
|
||||||
|
None => String::from("Entity"),
|
||||||
|
};
|
||||||
|
|
||||||
|
if rel.self_referencing || !rel.impl_related || rel.num_suffix > 0 {
|
||||||
|
let def = if reverse {
|
||||||
|
format!("Relation::{}.def().rev()", rel.get_enum_name())
|
||||||
|
} else {
|
||||||
|
format!("Relation::{}.def()", rel.get_enum_name())
|
||||||
|
};
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
#[sea_orm(
|
||||||
|
entity = #entity,
|
||||||
|
def = #def
|
||||||
|
)]
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
quote! {
|
||||||
|
#[sea_orm(
|
||||||
|
entity = #entity
|
||||||
|
)]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 2nd step get reverse self relations data
|
||||||
|
let self_relations_reverse_attrs = self
|
||||||
|
.relations
|
||||||
|
.iter()
|
||||||
|
.filter(|rel| rel.self_referencing)
|
||||||
|
.map(|rel| produce_relation_attrs(rel, true));
|
||||||
|
|
||||||
|
// 3rd step get normal relations data
|
||||||
|
self.relations
|
||||||
|
.iter()
|
||||||
|
.map(|rel| produce_relation_attrs(rel, false))
|
||||||
|
.chain(self_relations_reverse_attrs)
|
||||||
|
.chain(conjunct_related_attrs)
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_primary_key_auto_increment(&self) -> Ident {
|
pub fn get_primary_key_auto_increment(&self) -> Ident {
|
||||||
let auto_increment = self.columns.iter().any(|col| col.auto_increment);
|
let auto_increment = self.columns.iter().any(|col| col.auto_increment);
|
||||||
format_ident!("{}", auto_increment)
|
format_ident!("{}", auto_increment)
|
||||||
|
@ -385,6 +385,7 @@ mod tests {
|
|||||||
false,
|
false,
|
||||||
&Default::default(),
|
&Default::default(),
|
||||||
&Default::default(),
|
&Default::default(),
|
||||||
|
false,
|
||||||
)
|
)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.skip(1)
|
.skip(1)
|
||||||
|
@ -47,6 +47,7 @@ pub struct EntityWriterContext {
|
|||||||
pub(crate) serde_skip_deserializing_primary_key: bool,
|
pub(crate) serde_skip_deserializing_primary_key: bool,
|
||||||
pub(crate) model_extra_derives: TokenStream,
|
pub(crate) model_extra_derives: TokenStream,
|
||||||
pub(crate) model_extra_attributes: TokenStream,
|
pub(crate) model_extra_attributes: TokenStream,
|
||||||
|
pub(crate) seaography: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WithSerde {
|
impl WithSerde {
|
||||||
@ -142,6 +143,7 @@ impl EntityWriterContext {
|
|||||||
serde_skip_hidden_column: bool,
|
serde_skip_hidden_column: bool,
|
||||||
model_extra_derives: Vec<String>,
|
model_extra_derives: Vec<String>,
|
||||||
model_extra_attributes: Vec<String>,
|
model_extra_attributes: Vec<String>,
|
||||||
|
seaography: bool,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
expanded_format,
|
expanded_format,
|
||||||
@ -154,6 +156,7 @@ impl EntityWriterContext {
|
|||||||
serde_skip_hidden_column,
|
serde_skip_hidden_column,
|
||||||
model_extra_derives: bonus_derive(model_extra_derives),
|
model_extra_derives: bonus_derive(model_extra_derives),
|
||||||
model_extra_attributes: bonus_attributes(model_extra_attributes),
|
model_extra_attributes: bonus_attributes(model_extra_attributes),
|
||||||
|
seaography,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -209,6 +212,7 @@ impl EntityWriter {
|
|||||||
serde_skip_hidden_column,
|
serde_skip_hidden_column,
|
||||||
&context.model_extra_derives,
|
&context.model_extra_derives,
|
||||||
&context.model_extra_attributes,
|
&context.model_extra_attributes,
|
||||||
|
context.seaography,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
Self::gen_compact_code_blocks(
|
Self::gen_compact_code_blocks(
|
||||||
@ -220,6 +224,7 @@ impl EntityWriter {
|
|||||||
serde_skip_hidden_column,
|
serde_skip_hidden_column,
|
||||||
&context.model_extra_derives,
|
&context.model_extra_derives,
|
||||||
&context.model_extra_attributes,
|
&context.model_extra_attributes,
|
||||||
|
context.seaography,
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
Self::write(&mut lines, code_blocks);
|
Self::write(&mut lines, code_blocks);
|
||||||
@ -323,6 +328,7 @@ impl EntityWriter {
|
|||||||
serde_skip_hidden_column: bool,
|
serde_skip_hidden_column: bool,
|
||||||
model_extra_derives: &TokenStream,
|
model_extra_derives: &TokenStream,
|
||||||
model_extra_attributes: &TokenStream,
|
model_extra_attributes: &TokenStream,
|
||||||
|
seaography: bool,
|
||||||
) -> Vec<TokenStream> {
|
) -> Vec<TokenStream> {
|
||||||
let mut imports = Self::gen_import(with_serde);
|
let mut imports = Self::gen_import(with_serde);
|
||||||
imports.extend(Self::gen_import_active_enum(entity));
|
imports.extend(Self::gen_import_active_enum(entity));
|
||||||
@ -349,6 +355,9 @@ impl EntityWriter {
|
|||||||
code_blocks.extend(Self::gen_impl_related(entity));
|
code_blocks.extend(Self::gen_impl_related(entity));
|
||||||
code_blocks.extend(Self::gen_impl_conjunct_related(entity));
|
code_blocks.extend(Self::gen_impl_conjunct_related(entity));
|
||||||
code_blocks.extend([Self::gen_impl_active_model_behavior()]);
|
code_blocks.extend([Self::gen_impl_active_model_behavior()]);
|
||||||
|
if seaography {
|
||||||
|
code_blocks.extend([Self::gen_related_entity(entity)]);
|
||||||
|
}
|
||||||
code_blocks
|
code_blocks
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -362,6 +371,7 @@ impl EntityWriter {
|
|||||||
serde_skip_hidden_column: bool,
|
serde_skip_hidden_column: bool,
|
||||||
model_extra_derives: &TokenStream,
|
model_extra_derives: &TokenStream,
|
||||||
model_extra_attributes: &TokenStream,
|
model_extra_attributes: &TokenStream,
|
||||||
|
seaography: bool,
|
||||||
) -> Vec<TokenStream> {
|
) -> Vec<TokenStream> {
|
||||||
let mut imports = Self::gen_import(with_serde);
|
let mut imports = Self::gen_import(with_serde);
|
||||||
imports.extend(Self::gen_import_active_enum(entity));
|
imports.extend(Self::gen_import_active_enum(entity));
|
||||||
@ -382,6 +392,9 @@ impl EntityWriter {
|
|||||||
code_blocks.extend(Self::gen_impl_related(entity));
|
code_blocks.extend(Self::gen_impl_related(entity));
|
||||||
code_blocks.extend(Self::gen_impl_conjunct_related(entity));
|
code_blocks.extend(Self::gen_impl_conjunct_related(entity));
|
||||||
code_blocks.extend([Self::gen_impl_active_model_behavior()]);
|
code_blocks.extend([Self::gen_impl_active_model_behavior()]);
|
||||||
|
if seaography {
|
||||||
|
code_blocks.extend([Self::gen_related_entity(entity)]);
|
||||||
|
}
|
||||||
code_blocks
|
code_blocks
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -608,6 +621,22 @@ impl EntityWriter {
|
|||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Used to generate `enum RelatedEntity` that is useful to the Seaography project
|
||||||
|
pub fn gen_related_entity(entity: &Entity) -> TokenStream {
|
||||||
|
let related_enum_name = entity.get_related_entity_enum_name();
|
||||||
|
let related_attrs = entity.get_related_entity_attrs();
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelatedEntity)]
|
||||||
|
pub enum RelatedEntity {
|
||||||
|
#(
|
||||||
|
#related_attrs
|
||||||
|
#related_enum_name
|
||||||
|
),*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn gen_impl_conjunct_related(entity: &Entity) -> Vec<TokenStream> {
|
pub fn gen_impl_conjunct_related(entity: &Entity) -> Vec<TokenStream> {
|
||||||
let table_name_camel_case = entity.get_table_name_camel_case_ident();
|
let table_name_camel_case = entity.get_table_name_camel_case_ident();
|
||||||
let via_snake_case = entity.get_conjunct_relations_via_snake_case();
|
let via_snake_case = entity.get_conjunct_relations_via_snake_case();
|
||||||
@ -1371,6 +1400,7 @@ mod tests {
|
|||||||
false,
|
false,
|
||||||
&TokenStream::new(),
|
&TokenStream::new(),
|
||||||
&TokenStream::new(),
|
&TokenStream::new(),
|
||||||
|
false
|
||||||
)
|
)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.skip(1)
|
.skip(1)
|
||||||
@ -1391,6 +1421,7 @@ mod tests {
|
|||||||
false,
|
false,
|
||||||
&TokenStream::new(),
|
&TokenStream::new(),
|
||||||
&TokenStream::new(),
|
&TokenStream::new(),
|
||||||
|
false,
|
||||||
)
|
)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.skip(1)
|
.skip(1)
|
||||||
@ -1411,6 +1442,7 @@ mod tests {
|
|||||||
false,
|
false,
|
||||||
&TokenStream::new(),
|
&TokenStream::new(),
|
||||||
&TokenStream::new(),
|
&TokenStream::new(),
|
||||||
|
false,
|
||||||
)
|
)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.skip(1)
|
.skip(1)
|
||||||
@ -1467,6 +1499,7 @@ mod tests {
|
|||||||
false,
|
false,
|
||||||
&TokenStream::new(),
|
&TokenStream::new(),
|
||||||
&TokenStream::new(),
|
&TokenStream::new(),
|
||||||
|
false,
|
||||||
)
|
)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.skip(1)
|
.skip(1)
|
||||||
@ -1487,6 +1520,7 @@ mod tests {
|
|||||||
false,
|
false,
|
||||||
&TokenStream::new(),
|
&TokenStream::new(),
|
||||||
&TokenStream::new(),
|
&TokenStream::new(),
|
||||||
|
false,
|
||||||
)
|
)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.skip(1)
|
.skip(1)
|
||||||
@ -1507,6 +1541,7 @@ mod tests {
|
|||||||
false,
|
false,
|
||||||
&TokenStream::new(),
|
&TokenStream::new(),
|
||||||
&TokenStream::new(),
|
&TokenStream::new(),
|
||||||
|
false,
|
||||||
)
|
)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.skip(1)
|
.skip(1)
|
||||||
@ -1539,6 +1574,7 @@ mod tests {
|
|||||||
false,
|
false,
|
||||||
&TokenStream::new(),
|
&TokenStream::new(),
|
||||||
&TokenStream::new(),
|
&TokenStream::new(),
|
||||||
|
false,
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@ -1554,6 +1590,7 @@ mod tests {
|
|||||||
false,
|
false,
|
||||||
&TokenStream::new(),
|
&TokenStream::new(),
|
||||||
&TokenStream::new(),
|
&TokenStream::new(),
|
||||||
|
false,
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@ -1569,6 +1606,7 @@ mod tests {
|
|||||||
false,
|
false,
|
||||||
&TokenStream::new(),
|
&TokenStream::new(),
|
||||||
&TokenStream::new(),
|
&TokenStream::new(),
|
||||||
|
false,
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@ -1582,6 +1620,7 @@ mod tests {
|
|||||||
false,
|
false,
|
||||||
&TokenStream::new(),
|
&TokenStream::new(),
|
||||||
&TokenStream::new(),
|
&TokenStream::new(),
|
||||||
|
false,
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -1597,6 +1636,7 @@ mod tests {
|
|||||||
false,
|
false,
|
||||||
&TokenStream::new(),
|
&TokenStream::new(),
|
||||||
&TokenStream::new(),
|
&TokenStream::new(),
|
||||||
|
false,
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@ -1612,6 +1652,7 @@ mod tests {
|
|||||||
false,
|
false,
|
||||||
&TokenStream::new(),
|
&TokenStream::new(),
|
||||||
&TokenStream::new(),
|
&TokenStream::new(),
|
||||||
|
false,
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@ -1627,6 +1668,7 @@ mod tests {
|
|||||||
false,
|
false,
|
||||||
&TokenStream::new(),
|
&TokenStream::new(),
|
||||||
&TokenStream::new(),
|
&TokenStream::new(),
|
||||||
|
false,
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@ -1640,6 +1682,104 @@ mod tests {
|
|||||||
false,
|
false,
|
||||||
&TokenStream::new(),
|
&TokenStream::new(),
|
||||||
&TokenStream::new(),
|
&TokenStream::new(),
|
||||||
|
false,
|
||||||
|
))
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_gen_with_seaography() -> io::Result<()> {
|
||||||
|
let cake_entity = Entity {
|
||||||
|
table_name: "cake".to_owned(),
|
||||||
|
columns: vec![
|
||||||
|
Column {
|
||||||
|
name: "id".to_owned(),
|
||||||
|
col_type: ColumnType::Integer,
|
||||||
|
auto_increment: true,
|
||||||
|
not_null: true,
|
||||||
|
unique: false,
|
||||||
|
},
|
||||||
|
Column {
|
||||||
|
name: "name".to_owned(),
|
||||||
|
col_type: ColumnType::Text,
|
||||||
|
auto_increment: false,
|
||||||
|
not_null: false,
|
||||||
|
unique: false,
|
||||||
|
},
|
||||||
|
Column {
|
||||||
|
name: "base_id".to_owned(),
|
||||||
|
col_type: ColumnType::Integer,
|
||||||
|
auto_increment: false,
|
||||||
|
not_null: false,
|
||||||
|
unique: false,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
relations: vec![
|
||||||
|
Relation {
|
||||||
|
ref_table: "fruit".to_owned(),
|
||||||
|
columns: vec![],
|
||||||
|
ref_columns: vec![],
|
||||||
|
rel_type: RelationType::HasMany,
|
||||||
|
on_delete: None,
|
||||||
|
on_update: None,
|
||||||
|
self_referencing: false,
|
||||||
|
num_suffix: 0,
|
||||||
|
impl_related: true,
|
||||||
|
},
|
||||||
|
Relation {
|
||||||
|
ref_table: "cake".to_owned(),
|
||||||
|
columns: vec![],
|
||||||
|
ref_columns: vec![],
|
||||||
|
rel_type: RelationType::HasOne,
|
||||||
|
on_delete: None,
|
||||||
|
on_update: None,
|
||||||
|
self_referencing: true,
|
||||||
|
num_suffix: 0,
|
||||||
|
impl_related: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
conjunct_relations: vec![ConjunctRelation {
|
||||||
|
via: "cake_filling".to_owned(),
|
||||||
|
to: "filling".to_owned(),
|
||||||
|
}],
|
||||||
|
primary_keys: vec![PrimaryKey {
|
||||||
|
name: "id".to_owned(),
|
||||||
|
}],
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_eq!(cake_entity.get_table_name_snake_case(), "cake");
|
||||||
|
|
||||||
|
// Compact code blocks
|
||||||
|
assert_eq!(
|
||||||
|
comparable_file_string(include_str!("../../tests/with_seaography/cake.rs"))?,
|
||||||
|
generated_to_string(EntityWriter::gen_compact_code_blocks(
|
||||||
|
&cake_entity,
|
||||||
|
&WithSerde::None,
|
||||||
|
&DateTimeCrate::Chrono,
|
||||||
|
&None,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
&TokenStream::new(),
|
||||||
|
&TokenStream::new(),
|
||||||
|
true,
|
||||||
|
))
|
||||||
|
);
|
||||||
|
|
||||||
|
// Expanded code blocks
|
||||||
|
assert_eq!(
|
||||||
|
comparable_file_string(include_str!("../../tests/with_seaography/cake_expanded.rs"))?,
|
||||||
|
generated_to_string(EntityWriter::gen_expanded_code_blocks(
|
||||||
|
&cake_entity,
|
||||||
|
&WithSerde::None,
|
||||||
|
&DateTimeCrate::Chrono,
|
||||||
|
&None,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
&TokenStream::new(),
|
||||||
|
&TokenStream::new(),
|
||||||
|
true,
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -1666,6 +1806,7 @@ mod tests {
|
|||||||
false,
|
false,
|
||||||
&TokenStream::new(),
|
&TokenStream::new(),
|
||||||
&TokenStream::new(),
|
&TokenStream::new(),
|
||||||
|
false,
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@ -1679,6 +1820,7 @@ mod tests {
|
|||||||
false,
|
false,
|
||||||
&bonus_derive(["ts_rs::TS"]),
|
&bonus_derive(["ts_rs::TS"]),
|
||||||
&TokenStream::new(),
|
&TokenStream::new(),
|
||||||
|
false,
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@ -1694,6 +1836,7 @@ mod tests {
|
|||||||
false,
|
false,
|
||||||
&bonus_derive(["ts_rs::TS", "utoipa::ToSchema"]),
|
&bonus_derive(["ts_rs::TS", "utoipa::ToSchema"]),
|
||||||
&TokenStream::new(),
|
&TokenStream::new(),
|
||||||
|
false,
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -1711,6 +1854,7 @@ mod tests {
|
|||||||
false,
|
false,
|
||||||
&TokenStream::new(),
|
&TokenStream::new(),
|
||||||
&TokenStream::new(),
|
&TokenStream::new(),
|
||||||
|
false,
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@ -1726,6 +1870,7 @@ mod tests {
|
|||||||
false,
|
false,
|
||||||
&bonus_derive(["ts_rs::TS"]),
|
&bonus_derive(["ts_rs::TS"]),
|
||||||
&TokenStream::new(),
|
&TokenStream::new(),
|
||||||
|
false,
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@ -1741,6 +1886,7 @@ mod tests {
|
|||||||
false,
|
false,
|
||||||
&bonus_derive(["ts_rs::TS", "utoipa::ToSchema"]),
|
&bonus_derive(["ts_rs::TS", "utoipa::ToSchema"]),
|
||||||
&TokenStream::new(),
|
&TokenStream::new(),
|
||||||
|
false,
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -1785,6 +1931,7 @@ mod tests {
|
|||||||
bool,
|
bool,
|
||||||
&TokenStream,
|
&TokenStream,
|
||||||
&TokenStream,
|
&TokenStream,
|
||||||
|
bool,
|
||||||
) -> Vec<TokenStream>,
|
) -> Vec<TokenStream>,
|
||||||
>,
|
>,
|
||||||
) -> io::Result<()> {
|
) -> io::Result<()> {
|
||||||
@ -1815,6 +1962,7 @@ mod tests {
|
|||||||
serde_skip_hidden_column,
|
serde_skip_hidden_column,
|
||||||
&TokenStream::new(),
|
&TokenStream::new(),
|
||||||
&TokenStream::new(),
|
&TokenStream::new(),
|
||||||
|
false,
|
||||||
)
|
)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.fold(TokenStream::new(), |mut acc, tok| {
|
.fold(TokenStream::new(), |mut acc, tok| {
|
||||||
@ -1846,6 +1994,7 @@ mod tests {
|
|||||||
false,
|
false,
|
||||||
&TokenStream::new(),
|
&TokenStream::new(),
|
||||||
&TokenStream::new(),
|
&TokenStream::new(),
|
||||||
|
false,
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@ -1861,6 +2010,7 @@ mod tests {
|
|||||||
false,
|
false,
|
||||||
&TokenStream::new(),
|
&TokenStream::new(),
|
||||||
&bonus_attributes([r#"serde(rename_all = "camelCase")"#]),
|
&bonus_attributes([r#"serde(rename_all = "camelCase")"#]),
|
||||||
|
false,
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@ -1876,6 +2026,7 @@ mod tests {
|
|||||||
false,
|
false,
|
||||||
&TokenStream::new(),
|
&TokenStream::new(),
|
||||||
&bonus_attributes([r#"serde(rename_all = "camelCase")"#, "ts(export)"]),
|
&bonus_attributes([r#"serde(rename_all = "camelCase")"#, "ts(export)"]),
|
||||||
|
false,
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -1893,6 +2044,7 @@ mod tests {
|
|||||||
false,
|
false,
|
||||||
&TokenStream::new(),
|
&TokenStream::new(),
|
||||||
&TokenStream::new(),
|
&TokenStream::new(),
|
||||||
|
false,
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@ -1908,6 +2060,7 @@ mod tests {
|
|||||||
false,
|
false,
|
||||||
&TokenStream::new(),
|
&TokenStream::new(),
|
||||||
&bonus_attributes([r#"serde(rename_all = "camelCase")"#]),
|
&bonus_attributes([r#"serde(rename_all = "camelCase")"#]),
|
||||||
|
false,
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@ -1923,6 +2076,7 @@ mod tests {
|
|||||||
false,
|
false,
|
||||||
&TokenStream::new(),
|
&TokenStream::new(),
|
||||||
&bonus_attributes([r#"serde(rename_all = "camelCase")"#, "ts(export)"]),
|
&bonus_attributes([r#"serde(rename_all = "camelCase")"#, "ts(export)"]),
|
||||||
|
false,
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -2015,6 +2169,7 @@ mod tests {
|
|||||||
false,
|
false,
|
||||||
&TokenStream::new(),
|
&TokenStream::new(),
|
||||||
&TokenStream::new(),
|
&TokenStream::new(),
|
||||||
|
false,
|
||||||
)
|
)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.skip(1)
|
.skip(1)
|
||||||
@ -2035,6 +2190,7 @@ mod tests {
|
|||||||
false,
|
false,
|
||||||
&TokenStream::new(),
|
&TokenStream::new(),
|
||||||
&TokenStream::new(),
|
&TokenStream::new(),
|
||||||
|
false,
|
||||||
)
|
)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.skip(1)
|
.skip(1)
|
||||||
@ -2055,6 +2211,7 @@ mod tests {
|
|||||||
false,
|
false,
|
||||||
&TokenStream::new(),
|
&TokenStream::new(),
|
||||||
&TokenStream::new(),
|
&TokenStream::new(),
|
||||||
|
false,
|
||||||
)
|
)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.skip(1)
|
.skip(1)
|
||||||
|
50
sea-orm-codegen/tests/with_seaography/cake.rs
Normal file
50
sea-orm-codegen/tests/with_seaography/cake.rs
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
//! SeaORM Entity. Generated by sea-orm-codegen 0.1.0
|
||||||
|
|
||||||
|
use sea_orm::entity::prelude:: * ;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
|
||||||
|
#[sea_orm(table_name = "cake")]
|
||||||
|
pub struct Model {
|
||||||
|
#[sea_orm(primary_key)]
|
||||||
|
pub id: i32,
|
||||||
|
#[sea_orm(column_type = "Text", nullable)]
|
||||||
|
pub name: Option<String> ,
|
||||||
|
pub base_id: Option<i32> ,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||||
|
pub enum Relation {
|
||||||
|
#[sea_orm(has_many = "super::fruit::Entity")]
|
||||||
|
Fruit,
|
||||||
|
#[sea_orm(has_one = "Entity")]
|
||||||
|
SelfRef ,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Related<super::fruit::Entity> for Entity {
|
||||||
|
fn to() -> RelationDef {
|
||||||
|
Relation::Fruit.def()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Related<super::filling::Entity> for Entity {
|
||||||
|
fn to() -> RelationDef {
|
||||||
|
super::cake_filling::Relation::Filling.def()
|
||||||
|
}
|
||||||
|
fn via() -> Option<RelationDef> {
|
||||||
|
Some(super::cake_filling::Relation::Cake.def().rev())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ActiveModelBehavior for ActiveModel {}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelatedEntity)]
|
||||||
|
pub enum RelatedEntity {
|
||||||
|
#[sea_orm(entity = "super::fruit::Entity")]
|
||||||
|
Fruit,
|
||||||
|
#[sea_orm(entity = "Entity", def = "Relation::SelfRef.def()")]
|
||||||
|
SelfRef,
|
||||||
|
#[sea_orm(entity = "Entity", def = "Relation::SelfRef.def().rev()")]
|
||||||
|
SelfRefReverse,
|
||||||
|
#[sea_orm(entity = "super::filling::Entity")]
|
||||||
|
Filling
|
||||||
|
}
|
94
sea-orm-codegen/tests/with_seaography/cake_expanded.rs
Normal file
94
sea-orm-codegen/tests/with_seaography/cake_expanded.rs
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
//! SeaORM Entity. Generated by sea-orm-codegen 0.1.0
|
||||||
|
|
||||||
|
use sea_orm::entity::prelude:: * ;
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Default, Debug, DeriveEntity)]
|
||||||
|
pub struct Entity;
|
||||||
|
|
||||||
|
impl EntityName for Entity {
|
||||||
|
fn table_name(&self) -> &str {
|
||||||
|
"cake"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, DeriveModel, DeriveActiveModel, Eq)]
|
||||||
|
pub struct Model {
|
||||||
|
pub id: i32,
|
||||||
|
pub name: Option<String> ,
|
||||||
|
pub base_id: Option<i32> ,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)]
|
||||||
|
pub enum Column {
|
||||||
|
Id,
|
||||||
|
Name,
|
||||||
|
BaseId,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, EnumIter, DerivePrimaryKey)]
|
||||||
|
pub enum PrimaryKey {
|
||||||
|
Id,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PrimaryKeyTrait for PrimaryKey {
|
||||||
|
type ValueType = i32;
|
||||||
|
|
||||||
|
fn auto_increment() -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, EnumIter)]
|
||||||
|
pub enum Relation {
|
||||||
|
Fruit,
|
||||||
|
SelfRef ,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ColumnTrait for Column {
|
||||||
|
type EntityName = Entity;
|
||||||
|
fn def(&self) -> ColumnDef {
|
||||||
|
match self {
|
||||||
|
Self::Id => ColumnType::Integer.def(),
|
||||||
|
Self::Name => ColumnType::Text.def().null(),
|
||||||
|
Self::BaseId => ColumnType::Integer.def().null(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RelationTrait for Relation {
|
||||||
|
fn def(&self) -> RelationDef {
|
||||||
|
match self {
|
||||||
|
Self::Fruit => Entity::has_many(super::fruit::Entity).into(),
|
||||||
|
Self::SelfRef => Entity::has_one(Entity).into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Related<super::fruit::Entity> for Entity {
|
||||||
|
fn to() -> RelationDef {
|
||||||
|
Relation::Fruit.def()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Related<super::filling::Entity> for Entity {
|
||||||
|
fn to() -> RelationDef {
|
||||||
|
super::cake_filling::Relation::Filling.def()
|
||||||
|
}
|
||||||
|
fn via() -> Option<RelationDef> {
|
||||||
|
Some(super::cake_filling::Relation::Cake.def().rev())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ActiveModelBehavior for ActiveModel {}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelatedEntity)]
|
||||||
|
pub enum RelatedEntity {
|
||||||
|
#[sea_orm(entity = "super::fruit::Entity")]
|
||||||
|
Fruit,
|
||||||
|
#[sea_orm(entity = "Entity", def = "Relation::SelfRef.def()")]
|
||||||
|
SelfRef,
|
||||||
|
#[sea_orm(entity = "Entity", def = "Relation::SelfRef.def().rev()")]
|
||||||
|
SelfRefReverse,
|
||||||
|
#[sea_orm(entity = "super::filling::Entity")]
|
||||||
|
Filling
|
||||||
|
}
|
@ -34,3 +34,4 @@ default = ["derive"]
|
|||||||
postgres-array = []
|
postgres-array = []
|
||||||
derive = ["bae"]
|
derive = ["bae"]
|
||||||
strum = []
|
strum = []
|
||||||
|
seaography = []
|
||||||
|
@ -33,3 +33,29 @@ pub mod field_attr {
|
|||||||
pub condition_type: Option<syn::Lit>,
|
pub condition_type: Option<syn::Lit>,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub mod related_attr {
|
||||||
|
use bae::FromAttributes;
|
||||||
|
|
||||||
|
/// Operations for RelatedEntity enumeration
|
||||||
|
#[derive(Default, FromAttributes)]
|
||||||
|
pub struct SeaOrm {
|
||||||
|
///
|
||||||
|
/// Allows to modify target entity
|
||||||
|
///
|
||||||
|
/// Required on enumeration variants
|
||||||
|
///
|
||||||
|
/// If used on enumeration attributes
|
||||||
|
/// it allows to specify different
|
||||||
|
/// Entity ident
|
||||||
|
pub entity: Option<syn::Lit>,
|
||||||
|
///
|
||||||
|
/// Allows to specify RelationDef
|
||||||
|
///
|
||||||
|
/// Optional
|
||||||
|
///
|
||||||
|
/// If not supplied the generated code
|
||||||
|
/// will utilize `impl Related` trait
|
||||||
|
pub def: Option<syn::Lit>,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -11,6 +11,7 @@ mod migration;
|
|||||||
mod model;
|
mod model;
|
||||||
mod partial_model;
|
mod partial_model;
|
||||||
mod primary_key;
|
mod primary_key;
|
||||||
|
mod related_entity;
|
||||||
mod relation;
|
mod relation;
|
||||||
mod try_getable_from_json;
|
mod try_getable_from_json;
|
||||||
mod util;
|
mod util;
|
||||||
@ -27,5 +28,6 @@ pub use migration::*;
|
|||||||
pub use model::*;
|
pub use model::*;
|
||||||
pub use partial_model::*;
|
pub use partial_model::*;
|
||||||
pub use primary_key::*;
|
pub use primary_key::*;
|
||||||
|
pub use related_entity::*;
|
||||||
pub use relation::*;
|
pub use relation::*;
|
||||||
pub use try_getable_from_json::*;
|
pub use try_getable_from_json::*;
|
||||||
|
125
sea-orm-macros/src/derives/related_entity.rs
Normal file
125
sea-orm-macros/src/derives/related_entity.rs
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
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),
|
||||||
|
}
|
||||||
|
}
|
@ -639,6 +639,46 @@ pub fn derive_relation(input: TokenStream) -> TokenStream {
|
|||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The DeriveRelatedEntity derive macro will implement seaography::RelationBuilder for RelatedEntity enumeration.
|
||||||
|
///
|
||||||
|
/// ### Usage
|
||||||
|
///
|
||||||
|
/// ```ignore
|
||||||
|
/// use sea_orm::entity::prelude::*;
|
||||||
|
///
|
||||||
|
/// // ...
|
||||||
|
/// // Model, Relation enum, etc.
|
||||||
|
/// // ...
|
||||||
|
///
|
||||||
|
/// #[derive(Copy, Clone, Debug, EnumIter, DeriveRelatedEntity)]
|
||||||
|
/// pub enum RelatedEntity {
|
||||||
|
/// #[sea_orm(entity = "super::address::Entity")]
|
||||||
|
/// Address,
|
||||||
|
/// #[sea_orm(entity = "super::payment::Entity")]
|
||||||
|
/// Payment,
|
||||||
|
/// #[sea_orm(entity = "super::rental::Entity")]
|
||||||
|
/// Rental,
|
||||||
|
/// #[sea_orm(entity = "Entity", def = "Relation::SelfRef.def()")]
|
||||||
|
/// SelfRef,
|
||||||
|
/// #[sea_orm(entity = "super::store::Entity")]
|
||||||
|
/// Store,
|
||||||
|
/// #[sea_orm(entity = "Entity", def = "Relation::SelfRef.def().rev()")]
|
||||||
|
/// SelfRefRev,
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
#[cfg(feature = "derive")]
|
||||||
|
#[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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// The DeriveMigrationName derive macro will implement `sea_orm_migration::MigrationName` for a migration.
|
/// The DeriveMigrationName derive macro will implement `sea_orm_migration::MigrationName` for a migration.
|
||||||
///
|
///
|
||||||
/// ### Usage
|
/// ### Usage
|
||||||
|
@ -12,7 +12,7 @@ pub use crate::{
|
|||||||
pub use crate::{
|
pub use crate::{
|
||||||
DeriveActiveEnum, DeriveActiveModel, DeriveActiveModelBehavior, DeriveColumn,
|
DeriveActiveEnum, DeriveActiveModel, DeriveActiveModelBehavior, DeriveColumn,
|
||||||
DeriveCustomColumn, DeriveEntity, DeriveEntityModel, DeriveIntoActiveModel, DeriveModel,
|
DeriveCustomColumn, DeriveEntity, DeriveEntityModel, DeriveIntoActiveModel, DeriveModel,
|
||||||
DerivePrimaryKey, DeriveRelation, FromJsonQueryResult,
|
DerivePrimaryKey, DeriveRelatedEntity, DeriveRelation, FromJsonQueryResult,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub use async_trait;
|
pub use async_trait;
|
||||||
|
@ -350,8 +350,8 @@ pub use schema::*;
|
|||||||
pub use sea_orm_macros::{
|
pub use sea_orm_macros::{
|
||||||
DeriveActiveEnum, DeriveActiveModel, DeriveActiveModelBehavior, DeriveColumn,
|
DeriveActiveEnum, DeriveActiveModel, DeriveActiveModelBehavior, DeriveColumn,
|
||||||
DeriveCustomColumn, DeriveEntity, DeriveEntityModel, DeriveIntoActiveModel,
|
DeriveCustomColumn, DeriveEntity, DeriveEntityModel, DeriveIntoActiveModel,
|
||||||
DeriveMigrationName, DeriveModel, DerivePartialModel, DerivePrimaryKey, DeriveRelation,
|
DeriveMigrationName, DeriveModel, DerivePartialModel, DerivePrimaryKey, DeriveRelatedEntity,
|
||||||
FromJsonQueryResult, FromQueryResult,
|
DeriveRelation, FromJsonQueryResult, FromQueryResult,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub use sea_query;
|
pub use sea_query;
|
||||||
|
64
src/tests_cfg/cake_seaography.rs
Normal file
64
src/tests_cfg/cake_seaography.rs
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
use crate as sea_orm;
|
||||||
|
use crate::entity::prelude::*;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)]
|
||||||
|
#[sea_orm(table_name = "cake")]
|
||||||
|
pub struct Model {
|
||||||
|
#[sea_orm(primary_key)]
|
||||||
|
pub id: i32,
|
||||||
|
#[sea_orm(column_name = "name", enum_name = "Name")]
|
||||||
|
pub name: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||||
|
pub enum Relation {
|
||||||
|
#[sea_orm(has_many = "super::fruit::Entity")]
|
||||||
|
Fruit,
|
||||||
|
#[sea_orm(
|
||||||
|
has_many = "super::fruit::Entity",
|
||||||
|
on_condition = r#"super::fruit::Column::Name.like("%tropical%")"#
|
||||||
|
)]
|
||||||
|
TropicalFruit,
|
||||||
|
#[sea_orm(
|
||||||
|
has_many = "super::fruit::Entity",
|
||||||
|
condition_type = "any",
|
||||||
|
on_condition = r#"super::fruit::Column::Name.like("%tropical%")"#
|
||||||
|
)]
|
||||||
|
OrTropicalFruit,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Related<super::fruit::Entity> for Entity {
|
||||||
|
fn to() -> RelationDef {
|
||||||
|
Relation::Fruit.def()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Related<super::filling::Entity> for Entity {
|
||||||
|
fn to() -> RelationDef {
|
||||||
|
super::cake_filling::Relation::Filling.def()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn via() -> Option<RelationDef> {
|
||||||
|
Some(super::cake_filling::Relation::Cake.def().rev())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelatedEntity)]
|
||||||
|
pub enum RelatedEntity {
|
||||||
|
#[sea_orm(entity = "super::fruit::Entity")]
|
||||||
|
Fruit,
|
||||||
|
#[sea_orm(entity = "super::filling::Entity")]
|
||||||
|
Filling,
|
||||||
|
#[sea_orm(
|
||||||
|
entity = "super::fruit::Entity",
|
||||||
|
def = "Relation::TropicalFruit.def()"
|
||||||
|
)]
|
||||||
|
TropicalFruit,
|
||||||
|
#[sea_orm(
|
||||||
|
entity = "super::fruit::Entity",
|
||||||
|
def = "Relation::OrTropicalFruit.def()"
|
||||||
|
)]
|
||||||
|
OrTropicalFruit,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ActiveModelBehavior for ActiveModel {}
|
Loading…
x
Reference in New Issue
Block a user