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
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions core/codegen/src/derive/from_param.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
use devise::Support;
use proc_macro2::TokenStream;
use quote::quote;
use devise::*;
use devise::ext::SpanDiagnosticExt;
use crate::exports::*;

pub fn derive_from_param(input: proc_macro::TokenStream) -> TokenStream {
DeriveGenerator::build_for(input, quote!(impl<'a> #_request::FromParam<'a>))
.support(Support::Enum)
.validator(ValidatorBuild::new()
.fields_validate(|_, fields| {
if !fields.is_empty() {
return Err(
fields.span().error("Only enums without data fields are supported")
);
}
Ok(())
})
)
.inner_mapper(MapperBuild::new()
.enum_map(|_, data| {
let matches = data.variants().map(|field| {
let field_name = &field;
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")
}
}
}
})
)
.to_tokens()
}
1 change: 1 addition & 0 deletions core/codegen/src/derive/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ pub mod from_form;
pub mod from_form_field;
pub mod responder;
pub mod uri_display;
pub mod from_param;
33 changes: 33 additions & 0 deletions core/codegen/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -774,6 +774,39 @@ pub fn derive_from_form(input: TokenStream) -> TokenStream {
emit!(derive::from_form::derive_from_form(input))
}

/// Derive for the [`FromParam`] trait.
///
/// The [`FromParam`] derive can be applied to enums with nullary
the10thWiz marked this conversation as resolved.
Show resolved Hide resolved
/// (zero-length) fields. To implement FromParam, the function matches each variant
/// to its stringified field name (case sensitive):
///
/// ```rust
/// # #[macro_use] extern crate rocket;
/// #
/// use rocket::request::FromParam;
///
/// #[derive(FromParam, Debug, PartialEq)]
/// enum MyParam {
/// A,
/// B,
/// }
///
/// assert_eq!(MyParam::from_param("A").unwrap(), MyParam::A);
the10thWiz marked this conversation as resolved.
Show resolved Hide resolved
/// assert_eq!(MyParam::from_param("B").unwrap(), MyParam::B);
/// assert!(MyParam::from_param("a").is_err());
/// assert!(MyParam::from_param("b").is_err());
/// assert!(MyParam::from_param("c").is_err());
/// assert!(MyParam::from_param("C").is_err());
/// ```
///
/// Now `MyParam` can be used in an endpoint and will accept either `A` or `B`.
/// [`FromParam`]: ../rocket/request/trait.FromParam.html
///
#[proc_macro_derive(FromParam)]
pub fn derive_from_param(input: TokenStream) -> TokenStream {
emit!(derive::from_param::derive_from_param(input))
}

/// Derive for the [`Responder`] trait.
///
/// The [`Responder`] derive can be applied to enums and structs with named
Expand Down
19 changes: 19 additions & 0 deletions core/codegen/tests/from_param.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
use rocket::request::FromParam;

#[derive(Debug, FromParam, PartialEq)]
enum Test {
Test1,
Test2
}

#[test]
fn derive_from_param() {
let test1 = Test::from_param("Test1").expect("Should be valid");
assert_eq!(test1, Test::Test1);

let test2 = Test::from_param("Test2").expect("Should be valid");
assert_eq!(test2, Test::Test2);
the10thWiz marked this conversation as resolved.
Show resolved Hide resolved

let test3 = Test::from_param("not_test");
assert!(test3.is_err());
}
1 change: 1 addition & 0 deletions core/codegen/tests/ui-fail-nightly/from_param.rs
53 changes: 53 additions & 0 deletions core/codegen/tests/ui-fail-nightly/from_param.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
error: named structs are not supported
--> tests/ui-fail-nightly/from_param.rs:4:1
|
4 | / struct Foo1 {
5 | | a: String
6 | | }
| |_^
|
note: error occurred while deriving `FromParam`
--> tests/ui-fail-nightly/from_param.rs:3:10
|
3 | #[derive(FromParam)]
| ^^^^^^^^^
= note: this error originates in the derive macro `FromParam` (in Nightly builds, run with -Z macro-backtrace for more info)

