router.rs 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248
  1. use proc_macro2::TokenStream;
  2. use quote::quote;
  3. use syn::parse::{Parse, ParseStream};
  4. use syn::punctuated::Punctuated;
  5. use syn::spanned::Spanned;
  6. use syn::{Data, DeriveInput, Fields, Ident, LitStr, Variant};
  7. const AT_ATTR_IDENT: &str = "at";
  8. const NOT_FOUND_ATTR_IDENT: &str = "not_found";
  9. pub struct Routable {
  10. ident: Ident,
  11. ats: Vec<LitStr>,
  12. variants: Punctuated<Variant, syn::token::Comma>,
  13. not_found_route: Option<Ident>,
  14. }
  15. impl Parse for Routable {
  16. fn parse(input: ParseStream) -> syn::Result<Self> {
  17. let DeriveInput { ident, data, .. } = input.parse()?;
  18. let data = match data {
  19. Data::Enum(data) => data,
  20. Data::Struct(s) => {
  21. return Err(syn::Error::new(
  22. s.struct_token.span(),
  23. "expected enum, found struct",
  24. ))
  25. }
  26. Data::Union(u) => {
  27. return Err(syn::Error::new(
  28. u.union_token.span(),
  29. "expected enum, found union",
  30. ))
  31. }
  32. };
  33. let (not_found_route, ats) = parse_variants_attributes(&data.variants)?;
  34. Ok(Self {
  35. ident,
  36. variants: data.variants,
  37. ats,
  38. not_found_route,
  39. })
  40. }
  41. }
  42. fn parse_variants_attributes(
  43. variants: &Punctuated<Variant, syn::token::Comma>,
  44. ) -> syn::Result<(Option<Ident>, Vec<LitStr>)> {
  45. let mut not_founds = vec![];
  46. let mut ats: Vec<LitStr> = vec![];
  47. let mut not_found_attrs = vec![];
  48. for variant in variants.iter() {
  49. if let Fields::Unnamed(ref field) = variant.fields {
  50. return Err(syn::Error::new(
  51. field.span(),
  52. "only named fields are supported",
  53. ));
  54. }
  55. let attrs = &variant.attrs;
  56. let at_attrs = attrs
  57. .iter()
  58. .filter(|attr| attr.path.is_ident(AT_ATTR_IDENT))
  59. .collect::<Vec<_>>();
  60. let attr = match at_attrs.len() {
  61. 1 => *at_attrs.first().unwrap(),
  62. 0 => {
  63. return Err(syn::Error::new(
  64. variant.span(),
  65. format!(
  66. "{} attribute must be present on every variant",
  67. AT_ATTR_IDENT
  68. ),
  69. ))
  70. }
  71. _ => {
  72. return Err(syn::Error::new_spanned(
  73. quote! { #(#at_attrs)* },
  74. format!("only one {} attribute must be present", AT_ATTR_IDENT),
  75. ))
  76. }
  77. };
  78. let lit = attr.parse_args::<LitStr>()?;
  79. ats.push(lit);
  80. for attr in attrs.iter() {
  81. if attr.path.is_ident(NOT_FOUND_ATTR_IDENT) {
  82. not_found_attrs.push(attr);
  83. not_founds.push(variant.ident.clone())
  84. }
  85. }
  86. }
  87. if not_founds.len() > 1 {
  88. return Err(syn::Error::new_spanned(
  89. quote! { #(#not_found_attrs)* },
  90. format!("there can only be one {}", NOT_FOUND_ATTR_IDENT),
  91. ));
  92. }
  93. Ok((not_founds.into_iter().next(), ats))
  94. }
  95. impl Routable {
  96. fn build_from_path(&self) -> TokenStream {
  97. let from_path_matches = self.variants.iter().enumerate().map(|(i, variant)| {
  98. let ident = &variant.ident;
  99. let right = match &variant.fields {
  100. Fields::Unit => quote! { Self::#ident },
  101. Fields::Named(field) => {
  102. let fields = field.named.iter().map(|it| {
  103. //named fields have idents
  104. it.ident.as_ref().unwrap()
  105. });
  106. quote! { Self::#ident { #(#fields: params.get(stringify!(#fields))?.parse().ok()?,)* } }
  107. }
  108. Fields::Unnamed(_) => unreachable!(), // already checked
  109. };
  110. let left = self.ats.get(i).unwrap();
  111. quote! {
  112. #left => ::std::option::Option::Some(#right)
  113. }
  114. });
  115. quote! {
  116. fn from_path(path: &str, params: &::std::collections::HashMap<&str, &str>) -> ::std::option::Option<Self> {
  117. match path {
  118. #(#from_path_matches),*,
  119. _ => ::std::option::Option::None,
  120. }
  121. }
  122. }
  123. }
  124. fn build_to_path(&self) -> TokenStream {
  125. let to_path_matches = self.variants.iter().enumerate().map(|(i, variant)| {
  126. let ident = &variant.ident;
  127. let mut right = self.ats.get(i).unwrap().value();
  128. match &variant.fields {
  129. Fields::Unit => quote! { Self::#ident => ::std::string::ToString::to_string(#right) },
  130. Fields::Named(field) => {
  131. let fields = field
  132. .named
  133. .iter()
  134. .map(|it| it.ident.as_ref().unwrap())
  135. .collect::<Vec<_>>();
  136. for field in fields.iter() {
  137. // :param -> {param}
  138. // so we can pass it to `format!("...", param)`
  139. right = right.replace(&format!(":{}", field), &format!("{{{}}}", field))
  140. }
  141. quote! {
  142. Self::#ident { #(#fields),* } => ::std::format!(#right, #(#fields = #fields),*)
  143. }
  144. }
  145. Fields::Unnamed(_) => unreachable!(), // already checked
  146. }
  147. });
  148. quote! {
  149. fn to_path(&self) -> ::std::string::String {
  150. match self {
  151. #(#to_path_matches),*,
  152. }
  153. }
  154. }
  155. }
  156. }
  157. pub fn routable_derive_impl(input: Routable) -> TokenStream {
  158. let Routable {
  159. ats,
  160. not_found_route,
  161. ident,
  162. ..
  163. } = &input;
  164. let from_path = input.build_from_path();
  165. let to_path = input.build_to_path();
  166. let not_found_route = match not_found_route {
  167. Some(route) => quote! { ::std::option::Option::Some(Self::#route) },
  168. None => quote! { ::std::option::Option::None },
  169. };
  170. let cache_thread_local_ident = Ident::new(
  171. &format!("__{}_ROUTER_CURRENT_ROUTE_CACHE", ident),
  172. ident.span(),
  173. );
  174. quote! {
  175. // ::std::thread_local! {
  176. // #[doc(hidden)]
  177. // #[allow(non_upper_case_globals)]
  178. // static #cache_thread_local_ident: ::std::cell::RefCell<::std::option::Option<#ident>> = ::std::cell::RefCell::new(::std::option::Option::None);
  179. // }
  180. #[automatically_derived]
  181. impl ::dioxus::router::Routable for #ident {
  182. #from_path
  183. #to_path
  184. fn routes() -> ::std::vec::Vec<&'static str> {
  185. ::std::vec![#(#ats),*]
  186. }
  187. fn not_found_route() -> ::std::option::Option<Self> {
  188. #not_found_route
  189. }
  190. // fn current_route() -> ::std::option::Option<Self> {
  191. // #cache_thread_local_ident.with(|val| ::std::clone::Clone::clone(&*val.borrow()))
  192. // }
  193. fn recognize(pathname: &str) -> ::std::option::Option<Self> {
  194. todo!()
  195. // ::std::thread_local! {
  196. // static ROUTER: ::dioxus::router::__macro::Router = ::dioxus::router::__macro::build_router::<#ident>();
  197. // }
  198. // let route = ROUTER.with(|router| ::dioxus::router::__macro::recognize_with_router(router, pathname));
  199. // {
  200. // let route = ::std::clone::Clone::clone(&route);
  201. // #cache_thread_local_ident.with(move |val| {
  202. // *val.borrow_mut() = route;
  203. // });
  204. // }
  205. // route
  206. }
  207. // fn cleanup() {
  208. // #cache_thread_local_ident.with(move |val| {
  209. // *val.borrow_mut() = ::std::option::Option::None;
  210. // });
  211. // }
  212. }
  213. }
  214. }