Skip to content
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

Derive FromParam for Enum #2832

Closed
wants to merge 5 commits into from
Closed

Conversation

loikki
Copy link

@loikki loikki commented Jul 25, 2024

Implement #2826

Copy link
Collaborator

@the10thWiz the10thWiz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall, this is a great start.

Ideally, we should include some ui-fail tests with this macro, see core/codegen/tests/ui-fail/from_form.rs for some examples.

@SergioBenitez, I think this should be good to go with the changes I've recommended, let us know if you have any other comments.

core/codegen/src/derive/from_param.rs Outdated Show resolved Hide resolved
core/codegen/src/lib.rs Outdated Show resolved Hide resolved
core/codegen/src/lib.rs Outdated Show resolved Hide resolved
core/codegen/tests/from_param.rs Outdated Show resolved Hide resolved
core/codegen/tests/from_param.rs Show resolved Hide resolved
core/codegen/src/lib.rs Outdated Show resolved Hide resolved
@loikki loikki force-pushed the implement-derive-parafrom branch from 079ee9f to 9df9014 Compare August 2, 2024 15:25
@loikki
Copy link
Author

loikki commented Aug 2, 2024

Hi @the10thWiz

Thanks for the feedback. I have applied all your suggestions but I am not sure what is the point of core/codegen/tests/ui-fail/from_form.rs.

From the name, I guess everything should fail in it right? How do you check that each structure fails? In my understanding, just one failing would be sufficient for the test to pass, right?

@the10thWiz
Copy link
Collaborator

In my understanding, just one failing would be sufficient for the test to pass, right?

Not in this case. We use trybuild to run the ui-fail test cases, which actually validates the compiler output generated by the build failures. When you run the test, you can set an evironment variable to have trybuild save the current outpuut.

Use ./scripts/test.sh --ui to execute the ui tests locally. (They are ignored by default, and only run on one platforms in CI since they can be somewhat finnicky).

@loikki
Copy link
Author

loikki commented Aug 2, 2024

Thanks for the explanations. trybuild looks like a nice project :)

I have updated the tests with the correct stderr. Should I add it to nightly too or just stable?

Copy link
Collaborator

@the10thWiz the10thWiz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we're very close! In terms of the ui-fail test, I think we will need a nightly version, but I might just take care of it myself.

Comment on lines 12 to 40
.fields_validate(|_, fields| {
if !fields.is_empty() {
return Err(fields.span().error("Only empty enums are accepted"));
}

Ok(())
})
)
.inner_mapper(MapperBuild::new()
.enum_map(|_, data| {
let mut matches = vec![];

for field in data.variants() {
let field_name = &field;
matches.push(quote!(
stringify!(#field_name) => Ok(Self::#field_name),
))
}

quote! {
type Error = &'a str;
fn from_param(param: &'a str) -> Result<Self, Self::Error> {
match param {
#(#matches)*
_ => Err("Failed to find enum")
}
}
}
})
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you take a look at the indentation here?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see any issue with it. I checked with the auto-indent in my IDE and with cargo fmt --check.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rocket intentionally disables rustfmt (cargo fmt), since it doesn't handle some of the more complex trait bounds well. I suspect your IDE's auto-indent is based on rustfmt as well (I don't think there is another well built option out there yet), so that's likely why it's not picking up on the issues here.

The specific issue here is that not all the indentations are the same size (This is extremely obvious in my editor, since I have indent guides turned on). If you would like, I can push a fixed version to this PR.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Even after enabling the indent guide, I don't see it. So it would be nice if you can do it.

core/codegen/tests/ui-fail/from_param.rs Outdated Show resolved Hide resolved
core/codegen/src/lib.rs Outdated Show resolved Hide resolved
@loikki
Copy link
Author

loikki commented Aug 6, 2024

I have pushed the requested changes and wrote a few comments

@the10thWiz
Copy link
Collaborator

You're close, just a few things to fix. The indentation on the one file needs to be fixed, and the test failure in the one doc-test needs to be addressed.

Other than that, I'm quite happy with this feature.

Comment on lines 23 to 29
error: [note] error occurred while deriving `FromParam`
--> tests/ui-fail-stable/from_param.rs:8:10
|
8 | #[derive(FromParam)]
| ^^^^^^^^^
|
= note: this error originates in the derive macro `FromParam` (in Nightly builds, run with -Z macro-backtrace for more info)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's surprising that this isn't an actual note, since they exist on nightly. Is this being tested? Is this the output that's actually being produced?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My bad. I supposed that it would produce the same output. Just tested and fixed it.

.validator(ValidatorBuild::new()
.fields_validate(|_, fields| {
if !fields.is_empty() {
return Err(fields.span().error("Only empty enums are accepted"));
Copy link
Member

@SergioBenitez SergioBenitez Aug 7, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe the wording here should be "only enums without variants are supported" ah, I confused myself. The actual constraint here is "only enum variants with nullary (zero-length) fields".

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably Only enums without data fields are supported.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the language I was looking for was getting away form "enums with foo" and instead going for "enum variants with foo," since that's the actual constant. We should also make sure the error is on the fields themselves.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should also make sure the error is on the fields themselves. Do you mean the error message?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

He means the associated span, and it looks like you already have it correct.

Comment on lines 21 to 28
let mut matches = vec![];

for field in data.variants() {
let field_name = &field;
matches.push(quote!(
stringify!(#field_name) => Ok(Self::#field_name),
))
}
Copy link
Member

@SergioBenitez SergioBenitez Aug 7, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can probably be a .iter().map(). I don't recall if quote can use the iterator directly, but if it can, then that would save us the allocation here.

Copy link
Member

@SergioBenitez SergioBenitez left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this correctly handle raw idents? IE:

enum Foo {
    r#for,
}

// parse "for" as `Foo::r#for`

Finally, it want to note that this macro is almost exactly the FromFormField macro except that macro also allows renaming fields. Is there a reason to have two implementations, or can we simply factor out the existing FromFormField implementation for FromParam?

@loikki
Copy link
Author

loikki commented Aug 7, 2024

I agree that FromFormField is very similar. I have little experience with rust macros and especially with devise (which is extremely badly documented). So I am not sure how would be the best approach to do it.

I am just wondering if it makes sense to merge them together. Is there any chance that FromFormField will be extended to more complex structures? FromParam is really restricted to "object" that can be matched to string as it is used in the query path.

By the way, raw identifiers are not working. How would do you like to proceed? Removing the r# from the string or add a parameter to rename the field (e.g. #[field(value="for")] for FromFormField.

Copy link
Collaborator

@the10thWiz the10thWiz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great! I'll take care of formatting and raw idents (Ident has an unraw method that strips the r#), and merge these changes.

Looking forward, I think we will want to add support for a rename attribute, as well as refactor both this and the FromFormField derive to share code as much as possible. FromFormField may eventually wind up with a more complex macro, since it does have quite a bit more context (i.e. the name, and potentially a whole file). It also matches case-insensitively, but I think the difference in behavior is justified.

@the10thWiz
Copy link
Collaborator

Merged in 38cebeb!

@the10thWiz the10thWiz closed this Aug 8, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants