-
-
Notifications
You must be signed in to change notification settings - Fork 213
Replace "DynIden" with "IdenImpl" #892
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
Conversation
If you don't mind, I'll review when the CI passes. But feel free to ask if you need my review earlier |
Because it is too troublesome to fix the tests and examples, I also implemented the corresponding behavior for the derive macro Iden in this PR. |
It seems that all errors are caused by deprecation. |
sorry, it passed CI already. then there's a conflict |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I love this!
src/extension/mysql/column.rs
Outdated
impl From<MySqlType> for IdenImpl { | ||
fn from(value: MySqlType) -> Self { | ||
Self::from(match value { | ||
MySqlType::TinyBlob => "tinyblob", | ||
MySqlType::MediumBlob => "mediumblob", | ||
MySqlType::LongBlob => "longblob", | ||
}) | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This duplicated match
(with impl Iden
right above) doesn't look right. I think, in this iteration (this PR that doesn't delete trait Iden
yet) we should blanket impl<T> Iden for T where T: Into<IdenImpl>
.
Also, your PR description should mention the plan to delete the trait, so that it's easier for Chris to review and understand the whole picture (there will be nothing called IdenImpl
eventually)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
After this PR, the Iden trait is no longer used in the actual code. I think we can remove these impls?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the trait is already unused (there's no meaningful "intermediate" state of this trait in this PR), then I'd say we should just delete it and rename IdenImpl -> Iden
right in this PR
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're right. My current choice is mainly for compatibility, but since users will have to re-implement Into<IdenImpl>
anyway, it makes sense to replace Iden
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm. As I think about it, there's another, more compatibible option. The blanket impl could go the other way around: impl<T> From<T> for IdenImpl where T: Iden
. That way, if users have manual Iden
impls, these types will continue to work where IntoIden
is accepted.
But the downside is that the struct will be called IdenImpl
or something like that (Iden
is still taken by the trait). And there's more complexity overall because the trait is still around.
I'd say, we should clean things up and delete the trait.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm having some issues while cleaning up the code. Keeping IdenStatic
requires a lot of (dirty) fixes. But since the current Iden
is already static, I think we can remove IdenStatic as well?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, right. Nuke it. The lifetime of Rust identifier types no longer matters, because we no longer keep them inside DynIden
. Instead, we eagerly "render" them into an owned struct Iden
, and then deal only with that.
I really like your new design and cleanups. Less traits and lifetimes; more simple, concrete, owned values.
@@ -5,5 +5,5 @@ use sea_query::Iden; | |||
pub struct CustomName; | |||
|
|||
fn main() { | |||
assert_eq!(CustomName.to_string(), "another_name"); | |||
assert_eq!(Iden::from(CustomName).to_string(), "another_name"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm. Is this pattern common in the actual user code? I see a lot of this in the diff. Perhaps, we should find a way to reduce this breakage and verbosity. E.g., add a helper trait ToIdenSting
that's blanket-implemented for Into<Iden>
and provides this to_string
method from the old Iden
trait?
I think, it won't be as bad as the old Iden
trait, because SeaQuery interfaces wouldn't depend on ToIdenSting
for anything. It's just a helper "extension trait", and we can document that
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think that's very common. But we could add a trait to provide the corresponding Iden methods for any T: Into.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I think we can provide that extension trait as IdenTrait
or something like that.
On one hand, it's a little messy because we keep much of the old API surface.
On the other hand, the diff indicates that the trait was actually used outside of DynIden
. So, it's probably worth keeping. And we still improve the API by making it an optional extension trait and no longer depending on it via DynIden
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, I have an even crazier (or even more boring and conservative?) idea. What if we keep even the name of trait Iden
the same, and keep your Cow
struct called DynIden
? This naming even makes sense, because struct DynIden
is a "raw" identifier string that's no longer "type safe" and could refer to any SQL entity
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This even preserves backward-compatibility with user code that uses the actual dyn Iden
. We'll just no longer use it in SeaQuery
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For even more compatibility, we can:
- Reverse the blanket impl:
impl<T> From<T> for DynIden where T: Iden
. - Keep generating
impl Iden
in#[derive(Iden)]
. - Keep the users' manual
impl Iden
working
🤯🤯🤯
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's the issues with this implementation:
-
The Iden trait doesn't provide a method to get a
&'static str
, so we can only implement
impl<T> From<T> for DynIden where T: IdenStatic
. -
Therefore, we cannot solve the problem of
derive(Iden)
because this trait can be implemented in a non-static way. -
One option is to add a method to
Iden
trait that returns aCow
, but in practice, I think this isn't much different from implementing theIden
trait for types that implementInto<IdenImpl>
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are still more issues: users can implement the Iden trait themselves to override the default behavior—for example, to ignore quote as a way to bypass escaping. The current idenimpl cannot provide the same behavior.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
impl<T> From<T> for DynIden where T: Iden
we can, but we have to always return Owned
, which is not ideal.
yeah, IdenStatic
is somehow added later than Iden
that made things complicated.
for example, to ignore quote as a way to bypass escaping
I think we ultimately have to reject this use case as part of the breaking change
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we ultimately have to reject this use case as part of the breaking change
Ok, that means that we must delete the provided Iden
methods, so that they can not be overridden [1]. We can keep their calls working [2] by adding a blanket-implemented trait IdenExt
. This solution makes sense to me.
[1]: Whether we decide to keep trait Iden
for some compatibility, or not.
[2]: Actually, the calls will break initially, but the users only need to use sea_query::IdenExt
as the compiler suggests.
use sea_query::{Iden, IdenStatic}; | ||
use strum::{EnumIter, IntoEnumIterator}; | ||
|
||
#[derive(Copy, Clone, IdenStatic, EnumIter)] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry for noticing this only now, but perhaps we should keep IdenStatic
derive macro for compatibility? Just as an alias that does the same thing as #[derive(Iden)]
.
Hmm, perhaps it shouldn't even be an alias, and we should keep the trait IdenStatic
and blaknet-impl<T> From<T> for Iden where T: IdenStatic
? That would be nice for compatibility, and also for providing a compile-time guarantee that you can cheaply get a &static str
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We could make the derive(IdenStatic) do nothing and implement as_str and AsRef for Iden.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
On this line of code that I have reviewed, #[derive(IdenStatic)]
is used without #[derive(Iden)]
. It used to implicitly generate impl Iden
in addition to impl IdenStatic
.
If we make #[derive(IdenStatic)]
do nothing, then code like this breaks completely (no longer gives us an IntoIden
type), instead of generating a partially-compatible impl From<_> for Iden
that at least still allows the type to be passed into methods that accept IntoIden
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, I see — IdenStatic actually implements both Iden and IdenStatic, so maybe we just need to re-export the derive(Iden) macro as derive(IdenStatic)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's what I've initially suggested here, yeah. That's the minimal viable solution, IMO.
Whether we should additionally preserve trait IdenStatic
, is a more debatable design tradeoff
sorry for the late reply. I think this is in the right direction. however, I am afraid there's still too much breaking change. having said that, I think most of the changes can be polyfilled:
|
|
I agree with you in principle, but we're faced with a peculiar task of balancing between APIs that make more sense and APIs that are more compatible. I think, the right way is keeping
There is one that I proposed here: blanket This will still break manual The only really-non-breaking alternative is reversing the blanket
Yeah, we can keep if for compatibility and later deprecate in
This PR already keeps This still breaks manual If the type manually implements
I'm also strongly in favor of adding an |
I would probably even not put in the
well, I guess my argument is that String is data and I don't want it to be confused as identifier. example: let a = String::new("a");
let b = String::new("b");
`a.eq(b)` would result in `"a" = 'b'`
`b.eq(a)` would result in `"b" = 'a'` one is quoted as identifier, one is quoted as literal. am I right that it will cause this confusion ultimately? |
Yeah, agree. Definitely not in
That's an interesting argument and we should explore it. But it needs a convincing example. Your pseudocode example is incorrect. The non-pseudocode // [dependencies]
// sea-query = { version = "0.32.3", features = ["tests-cfg"] }
use sea_query::{ExprTrait, PostgresQueryBuilder, QueryBuilder};
fn main() {
let a = String::from("a");
let b = String::from("b");
let mut sql = String::new();
PostgresQueryBuilder.prepare_simple_expr(&a.clone().eq(&b), &mut sql);
println!("{}", sql);
let mut sql = String::new();
PostgresQueryBuilder.prepare_simple_expr(&b.eq(&a), &mut sql);
println!("{}", sql);
} prints 'a' = 'b'
'b' = 'a' And to me, that's obvious and expected. Identifiers don't even implement |
@Huliiiiii, we've chatted with Chris and decided to split the PR in order to proceed. Initially, we want to merge these two small changes as separate PRs:
That's a blocker for now. |
I haven’t had time to work on this lately. I’m totally fine if you wants to take over or use this as a base for further changes. |
I will try some experiments |
thank you so much for the initiative! |
Background: #795 (comment)
PR Info
New Features
Bug Fixes
Breaking Changes
Replaced
DynIden
with concretestruct IdenImpl
.Iden
, you don't need to do anything.Iden
manually, you need to replace it withimpl From<YourType> for IdenImpl
.Unlike
DynIden
,IdemImpl
is compared purely by string. Same strings rendered from different Rust identifier types will now compare as equal.Remove
SeaRc
.SeaRc
was only used forDynIden
. So, after you fix the errors aroundDynIden
, you shouldn't have any errors left.SeaRc
for your own purposes outside ofDynIden
, there can be more errors. There, you can try replacingSeaRc
withRcOrArc
(which was the underlying implementation ofSeaRc
anyway).Changes