error: named structs are not supported
--> tests/ui-fail-nightly/from_param.rs:9:1
|
9 | struct Foo2 {}
| ^^^^^^^^^^^^^^
|
note: error occurred while deriving `FromParam`
--> tests/ui-fail-nightly/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)

error: Only enums without data fields are supported
--> tests/ui-fail-nightly/from_param.rs:13:6
|
13 | A(String),
| ^^^^^^^^
|
note: error occurred while deriving `FromParam`
--> tests/ui-fail-nightly/from_param.rs:11:10
|
11 | #[derive(FromParam)]
| ^^^^^^^^^
= note: this error originates in the derive macro `FromParam` (in Nightly builds, run with -Z macro-backtrace for more info)

error: tuple structs are not supported
--> tests/ui-fail-nightly/from_param.rs:18:1
|
18 | struct Foo4(usize);
| ^^^^^^^^^^^^^^^^^^^
|
note: error occurred while deriving `FromParam`
--> tests/ui-fail-nightly/from_param.rs:17:10
|
17 | #[derive(FromParam)]
| ^^^^^^^^^
= note: this error originates in the derive macro `FromParam` (in Nightly builds, run with -Z macro-backtrace for more info)
1 change: 1 addition & 0 deletions core/codegen/tests/ui-fail-stable/from_param.rs
57 changes: 57 additions & 0 deletions core/codegen/tests/ui-fail-stable/from_param.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
error: named structs are not supported
--> tests/ui-fail-stable/from_param.rs:4:1
|
4 | / struct Foo1 {
5 | | a: String
6 | | }
| |_^

error: [note] error occurred while deriving `FromParam`
--> tests/ui-fail-stable/from_param.rs:3:10
|
3 | #[derive(FromParam)]
| ^^^^^^^^^
|
= note: this error originates in the derive macro `FromParam` (in Nightly builds, run with -Z macro-backtrace for more info)

error: named structs are not supported
--> tests/ui-fail-stable/from_param.rs:9:1
|
9 | struct Foo2 {}
| ^^^^^^^^^^^^^^

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)

error: Only enums without data fields are supported
--> tests/ui-fail-stable/from_param.rs:13:6
|
13 | A(String),
| ^^^^^^^^

error: [note] error occurred while deriving `FromParam`
--> tests/ui-fail-stable/from_param.rs:11:10
|
11 | #[derive(FromParam)]
| ^^^^^^^^^
|
= note: this error originates in the derive macro `FromParam` (in Nightly builds, run with -Z macro-backtrace for more info)

error: tuple structs are not supported
--> tests/ui-fail-stable/from_param.rs:18:1
|
18 | struct Foo4(usize);
| ^^^^^^^^^^^^^^^^^^^

error: [note] error occurred while deriving `FromParam`
--> tests/ui-fail-stable/from_param.rs:17:10
|
17 | #[derive(FromParam)]
| ^^^^^^^^^
|
= note: this error originates in the derive macro `FromParam` (in Nightly builds, run with -Z macro-backtrace for more info)
20 changes: 20 additions & 0 deletions core/codegen/tests/ui-fail/from_param.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
use rocket::request::FromParam;

#[derive(FromParam)]
struct Foo1 {
a: String
}

#[derive(FromParam)]
struct Foo2 {}

#[derive(FromParam)]
enum Foo3 {
A(String),
B(String)
}

#[derive(FromParam)]
struct Foo4(usize);

fn main() {}
3 changes: 3 additions & 0 deletions core/lib/src/request/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ pub use self::request::Request;
pub use self::from_request::{FromRequest, Outcome};
pub use self::from_param::{FromParam, FromSegments};

#[doc(hidden)]
pub use rocket_codegen::FromParam;

#[doc(inline)]
pub use crate::response::flash::FlashMessage;

Expand Down
Loading