Skip to content
This repository has been archived by the owner on Jul 19, 2020. It is now read-only.

Commit

Permalink
Add unnamed matches for unnamed structs and variants. (#155)
Browse files Browse the repository at this point in the history
* add variants to support unnamed variants

* cargo fmt

* add plumbing for field_type

* more plumbing field_type for capture and capture_single

* create unnamed module for capture parsers

* implement field-type sensitive capturing for capture() and single_capture()

* enable empty capture sections

* remove dead code

* make things private that should be private

* update example code

* update doc comment for switch derive

* cargo fmt
  • Loading branch information
hgzimmerman authored Oct 25, 2019
1 parent b4a6475 commit 2bdb609
Show file tree
Hide file tree
Showing 13 changed files with 510 additions and 287 deletions.
23 changes: 13 additions & 10 deletions crates/yew_router_macro/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,9 @@ mod switch;
/// `{1:field_name}` is the same as `{field_name}`.
///
/// Tuple-structs and Tuple-enum-variants are also supported.
/// At the moment, dummy field-names still need to be provided to capture sections, but in the future,
/// `{}`, `{*}`, and `{4}` will be valid matchers when used on structs and variants without named fields.
/// If you don't want to specify keys that don't correspond to any specific field,
/// `{}`, `{*}`, and `{4}` also denote valid capture sections when used on structs and variants without named fields.
/// In datastructures without field names, the captures will be assigned in order - left to right.
///
/// # Note
/// It should be mentioned that the derived function for matching will try enum variants in order,
Expand Down Expand Up @@ -55,21 +56,23 @@ mod switch;
///
/// #[derive(Switch)]
/// enum AppRoute {
/// #[to="/some/simple/route"]
/// #[to = "/some/simple/route"]
/// SomeSimpleRoute,
/// #[to="/capture/{cap}"]
/// #[to = "/capture/{}"]
/// Capture(String),
/// #[to="/convert/{id}"]
/// Convert{id: usize},
/// #[rest] // #[to="{*:rest}"] would work just as well here
/// Inner(InnerRoute)
/// #[to = "/named/capture/{name}"]
/// NamedCapture { name: String },
/// #[to = "/convert/{id}"]
/// Convert { id: usize },
/// #[rest] // shorthand for #[to="{*}"]
/// Inner(InnerRoute),
/// }
///
/// #[derive(Switch)]
/// #[to="/inner/route/{first}/{second}"]
/// #[to = "/inner/route/{first}/{second}"]
/// struct InnerRoute {
/// first: String,
/// second: String
/// second: String,
/// }
/// ```
/// Check out the examples directory in the repository to see some more usages of the routing syntax.
Expand Down
33 changes: 22 additions & 11 deletions crates/yew_router_macro/src/switch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,14 @@ pub fn switch_impl(input: TokenStream) -> TokenStream {

match input.data {
Data::Struct(ds) => {
let field_type = match ds.fields {
Fields::Unnamed(_) | Fields::Unit => yew_router_route_parser::FieldType::Unnamed,
Fields::Named(_) => yew_router_route_parser::FieldType::Named,
};
let matcher = AttrToken::convert_attributes_to_tokens(input.attrs)
.into_iter()
.enumerate()
.map(|(index, at)| at.into_shadow_matcher_tokens(index))
.map(|(index, at)| at.into_shadow_matcher_tokens(index, field_type))
.flatten()
.collect::<Vec<_>>();
let switch_item = SwitchItem {
Expand All @@ -47,10 +51,16 @@ pub fn switch_impl(input: TokenStream) -> TokenStream {
.variants
.into_iter()
.map(|variant: Variant| {
let field_type = match variant.fields {
Fields::Unnamed(_) | Fields::Unit => {
yew_router_route_parser::FieldType::Unnamed
}
Fields::Named(_) => yew_router_route_parser::FieldType::Named,
};
let matcher = AttrToken::convert_attributes_to_tokens(variant.attrs)
.into_iter()
.enumerate()
.map(|(index, at)| at.into_shadow_matcher_tokens(index))
.map(|(index, at)| at.into_shadow_matcher_tokens(index, field_type))
.flatten()
.collect::<Vec<_>>();
SwitchItem {
Expand Down Expand Up @@ -120,17 +130,18 @@ fn write_for_token(token: &ShadowMatcherToken, naming_scheme: FieldType) -> Toke
state = state.or(#name.build_route_section(buf));
}
}
},
FieldType::Unnamed { index } => match &capture {
ShadowCaptureVariant::Named(_)
| ShadowCaptureVariant::ManyNamed(_)
| ShadowCaptureVariant::NumberedNamed { .. } => {
let name = unnamed_field_index_item(index);
quote! {
state = state.or(#name.build_route_section(&mut buf));
}
ShadowCaptureVariant::Unnamed
| ShadowCaptureVariant::ManyUnnamed
| ShadowCaptureVariant::NumberedUnnamed { .. } => {
panic!("Unnamed matcher sections not allowed for named field types")
}
},
FieldType::Unnamed { index } => {
let name = unnamed_field_index_item(index);
quote! {
state = state.or(#name.build_route_section(&mut buf));
}
}
},
ShadowMatcherToken::End => quote! {},
}
Expand Down
8 changes: 6 additions & 2 deletions crates/yew_router_macro/src/switch/attribute.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,14 @@ impl AttrToken {

/// The id is an unique identifier that allows otherwise unnamed captures to still be captured
/// with unique names.
pub fn into_shadow_matcher_tokens(self, id: usize) -> Vec<ShadowMatcherToken> {
pub fn into_shadow_matcher_tokens(
self,
id: usize,
field_type: yew_router_route_parser::FieldType,
) -> Vec<ShadowMatcherToken> {
match self {
AttrToken::To(matcher_string) => {
yew_router_route_parser::parse_str_and_optimize_tokens(&matcher_string)
yew_router_route_parser::parse_str_and_optimize_tokens(&matcher_string, field_type)
.expect("Invalid Matcher") // This is the point where users should see an error message if their matcher string has some syntax error.
.into_iter()
.map(crate::switch::shadow::ShadowMatcherToken::from)
Expand Down
21 changes: 21 additions & 0 deletions crates/yew_router_macro/src/switch/shadow.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,15 @@ pub enum ShadowMatcherToken {
}

pub enum ShadowCaptureVariant {
/// {}
Unnamed,
/// {*}
ManyUnnamed,
/// {5}
NumberedUnnamed {
/// Number of sections to match.
sections: usize,
},
/// {name} - captures a section and adds it to the map with a given name
Named(String),
/// {*:name} - captures over many sections and adds it to the map with a given name.
Expand All @@ -49,6 +58,15 @@ impl ToTokens for ShadowCaptureVariant {
ShadowCaptureVariant::NumberedNamed { sections, name } => {
quote! {::yew_router::matcher::CaptureVariant::NumberedNamed{sections: #sections, name: #name.to_string()}}
}
ShadowCaptureVariant::Unnamed => {
quote! {::yew_router::matcher::CaptureVariant::Unnamed}
}
ShadowCaptureVariant::ManyUnnamed => {
quote! {::yew_router::matcher::CaptureVariant::ManyUnnamed}
}
ShadowCaptureVariant::NumberedUnnamed { sections } => {
quote! {::yew_router::matcher::CaptureVariant::NumberedUnnamed{sections: #sections}}
}
};
ts.extend(t)
}
Expand All @@ -75,6 +93,9 @@ impl From<CaptureVariant> for ShadowCaptureVariant {
CaptureVariant::NumberedNamed { sections, name } => {
SCV::NumberedNamed { sections, name }
}
CaptureVariant::Unnamed => SCV::Unnamed,
CaptureVariant::ManyUnnamed => SCV::ManyUnnamed,
CaptureVariant::NumberedUnnamed { sections } => SCV::NumberedUnnamed { sections },
}
}
}
Loading

0 comments on commit 2bdb609

Please sign in to comment.