freya_router_macro/
redirect.rs

1use proc_macro2::{
2    Ident,
3    TokenStream,
4};
5use quote::{
6    format_ident,
7    quote,
8};
9use syn::LitStr;
10
11use crate::{
12    hash::HashFragment,
13    nest::NestId,
14    query::QuerySegment,
15    segment::{
16        RouteSegment,
17        create_error_type,
18        parse_route_segments,
19    },
20};
21
22#[derive(Debug)]
23pub(crate) struct Redirect {
24    pub route: LitStr,
25    pub nests: Vec<NestId>,
26    pub segments: Vec<RouteSegment>,
27    pub query: Option<QuerySegment>,
28    pub hash: Option<HashFragment>,
29    pub function: syn::ExprClosure,
30    pub index: usize,
31}
32
33impl Redirect {
34    pub fn error_ident(&self) -> Ident {
35        format_ident!("Redirect{}ParseError", self.index)
36    }
37
38    pub fn error_variant(&self) -> Ident {
39        format_ident!("Redirect{}", self.index)
40    }
41
42    pub fn error_type(&self) -> TokenStream {
43        let error_name = self.error_ident();
44
45        create_error_type(&self.route.value(), error_name, &self.segments, None)
46    }
47
48    pub fn parse_query(&self) -> TokenStream {
49        match &self.query {
50            Some(query) => query.parse(),
51            None => quote! {},
52        }
53    }
54
55    pub fn parse_hash(&self) -> TokenStream {
56        match &self.hash {
57            Some(hash) => hash.parse(),
58            None => quote! {},
59        }
60    }
61
62    pub fn parse(
63        input: syn::parse::ParseStream,
64        active_nests: Vec<NestId>,
65        index: usize,
66    ) -> syn::Result<Self> {
67        let path = input.parse::<syn::LitStr>()?;
68
69        let _ = input.parse::<syn::Token![,]>();
70        let function = input.parse::<syn::ExprClosure>()?;
71
72        let mut closure_arguments = Vec::new();
73        for arg in function.inputs.iter() {
74            match arg {
75                syn::Pat::Type(pat) => match &*pat.pat {
76                    syn::Pat::Ident(ident) => {
77                        closure_arguments.push((ident.ident.clone(), (*pat.ty).clone()));
78                    }
79                    _ => {
80                        return Err(syn::Error::new_spanned(
81                            arg,
82                            "Expected closure argument to be a typed pattern",
83                        ));
84                    }
85                },
86                _ => {
87                    return Err(syn::Error::new_spanned(
88                        arg,
89                        "Expected closure argument to be a typed pattern",
90                    ));
91                }
92            }
93        }
94
95        let (segments, query, hash) = parse_route_segments(
96            path.span(),
97            #[allow(clippy::map_identity)]
98            closure_arguments.iter().map(|(name, ty)| (name, ty)),
99            &path.value(),
100        )?;
101
102        Ok(Redirect {
103            route: path,
104            nests: active_nests,
105            segments,
106            query,
107            hash,
108            function,
109            index,
110        })
111    }
112}