-
-
Notifications
You must be signed in to change notification settings - Fork 523
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Compact Entity format #105
Comments
I haven't thought of this before, but I think I can agree with you somewhat. For example, #[derive(Copy, Clone, Default, Debug, DeriveEntity)]
pub struct Entity;
impl EntityName for Entity {
fn schema_name(&self) -> Option<&str> {
Some("public")
}
fn table_name(&self) -> &str {
"users"
}
}
#[derive(Clone, Debug, PartialEq, DeriveModel, DeriveActiveModel)]
pub struct Model {
pub id: Uuid,
pub created_at: DateTimeWithTimeZone,
pub updated_at: DateTimeWithTimeZone,
pub username: String,
pub password: String,
pub email: Option<String>,
}
#[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)]
pub enum Column {
Id,
CreatedAt,
UpdatedAt,
Username,
Password,
Email,
}
#[derive(Copy, std::clone::Clone, std::fmt::Debug, EnumIter, DerivePrimaryKey)]
pub enum PrimaryKey {
Id,
}
impl PrimaryKeyTrait for PrimaryKey {
fn auto_increment() -> bool {
false
}
}
#[derive(Copy, Clone, Debug, EnumIter)]
pub enum Relation {}
impl ColumnTrait for Column {
type EntityName = Entity;
fn def(&self) -> ColumnDef {
match self {
Self::Id => ColumnType::Uuid.def(),
Self::CreatedAt => ColumnType::Timestamp.def(),
Self::UpdatedAt => ColumnType::Timestamp.def(),
Self::Username => ColumnType::String(None).def(),
Self::Password => ColumnType::String(None).def(),
Self::Email => ColumnType::String(None).def().null(),
}
}
} Into: #[derive(Clone, Debug, PartialEq, SeaORM)]
#[schema_name = "public"]
#[table_name = "coupons"]
pub struct Model {
#[primary_key]
#[auto_increment = false]
pub id: Uuid,
pub created_at: DateTimeWithTimeZone,
pub updated_at: DateTimeWithTimeZone,
pub username: String,
pub password: String,
pub email: Option<String>,
} I think this wouldn't be too hard as a lot of information could be inferred from the struct.
Obviously with this comes a lack of customizability. But this could be solved by allowing manual implementations where needed perhaps? |
Thank you for your interest. I will first talk about the original ideas and choices, and may add more thoughts on later post. The first thing is, I do not want to invent any DSL or a custom syntax or semantics. It will definitely be much less verbose, but it will be hard to understand and reason about, and people don't want to learn a new micro-language. Sooner or later, someone will have a need to customise the behaviour and hack the thing, and it will be much easier to do so in the plain Rust language. (Just a small use case, how can I dynamically remap the table name based on environment variables?) If there are tricks to reduce the verbosity of the Entity file without countering the above, I'll definitely adopt. A lesser motivation would be code completion. List out the essential symbols and traits will be helpful. But after all it is about how much information we want to present on plain sight. Too much it is hard to digest, too little it will be hard to see-through. |
Yes, I understand your point of view, and it was exactly the reply I thought for the question "why didn't they already made this?". For example, serde has a derive macro for Serialize and Deserialize, but anyone can manually derive the traits for non-standard situations, and there are lots of docs about it. I would prefer a serde-like approach, where the default is easily reached with a macro, and non-default-fitting situations can be covered in plain Rust. |
I also think that having to use both CamelCase and snake_case when referring to attributes is slightly awkward. Is it better or worse if we break the Rust convention and use snake_case enums? |
I don't think so honestly, I think keeping the enum as CamelCase... and just renaming it through the derive macro. And the derive macro would just generate an implementation of |
Serde is a brilliant library and there is definitely a lot to learn and borrow from. Yes I would prefer to have a more compact form, as long as it is compatible with the longer form. And that the derive macro should not do too much magic other than providing syntax sugars. |
Also, it'd be better if we settle on a 'base format' first, and versions afterwards to be backward compatible. |
@acidic9 the format you propose already looks good. Perhaps we just need to think about the relations. |
I've put together a minimal impl, have a look https://github.com/nappa85/sea-orm-macro/ |
Wow thank you for the quick update. Looks great so far. I was just thinking how to name it (lol) perhaps, |
I've named it AutoColumn because it's main purpose is to generate Column from Model, but it generates optionally also Entity and PrimaryKey, so maybe a more generic AutoDef? |
Yeah |
I feel like |
Well, |
@nappa85 we are releasing |
Yes, if you let me know what you want me to edit, I'll do it ASAP Here we discussed only the derive macro name |
@tyt2y3 I spent most of my day yesterday and today creating a PR for this too. It's at a working stage, just need the columntrait to be generated too. I'll create the PR in a few hours! |
Thank you. After 0.2.0 we will make our focus on this matter, with 0.3.0 on the horizon of early Oct. |
Great, good to know. Thank you for the suggestion and everything up to now. |
Thank you so much for the big PR. I think yours are very promising! @nappa85 What's your view on this? @acidic9 Can you show some more examples on the proposed format and annotation syntax (even if they are not implemented yet)? |
I'm not telling my version in better than @acidic9 one, but I'm already using mine in my projects, it's almost complete, at least for my use cases, and I made almost no changes since I created it 8 days ago |
@nappa85 I didn't get a chance to get an example working with your code, I'm not sure how exactly to use it. Do you think you could briefly explain what the API looks like for your version? |
you can find a working example in the tests folder of my repo https://github.com/nappa85/sea-orm-macro/blob/master/tests/it_works.rs I can write some docs once we decided what has to be changed |
Thank you for everyone's input here. We will think a bit and perhaps draft a design document. |
I've wrote a little readme to better explain the proc macro: https://github.com/nappa85/sea-orm-macro/blob/master/README.md |
Yes, I think we should use the |
Perfect, so the macro code is already aligned |
Thank you everyone for everything! |
I am done for today. Feel free to peek at the current status. |
I am thinking, whether DeriveEntityModel should automatically derive DeriveModel and DeriveActiveModel, to make it less verbose. |
Could be a good idea |
@billy1624 can you help a bit on this? |
I've made some tests about it, maybe I'm wrong, but it seems that a derive macro can't edit the original struct, to do so you need a proc-macro. I've tried with this code, inside DeriveEntityModel macro: attrs.push(Attribute {
pound_token: Pound(Span::call_site()),
style: AttrStyle::Outer,
bracket_token: Bracket(Span::call_site()),
path: Path {
leading_colon: None,
segments: {
let mut temp = Punctuated::new();
temp.push(PathSegment {
ident: Ident::new("derive", Span::call_site()),
arguments: PathArguments::None,
});
temp
},
},
tokens: quote! { DeriveModel, DeriveActiveModel },
}); |
Rather than modifying the derive attribute of the struct itself, it's probably better to just reuse the code of For example, if they both use the struct approach, then you could just call expand them directly. struct DeriveEntityModel {
derive_model: DeriveModel,
derive_active_model: DeriveActiveModel,
...
}
impl DeriveEntityModel {
fn new(input: syn::DeriveInput) -> syn::Result<Self, Error> {
...
Ok(DeriveEntityModel {
derive_model: DeriveModel::new(input)?,
derive_active_model: DeriveActiveModel::new(input)?,
...
})
}
fn expand(&self) -> syn::Result<TokenStream> {
let expanded_derive_model = self.derive_model.expand()?;
let expanded_derive_active_model = self.derive_active_model.expand()?;
...
Ok(TokenStream::from_iter([
expanded_derive_model,
expanded_derive_active_model,
...
]))
}
} I hope that makes sense. |
Oh I just committed 54bb358 for this, feel free to comments :) |
Ah yea you did the same as I explained haha, just call the function directly. |
Are we ok with the macro always deriving the other macros? |
This is crazy loll |
Yeah, it was a really raw approach |
sea-orm/tests/common/bakery_chain/customer.rs Lines 5 to 11 in 9885029
@nappa85 I discovered a strange behavior. when So the following is correct: #[sea_orm(column_type = "Text", nullable)]
pub notes: Option<String>, But the following is wrong: #[sea_orm(column_type = "Text")]
pub notes: Option<String>, |
sea-orm/tests/common/bakery_chain/baker.rs Lines 13 to 23 in 9885029
Our current I am thinking if instead of #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
pub enum Relation {
#[sea_orm(
belongs_to = "super::bakery::Entity",
from = "Column::BakeryId",
to = "super::bakery::Column::Id",
on_update = "Cascade",
on_delete = "Cascade"
)]
Bakery,
} we do #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
pub enum Relation {
#[sea_orm(
belongs_to = "bakery",
from = "bakery_id",
to = "bakery.id",
on_update = "cascade",
on_delete = "cascade"
)]
Bakery,
} But is this 'too much'? |
Yes, it's kind-of wanted, IMHO if you're specifying the type, you've to specify it entirely |
I think it's becoming too much magic, what if the other entity isn't on the the parent module but it's on another crate? |
I agree that it's becoming a little bit too much magic. Perhaps the entire |
Understood
um... it does save a few lines and another impl block, innit?
Great, then seemingly everything is set now |
🎉 Released In 0.12.1 🎉Thank you everyone for the contribution! |
I was trying your crate, seems promising, but the way you choose to represent models is too verbose.
For example I've a DB table with 53 fields, using your system I've written like 210 LOC, repeating the same informations like 3 times.
I think everything could be done with a single proc_macro, that takes a standard Rust struct and replaces it with a module with the same name of the struct, with Model struct with more or less the same contents of the original struct, Column enum with structs fields as variants, automatic impl ColumnTrait for Column with a type mapping, where the mapping fails you can use a field notation to override it (comes handy for custom types), table name and primary key can be specified with a struct notation, what's missing?
Relations, well, this have to be specified externally, but you can point the enum with another notation, and if the notation isn't present you generate a void enum.
After that, if I can add something more, I often use Cow on my entities, because this way I'm not forced to clone Strings when updating/creating. With your system I can't, because Model doesn't support a lifetime. It's not a big problem, but it blocks also other custom types with a lifetime
The text was updated successfully, but these errors were encountered: