yew_router_nested_macro/lib.rs
1use proc_macro::TokenStream;
2use syn::{parse_macro_input, DeriveInput};
3
4mod switch;
5
6/// Implements the `Switch` trait based on attributes present on the struct or enum variants.
7///
8/// If deriving an enum, each variant should have a `#[to = ""]` attribute,
9/// and if deriving a struct, the struct itself should have a `#[to = ""]` attribute.
10///
11/// Inside the `""` you should put your **route matcher string**.
12/// At its simplest, the route matcher string will create your variant/struct if it exactly matches the browser's route.
13/// If the route in the url bar is `http://yoursite.com/some/route` and your route matcher string
14/// for an enum variant is `/some/route`, then that variant will be created when `switch()` is called with the route.
15///
16/// But the route matcher has other capabilities.
17/// If you want to capture data from the route matcher string, for example, extract an id or user name from the route,
18/// you can use `{field_name}` to capture data from the route.
19/// For example, `#[to = "/route/{id}"]` will capture the content after "/route/",
20/// and if the associated variant is defined as `Route{id: usize}`, then the string that was captured will be
21/// transformed into a `usize`.
22/// If the conversion fails, then the match won't succeed and the next variant will be tried instead.
23///
24/// There are also `{*:field_name}` and `{3:field_name}` types of capture sections that will capture
25/// _everything_, and the next 3 path sections respectively.
26/// `{1:field_name}` is the same as `{field_name}`.
27///
28/// Tuple-structs and Tuple-enum-variants are also supported.
29/// If you don't want to specify keys that don't correspond to any specific field,
30/// `{}`, `{*}`, and `{4}` also denote valid capture sections when used on structs and variants without named fields.
31/// In datastructures without field names, the captures will be assigned in order - left to right.
32///
33/// # Note
34/// It should be mentioned that the derived function for matching will try enum variants in order,
35/// from top to bottom, and that the whole route doesn't need to be matched by the route
36/// matcher string in order for the match to succeed.
37/// What is meant by this is that `[to = "/"]` will match "/", but also "/anything/else",
38/// because as soon as the "/" is satisfied, that is considered a match.
39///
40/// This can be mitigated by specifying a `!` at the end of your route to inform the matcher that if
41/// any characters are left after matching the route matcher string, the match should fail.
42/// This means that `[to = "/!"]` will match "/" and _only_ "/".
43///
44/// -----
45/// There are other attributes as well.
46/// `#[rest]`, `#[rest="field_name"]` and `#[end]` attributes exist as well.
47/// `#[rest]` and `#[rest="field_name"]` are equivalent to `{*}` and `{*:field_name}` respectively.
48/// `#[end]` is equivalent to `!`.
49/// The `#[rest]` attributes are good if you just want to delegate the whole matching of a variant to a specific
50/// wrapped struct or enum that also implements `Switch`.
51///
52/// ------
53/// # Example
54/// ```
55/// use yew_router::Switch;
56///
57/// #[derive(Switch, Clone)]
58/// enum AppRoute {
59/// #[to = "/some/simple/route"]
60/// SomeSimpleRoute,
61/// #[to = "/capture/{}"]
62/// Capture(String),
63/// #[to = "/named/capture/{name}"]
64/// NamedCapture { name: String },
65/// #[to = "/convert/{id}"]
66/// Convert { id: usize },
67/// #[rest] // shorthand for #[to="{*}"]
68/// Inner(InnerRoute),
69/// }
70///
71/// #[derive(Switch, Clone)]
72/// #[to = "/inner/route/{first}/{second}"]
73/// struct InnerRoute {
74/// first: String,
75/// second: String,
76/// }
77/// ```
78/// Check out the examples directory in the repository to see some more usages of the routing syntax.
79#[proc_macro_derive(Switch, attributes(to, rest, end))]
80pub fn switch(tokens: TokenStream) -> TokenStream {
81 let input: DeriveInput = parse_macro_input!(tokens as DeriveInput);
82
83 crate::switch::switch_impl(input)
84 .unwrap_or_else(|err| err.to_compile_error())
85 .into()
86}
87
88#[proc_macro_attribute]
89pub fn to(_: TokenStream, _: TokenStream) -> TokenStream {
90 TokenStream::new()
91}
92
93#[proc_macro_attribute]
94pub fn rest(_: TokenStream, _: TokenStream) -> TokenStream {
95 TokenStream::new()
96}
97
98#[proc_macro_attribute]
99pub fn end(_: TokenStream, _: TokenStream) -> TokenStream {
100 TokenStream::new()
101}