From c9376948f8f778597a736619454b2a181d924376 Mon Sep 17 00:00:00 2001 From: Jonas Platte Date: Mon, 27 Jan 2020 10:47:18 +0100 Subject: [PATCH 1/3] Add syn::Result scaffolding (and also add spans to one error message) --- crates/yew_router_macro/src/lib.rs | 7 +++++- crates/yew_router_macro/src/switch.rs | 25 ++++++++----------- .../yew_router_macro/src/switch/attribute.rs | 24 ++++++++---------- 3 files changed, 26 insertions(+), 30 deletions(-) diff --git a/crates/yew_router_macro/src/lib.rs b/crates/yew_router_macro/src/lib.rs index 4f407bf..28b7e25 100644 --- a/crates/yew_router_macro/src/lib.rs +++ b/crates/yew_router_macro/src/lib.rs @@ -1,5 +1,6 @@ extern crate proc_macro; use proc_macro::TokenStream; +use syn::{parse_macro_input, DeriveInput}; mod switch; @@ -78,7 +79,11 @@ mod switch; /// Check out the examples directory in the repository to see some more usages of the routing syntax. #[proc_macro_derive(Switch, attributes(to, rest, end))] pub fn switch(tokens: TokenStream) -> TokenStream { - crate::switch::switch_impl(tokens) + let input: DeriveInput = parse_macro_input!(tokens as DeriveInput); + + crate::switch::switch_impl(input) + .unwrap_or_else(|err| err.to_compile_error()) + .into() } #[proc_macro_attribute] diff --git a/crates/yew_router_macro/src/switch.rs b/crates/yew_router_macro/src/switch.rs index a61441d..e7b6d72 100644 --- a/crates/yew_router_macro/src/switch.rs +++ b/crates/yew_router_macro/src/switch.rs @@ -1,8 +1,7 @@ use crate::switch::shadow::{ShadowCaptureVariant, ShadowMatcherToken}; -use proc_macro::TokenStream; -use proc_macro2::Span; +use proc_macro2::{Span, TokenStream}; use quote::{quote, ToTokens}; -use syn::{export::TokenStream2, parse_macro_input, Data, DeriveInput, Fields, Ident, Variant}; +use syn::{export::TokenStream2, Data, DeriveInput, Fields, Ident, Variant}; mod attribute; mod enum_impl; @@ -21,20 +20,18 @@ pub struct SwitchItem { pub fields: Fields, } -pub fn switch_impl(input: TokenStream) -> TokenStream { - let input: DeriveInput = parse_macro_input!(input as DeriveInput); - +pub fn switch_impl(input: DeriveInput) -> syn::Result { let ident: Ident = input.ident; let generics = input.generics; - match input.data { + Ok(match input.data { Data::Struct(ds) => { let field_naming_scheme = match ds.fields { Fields::Unnamed(_) => FieldNamingScheme::Unnamed, Fields::Unit => FieldNamingScheme::Unit, Fields::Named(_) => FieldNamingScheme::Named, }; - let matcher = AttrToken::convert_attributes_to_tokens(input.attrs) + let matcher = AttrToken::convert_attributes_to_tokens(input.attrs)? .into_iter() .enumerate() .map(|(index, at)| at.into_shadow_matcher_tokens(index, field_naming_scheme)) @@ -59,7 +56,6 @@ pub fn switch_impl(input: TokenStream) -> TokenStream { }, } .to_token_stream() - .into() } Data::Enum(de) => { let switch_variants = de @@ -71,19 +67,19 @@ pub fn switch_impl(input: TokenStream) -> TokenStream { Fields::Unit => FieldNamingScheme::Unit, Fields::Named(_) => yew_router_route_parser::FieldNamingScheme::Named, }; - let matcher = AttrToken::convert_attributes_to_tokens(variant.attrs) + let matcher = AttrToken::convert_attributes_to_tokens(variant.attrs)? .into_iter() .enumerate() .map(|(index, at)| at.into_shadow_matcher_tokens(index, field_type)) .flatten() .collect::>(); - SwitchItem { + Ok(SwitchItem { matcher, ident: variant.ident, fields: variant.fields, - } + }) }) - .collect::>(); + .collect::>>()?; SwitchImpl { @@ -102,10 +98,9 @@ pub fn switch_impl(input: TokenStream) -> TokenStream { }, } .to_token_stream() - .into() } Data::Union(_du) => panic!("Deriving FromCaptures not supported for Unions."), - } + }) } trait Flatten { diff --git a/crates/yew_router_macro/src/switch/attribute.rs b/crates/yew_router_macro/src/switch/attribute.rs index 7d8dd11..090d4e3 100644 --- a/crates/yew_router_macro/src/switch/attribute.rs +++ b/crates/yew_router_macro/src/switch/attribute.rs @@ -9,11 +9,11 @@ pub enum AttrToken { } impl AttrToken { - pub fn convert_attributes_to_tokens(attributes: Vec) -> Vec { - fn get_meta_name_value_str(mnv: &MetaNameValue) -> Option { + pub fn convert_attributes_to_tokens(attributes: Vec) -> syn::Result> { + fn get_meta_name_value_str(mnv: &MetaNameValue) -> syn::Result { match &mnv.lit { - Lit::Str(s) => Some(s.value()), - _ => None, + Lit::Str(s) => Ok(s.value()), + lit => Err(syn::Error::new_spanned(lit, "expected a string literal")), } } @@ -27,14 +27,10 @@ impl AttrToken { .get_ident() .into_iter() .filter_map(|ident| match ident.to_string().as_str() { - "to" => Some(AttrToken::To( - get_meta_name_value_str(&mnv) - .expect("Value provided after `to` must be a String"), - )), - "rest" => Some(AttrToken::Rest(Some( - get_meta_name_value_str(&mnv) - .expect("Value provided after `rest` must be a String"), - ))), + "to" => Some(get_meta_name_value_str(&mnv).map(AttrToken::To)), + "rest" => { + Some(get_meta_name_value_str(&mnv).map(|s| AttrToken::Rest(Some(s)))) + } _ => None, }) .next(), @@ -42,8 +38,8 @@ impl AttrToken { .get_ident() .into_iter() .filter_map(|ident| match ident.to_string().as_str() { - "end" => Some(AttrToken::End), - "rest" => Some(AttrToken::Rest(None)), + "end" => Some(Ok(AttrToken::End)), + "rest" => Some(Ok(AttrToken::Rest(None))), _ => None, }) .next(), From 6b0b612b9793750059c4bb4983dfd40f9284f874 Mon Sep 17 00:00:00 2001 From: Jonas Platte Date: Mon, 27 Jan 2020 10:54:08 +0100 Subject: [PATCH 2/3] Error on unsupported #[to(...)], #[rest(...)] syntax --- .../yew_router_macro/src/switch/attribute.rs | 51 +++++++++++-------- 1 file changed, 30 insertions(+), 21 deletions(-) diff --git a/crates/yew_router_macro/src/switch/attribute.rs b/crates/yew_router_macro/src/switch/attribute.rs index 090d4e3..bc25ca5 100644 --- a/crates/yew_router_macro/src/switch/attribute.rs +++ b/crates/yew_router_macro/src/switch/attribute.rs @@ -1,5 +1,5 @@ use crate::switch::shadow::{ShadowCaptureVariant, ShadowMatcherToken}; -use syn::{Attribute, Lit, Meta, MetaNameValue}; +use syn::{spanned::Spanned, Attribute, Lit, Meta, MetaNameValue}; use yew_router_route_parser::FieldNamingScheme; pub enum AttrToken { @@ -20,30 +20,39 @@ impl AttrToken { attributes .iter() .filter_map(|attr: &Attribute| attr.parse_meta().ok()) - .filter_map(|meta: Meta| match meta { - Meta::NameValue(mnv) => mnv - .path - .clone() - .get_ident() - .into_iter() - .filter_map(|ident| match ident.to_string().as_str() { - "to" => Some(get_meta_name_value_str(&mnv).map(AttrToken::To)), - "rest" => { - Some(get_meta_name_value_str(&mnv).map(|s| AttrToken::Rest(Some(s)))) + .filter_map(|meta: Meta| { + let meta_span = meta.span(); + match meta { + Meta::NameValue(mnv) => mnv.path.get_ident().into_iter().find_map(|ident| { + match ident.to_string().as_str() { + "to" => Some(get_meta_name_value_str(&mnv).map(AttrToken::To)), + "rest" => Some( + get_meta_name_value_str(&mnv).map(|s| AttrToken::Rest(Some(s))), + ), + _ => None, } - _ => None, - }) - .next(), - Meta::Path(path) => path - .get_ident() - .into_iter() - .filter_map(|ident| match ident.to_string().as_str() { + }), + Meta::Path(path) => path.get_ident().into_iter().find_map(|ident| match ident + .to_string() + .as_str() + { "end" => Some(Ok(AttrToken::End)), "rest" => Some(Ok(AttrToken::Rest(None))), _ => None, - }) - .next(), - _ => None, + }), + Meta::List(list) => list.path.get_ident().into_iter().find_map(|ident| { + match ident.to_string().as_str() { + id @ "to" | id @ "rest" => Some(Err(syn::Error::new( + meta_span, + &format!( + "This syntax is not supported, did you mean `#[{} = ...]`?", + id + ), + ))), + _ => None, + } + }), + } }) .collect() } From a7ebef4276de02a8f703ce2b7b64c4e5c1aec390 Mon Sep 17 00:00:00 2001 From: Jonas Platte Date: Mon, 27 Jan 2020 18:35:36 +0100 Subject: [PATCH 3/3] Use `opt.and_then(...)` instead of `opt.into_iter().find_map(...)` --- .../yew_router_macro/src/switch/attribute.rs | 60 ++++++++++--------- 1 file changed, 32 insertions(+), 28 deletions(-) diff --git a/crates/yew_router_macro/src/switch/attribute.rs b/crates/yew_router_macro/src/switch/attribute.rs index bc25ca5..2afce79 100644 --- a/crates/yew_router_macro/src/switch/attribute.rs +++ b/crates/yew_router_macro/src/switch/attribute.rs @@ -23,35 +23,39 @@ impl AttrToken { .filter_map(|meta: Meta| { let meta_span = meta.span(); match meta { - Meta::NameValue(mnv) => mnv.path.get_ident().into_iter().find_map(|ident| { - match ident.to_string().as_str() { - "to" => Some(get_meta_name_value_str(&mnv).map(AttrToken::To)), - "rest" => Some( - get_meta_name_value_str(&mnv).map(|s| AttrToken::Rest(Some(s))), - ), - _ => None, - } - }), - Meta::Path(path) => path.get_ident().into_iter().find_map(|ident| match ident - .to_string() - .as_str() - { - "end" => Some(Ok(AttrToken::End)), - "rest" => Some(Ok(AttrToken::Rest(None))), - _ => None, - }), - Meta::List(list) => list.path.get_ident().into_iter().find_map(|ident| { - match ident.to_string().as_str() { - id @ "to" | id @ "rest" => Some(Err(syn::Error::new( - meta_span, - &format!( - "This syntax is not supported, did you mean `#[{} = ...]`?", - id + Meta::NameValue(mnv) => { + mnv.path + .get_ident() + .and_then(|ident| match ident.to_string().as_str() { + "to" => Some(get_meta_name_value_str(&mnv).map(AttrToken::To)), + "rest" => Some( + get_meta_name_value_str(&mnv).map(|s| AttrToken::Rest(Some(s))), ), - ))), - _ => None, - } - }), + _ => None, + }) + } + Meta::Path(path) => { + path.get_ident() + .and_then(|ident| match ident.to_string().as_str() { + "end" => Some(Ok(AttrToken::End)), + "rest" => Some(Ok(AttrToken::Rest(None))), + _ => None, + }) + } + Meta::List(list) => { + list.path + .get_ident() + .and_then(|ident| match ident.to_string().as_str() { + id @ "to" | id @ "rest" => Some(Err(syn::Error::new( + meta_span, + &format!( + "This syntax is not supported, did you mean `#[{} = ...]`?", + id + ), + ))), + _ => None, + }) + } } }) .collect()