-
Notifications
You must be signed in to change notification settings - Fork 16
feat: add databaseInitialized attribute to @Column macro to force optional on Draft properties #36
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
base: main
Are you sure you want to change the base?
Conversation
@alephao Thanks so much for exploring this! We'll leave a review with some initial feedback while we think on some of the ramifications of this feature. |
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.
Some notes below!
We're excited about supporting this, but we do think there are many open questions that we're still trying to figure out.
The main one concerns the library's current design, where right now Draft
is only a thing on primary-keyed tables. This means that database-initialized functionality is only available on primary-keyed-tables, which is kind of a bummer. We think it probably should be possible to have Draft
types on any table with a primary key or database-initialized fields, but this is a pretty big design change to figure out.
We will hopefully have time to explore soon!
/// - primaryKey: The column is its table's auto-incrementing primary key. | ||
/// - databaseInitialized: The column has a default value and is not needed in an insert statement. | ||
@attached(accessor, names: named(willSet)) | ||
public macro Column( |
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.
One idea to simplify the diagnostics you added in the macro for nonsensical combinations of these two parameters would be to instead introduce an overload of @Column
that simply takes the databaseInitialized
parameter. That way it's impossible to invoke @Column
with both parameters.
primaryKey: Bool? = nil, | ||
databaseInitialized: Bool? = nil |
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.
In the spirit of the above comment, I think these parameters could stay Bool
and not be optional, which would further simplify diagnostics added to the macro.
// Missing cases | ||
// x = Optional<T>.some(_) | ||
// x = fnReturningOptionalType() |
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.
Is this helper mostly here to mark these cases as todos?
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 forgot to mention in the PR description, but I was wondering if it's worth it trying to do anything to figure out if a type is actually Optional or not. I was getting in a rabbit hole and left it as is to not waste too much time and get some input.
It doesn't seem worth it IMO, but I left there to inform that isOptional
does not guarantee that the binding is actually optional, and left some examples of bindings that could return a false negative.
@stephencelis Thanks for the quick response! I understand it's a big decision, but I'm happy to help explore the idea, even if it ends up going in a completely different direction. I'll try out your suggestions soon! |
@alephao Marking this as a draft for now, and please let us know if you intend to take it any further! |
Discussed briefly here: #30
Context
Let's say I have a table with a default value:
and the swift type would be:
The Draft API is a pretty nice way to insert a row on this table, but at the moment I need to pass a
Date
like so:Would be great if I could annotate the property so the
createdAt
becomes optional onMyTable.Draft
, similar to the@Column(primaryKey: true)
macro. So I can use it like so:One idea is to extend the
@Column
to accept a boolean and use it to decide if the generated property on Draft is optional or not, e.g.:databaseInitialized: Bool
(@mbrandonw suggestion)Implementation
To decide if a non-optional property should become optional in the Draft type, I did the following mapping:
There are some invalid cases, so I added error diagnostics to them:
And there are some redundant cases. I added a warning diagnostics to those:
I added tests for all those cases
Other Changes
Updated primaryKey attribute type and default value
I had to update the attribute
primaryKey: Bool = false
toprimaryKey: Bool? = nil
because if I happen to have anid: String
that should be initialized by the application (and not the database), then following code would behave in a way I'm not expecting:I'm doing the same for
databaseInitialized
, the attribute's argument isdatabaseInitialized: Bool? = nil
so we don't implicitly force any behavior.Force removing of
databaseInitialized
attribute from the Draft typeWhen creating the Draft type, it was inheriting the
@Column
attribute withdatabaseInitialized
which caused all sorts of issues, so I had to add some changes to prevent the Draft property to inherit thedatabaseInitialized
attributePrint warning diagnostics and still run the macro
Previously, any diagnostic would prevent the macro generation, now it only stops when there is an
error
diagnostic, if there are warning diagnostics, it sill generates the extra swift code