Skip to content
This repository was archived by the owner on Jul 19, 2020. It is now read-only.
Merged
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
7 changes: 6 additions & 1 deletion crates/yew_router_macro/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
extern crate proc_macro;
use proc_macro::TokenStream;
use syn::{parse_macro_input, DeriveInput};

mod switch;

Expand Down Expand Up @@ -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]
Expand Down
25 changes: 10 additions & 15 deletions crates/yew_router_macro/src/switch.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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<TokenStream> {
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))
Expand All @@ -59,7 +56,6 @@ pub fn switch_impl(input: TokenStream) -> TokenStream {
},
}
.to_token_stream()
.into()
}
Data::Enum(de) => {
let switch_variants = de
Expand All @@ -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::<Vec<_>>();
SwitchItem {
Ok(SwitchItem {
matcher,
ident: variant.ident,
fields: variant.fields,
}
})
})
.collect::<Vec<SwitchItem>>();
.collect::<syn::Result<Vec<_>>>()?;


SwitchImpl {
Expand All @@ -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<T> {
Expand Down
75 changes: 42 additions & 33 deletions crates/yew_router_macro/src/switch/attribute.rs
Original file line number Diff line number Diff line change
@@ -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 {
Expand All @@ -9,45 +9,54 @@ pub enum AttrToken {
}

impl AttrToken {
pub fn convert_attributes_to_tokens(attributes: Vec<Attribute>) -> Vec<Self> {
fn get_meta_name_value_str(mnv: &MetaNameValue) -> Option<String> {
pub fn convert_attributes_to_tokens(attributes: Vec<Attribute>) -> syn::Result<Vec<Self>> {
fn get_meta_name_value_str(mnv: &MetaNameValue) -> syn::Result<String> {
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")),
}
}

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(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"),
))),
_ => None,
})
.next(),
Meta::Path(path) => path
.get_ident()
.into_iter()
.filter_map(|ident| match ident.to_string().as_str() {
"end" => Some(AttrToken::End),
"rest" => Some(AttrToken::Rest(None)),
_ => None,
})
.next(),
_ => None,
.filter_map(|meta: Meta| {
let meta_span = meta.span();
match meta {
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,
})
}
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()
}
Expand Down