mirror of https://github.com/rwf2/Rocket.git
Derive FromParam for Enum #2826
This commit is contained in:
parent
509a033c84
commit
38cebebbc3
|
@ -0,0 +1,48 @@
|
|||
use crate::exports::*;
|
||||
use devise::ext::SpanDiagnosticExt;
|
||||
use devise::Support;
|
||||
use devise::*;
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::quote;
|
||||
use syn::ext::IdentExt;
|
||||
|
||||
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.ident.unraw();
|
||||
quote!(
|
||||
stringify!(#field_name) => Ok(Self::#field),
|
||||
)
|
||||
});
|
||||
let names = data.variants().map(|field| {
|
||||
let field_name = field.ident.unraw();
|
||||
quote!(
|
||||
#_Cow::Borrowed(stringify!(#field_name)),
|
||||
)
|
||||
});
|
||||
|
||||
quote! {
|
||||
type Error = #_request::EnumFromParamError<'a>;
|
||||
fn from_param(param: &'a str) -> Result<Self, Self::Error> {
|
||||
match param {
|
||||
#(#matches)*
|
||||
_ => Err(#_request::EnumFromParamError::new(
|
||||
#_Cow::Borrowed(param),
|
||||
#_Cow::Borrowed(&[#(#names)*]),
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
}))
|
||||
.to_tokens()
|
||||
}
|
|
@ -3,3 +3,4 @@ pub mod from_form;
|
|||
pub mod from_form_field;
|
||||
pub mod responder;
|
||||
pub mod uri_display;
|
||||
pub mod from_param;
|
||||
|
|
|
@ -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
|
||||
/// (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);
|
||||
/// 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
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
use rocket::request::FromParam;
|
||||
|
||||
#[derive(Debug, FromParam, PartialEq)]
|
||||
enum Test {
|
||||
Test1,
|
||||
Test2,
|
||||
r#for,
|
||||
}
|
||||
|
||||
#[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);
|
||||
let test2 = Test::from_param("for").expect("Should be valid");
|
||||
assert_eq!(test2, Test::r#for);
|
||||
|
||||
let test3 = Test::from_param("not_test");
|
||||
assert!(test3.is_err());
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
../ui-fail/from_param.rs
|
|
@ -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)
|
|
@ -0,0 +1 @@
|
|||
../ui-fail/from_param.rs
|
|
@ -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)
|
|
@ -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() {}
|
|
@ -1,6 +1,10 @@
|
|||
use std::borrow::Cow;
|
||||
use std::fmt;
|
||||
use std::str::FromStr;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use cookie::Display;
|
||||
|
||||
use crate::error::Empty;
|
||||
use crate::either::Either;
|
||||
use crate::http::uri::{Segments, error::PathError, fmt::Path};
|
||||
|
@ -306,6 +310,31 @@ impl<'a, T: FromParam<'a>> FromParam<'a> for Option<T> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Error type for automatically derived `FromParam` enums
|
||||
#[derive(Debug, Clone)]
|
||||
#[non_exhaustive]
|
||||
pub struct EnumFromParamError<'a> {
|
||||
pub value: Cow<'a, str>,
|
||||
pub options: Cow<'static, [Cow<'static, str>]>,
|
||||
}
|
||||
|
||||
impl<'a> EnumFromParamError<'a> {
|
||||
pub fn new(value: Cow<'a, str>, options: Cow<'static, [Cow<'static, str>]>) -> Self {
|
||||
Self {
|
||||
value,
|
||||
options,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for EnumFromParamError<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "Unexpected value {:?}, expected one of {:?}", self.value, self.options)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for EnumFromParamError<'_> {}
|
||||
|
||||
/// Trait to convert _many_ dynamic path segment strings to a concrete value.
|
||||
///
|
||||
/// This is the `..` analog to [`FromParam`], and its functionality is identical
|
||||
|
|
|
@ -12,6 +12,12 @@ pub use self::request::Request;
|
|||
pub use self::from_request::{FromRequest, Outcome};
|
||||
pub use self::from_param::{FromParam, FromSegments};
|
||||
|
||||
#[doc(hidden)]
|
||||
pub use self::from_param::EnumFromParamError;
|
||||
|
||||
#[doc(hidden)]
|
||||
pub use rocket_codegen::FromParam;
|
||||
|
||||
#[doc(inline)]
|
||||
pub use crate::response::flash::FlashMessage;
|
||||
|
||||
|
|
Loading…
Reference in New Issue