route.rs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368
  1. use quote::{format_ident, quote};
  2. use syn::parse::Parse;
  3. use syn::parse::ParseStream;
  4. use syn::parse_quote;
  5. use syn::Field;
  6. use syn::Path;
  7. use syn::Type;
  8. use syn::{Ident, LitStr};
  9. use proc_macro2::TokenStream as TokenStream2;
  10. use crate::layout::Layout;
  11. use crate::layout::LayoutId;
  12. use crate::nest::Nest;
  13. use crate::nest::NestId;
  14. use crate::query::QuerySegment;
  15. use crate::segment::create_error_type;
  16. use crate::segment::parse_route_segments;
  17. use crate::segment::RouteSegment;
  18. struct RouteArgs {
  19. route: LitStr,
  20. comp_name: Option<Path>,
  21. props_name: Option<Path>,
  22. }
  23. impl Parse for RouteArgs {
  24. fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
  25. let route = input.parse::<LitStr>()?;
  26. Ok(RouteArgs {
  27. route,
  28. comp_name: {
  29. let _ = input.parse::<syn::Token![,]>();
  30. input.parse().ok()
  31. },
  32. props_name: {
  33. let _ = input.parse::<syn::Token![,]>();
  34. input.parse().ok()
  35. },
  36. })
  37. }
  38. }
  39. struct ChildArgs {
  40. route: LitStr,
  41. }
  42. impl Parse for ChildArgs {
  43. fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
  44. let route = input.parse::<LitStr>()?;
  45. Ok(ChildArgs { route })
  46. }
  47. }
  48. #[derive(Debug)]
  49. pub(crate) struct Route {
  50. pub route_name: Ident,
  51. pub ty: RouteType,
  52. pub route: String,
  53. pub segments: Vec<RouteSegment>,
  54. pub query: Option<QuerySegment>,
  55. pub nests: Vec<NestId>,
  56. pub layouts: Vec<LayoutId>,
  57. fields: Vec<(Ident, Type)>,
  58. }
  59. impl Route {
  60. pub fn parse(
  61. nests: Vec<NestId>,
  62. layouts: Vec<LayoutId>,
  63. variant: syn::Variant,
  64. ) -> syn::Result<Self> {
  65. let route_attr = variant
  66. .attrs
  67. .iter()
  68. .find(|attr| attr.path.is_ident("route"));
  69. let route;
  70. let ty;
  71. let route_name = variant.ident.clone();
  72. match route_attr {
  73. Some(attr) => {
  74. let args = attr.parse_args::<RouteArgs>()?;
  75. let comp_name = args.comp_name.unwrap_or_else(|| parse_quote!(#route_name));
  76. let props_name = args.props_name.unwrap_or_else(|| {
  77. let last = format_ident!(
  78. "{}Props",
  79. comp_name.segments.last().unwrap().ident.to_string()
  80. );
  81. let mut segments = comp_name.segments.clone();
  82. segments.pop();
  83. segments.push(last.into());
  84. Path {
  85. leading_colon: None,
  86. segments,
  87. }
  88. });
  89. ty = RouteType::Leaf {
  90. component: comp_name,
  91. props: props_name,
  92. };
  93. route = args.route.value();
  94. }
  95. None => {
  96. if let Some(route_attr) = variant
  97. .attrs
  98. .iter()
  99. .find(|attr| attr.path.is_ident("child"))
  100. {
  101. let args = route_attr.parse_args::<ChildArgs>()?;
  102. route = args.route.value();
  103. match &variant.fields {
  104. syn::Fields::Named(fields) => {
  105. // find either a field with #[child] or a field named "child"
  106. let child_field = fields.named.iter().find(|f| {
  107. f.attrs
  108. .iter()
  109. .any(|attr| attr.path.is_ident("child"))
  110. || *f.ident.as_ref().unwrap() == "child"
  111. });
  112. match child_field{
  113. Some(child) => {
  114. ty = RouteType::Child(child.clone());
  115. }
  116. None => {
  117. return Err(syn::Error::new_spanned(
  118. variant.clone(),
  119. "Routable variants with a #[child(...)] attribute must have a field named \"child\" or a field with a #[child] attribute",
  120. ));
  121. }
  122. }
  123. }
  124. _ => {
  125. return Err(syn::Error::new_spanned(
  126. variant.clone(),
  127. "Routable variants with a #[child(...)] attribute must have named fields",
  128. ))
  129. }
  130. }
  131. } else {
  132. return Err(syn::Error::new_spanned(
  133. variant.clone(),
  134. "Routable variants must either have a #[route(...)] attribute or a #[child(...)] attribute",
  135. ));
  136. }
  137. }
  138. };
  139. let fields = match &variant.fields {
  140. syn::Fields::Named(fields) => fields
  141. .named
  142. .iter()
  143. .filter_map(|f| {
  144. if let RouteType::Child(child) = &ty {
  145. if f.ident == child.ident {
  146. return None;
  147. }
  148. }
  149. Some((f.ident.clone().unwrap(), f.ty.clone()))
  150. })
  151. .collect(),
  152. _ => Vec::new(),
  153. };
  154. let (route_segments, query) = {
  155. parse_route_segments(
  156. variant.ident.span(),
  157. fields.iter().map(|f| (&f.0, &f.1)),
  158. &route,
  159. )?
  160. };
  161. Ok(Self {
  162. ty,
  163. route_name,
  164. segments: route_segments,
  165. route,
  166. query,
  167. nests,
  168. layouts,
  169. fields,
  170. })
  171. }
  172. pub fn display_match(&self, nests: &[Nest]) -> TokenStream2 {
  173. let name = &self.route_name;
  174. let dynamic_segments = self.dynamic_segments();
  175. let write_query = self.query.as_ref().map(|q| q.write());
  176. match &self.ty {
  177. RouteType::Child(field) => {
  178. let write_nests = self.nests.iter().map(|id| nests[id.0].write());
  179. let write_segments = self.segments.iter().map(|s| s.write_segment());
  180. let child = field.ident.as_ref().unwrap();
  181. quote! {
  182. Self::#name { #(#dynamic_segments,)* #child } => {
  183. use std::fmt::Display;
  184. use std::fmt::Write;
  185. let mut route = String::new();
  186. {
  187. let f = &mut route;
  188. #(#write_nests)*
  189. #(#write_segments)*
  190. }
  191. if route.ends_with('/') {
  192. route.pop();
  193. }
  194. f.write_str(&route)?;
  195. #child.fmt(f)?;
  196. }
  197. }
  198. }
  199. RouteType::Leaf { .. } => {
  200. let write_nests = self.nests.iter().map(|id| nests[id.0].write());
  201. let write_segments = self.segments.iter().map(|s| s.write_segment());
  202. quote! {
  203. Self::#name { #(#dynamic_segments,)* } => {
  204. #(#write_nests)*
  205. #(#write_segments)*
  206. #write_query
  207. }
  208. }
  209. }
  210. }
  211. }
  212. pub fn routable_match(&self, layouts: &[Layout], nests: &[Nest]) -> TokenStream2 {
  213. let name = &self.route_name;
  214. let name_str = name.to_string();
  215. let mut tokens = TokenStream2::new();
  216. // First match all layouts
  217. for (idx, layout_id) in self.layouts.iter().copied().enumerate() {
  218. let render_layout = layouts[layout_id.0].routable_match(nests);
  219. let dynamic_segments = self.dynamic_segments();
  220. let mut field_name = None;
  221. if let RouteType::Child(field) = &self.ty {
  222. field_name = field.ident.as_ref();
  223. }
  224. let field_name = field_name.map(|f| quote!(#f,));
  225. // This is a layout
  226. tokens.extend(quote! {
  227. #[allow(unused)]
  228. (#idx, Self::#name { #(#dynamic_segments,)* #field_name .. }) => {
  229. #render_layout
  230. }
  231. });
  232. }
  233. // Then match the route
  234. let last_index = self.layouts.len();
  235. tokens.extend(match &self.ty {
  236. RouteType::Child(field) => {
  237. let field_name = field.ident.as_ref().unwrap();
  238. quote! {
  239. #[allow(unused)]
  240. (#last_index.., Self::#name { #field_name, .. }) => {
  241. #field_name.render(cx, level - #last_index)
  242. }
  243. }
  244. }
  245. RouteType::Leaf { component, props } => {
  246. let dynamic_segments = self.dynamic_segments();
  247. let dynamic_segments_from_route = self.dynamic_segments();
  248. quote! {
  249. #[allow(unused)]
  250. (#last_index, Self::#name { #(#dynamic_segments,)* }) => {
  251. let comp = #props { #(#dynamic_segments_from_route,)* };
  252. let dynamic = cx.component(#component, comp, #name_str);
  253. render! {
  254. dynamic
  255. }
  256. }
  257. }
  258. }
  259. });
  260. tokens
  261. }
  262. fn dynamic_segments(&self) -> impl Iterator<Item = TokenStream2> + '_ {
  263. self.fields.iter().map(|(name, _)| {
  264. quote! {#name}
  265. })
  266. }
  267. pub fn construct(&self, nests: &[Nest], enum_name: Ident) -> TokenStream2 {
  268. let segments = self.fields.iter().map(|(name, _)| {
  269. let mut from_route = false;
  270. for id in &self.nests {
  271. let nest = &nests[id.0];
  272. if nest.dynamic_segments_names().any(|i| &i == name) {
  273. from_route = true
  274. }
  275. }
  276. for segment in &self.segments {
  277. if let RouteSegment::Dynamic(other, ..) = segment {
  278. if other == name {
  279. from_route = true
  280. }
  281. }
  282. }
  283. if let Some(query) = &self.query {
  284. if &query.ident == name {
  285. from_route = true
  286. }
  287. }
  288. if from_route {
  289. quote! {#name}
  290. } else {
  291. quote! {#name: Default::default()}
  292. }
  293. });
  294. match &self.ty {
  295. RouteType::Child(field) => {
  296. let name = &self.route_name;
  297. let child_name = field.ident.as_ref().unwrap();
  298. quote! {
  299. #enum_name::#name {
  300. #child_name,
  301. #(#segments,)*
  302. }
  303. }
  304. }
  305. RouteType::Leaf { .. } => {
  306. let name = &self.route_name;
  307. quote! {
  308. #enum_name::#name {
  309. #(#segments,)*
  310. }
  311. }
  312. }
  313. }
  314. }
  315. pub fn error_ident(&self) -> Ident {
  316. format_ident!("{}ParseError", self.route_name)
  317. }
  318. pub fn error_type(&self) -> TokenStream2 {
  319. let error_name = self.error_ident();
  320. let child_type = match &self.ty {
  321. RouteType::Child(field) => Some(&field.ty),
  322. RouteType::Leaf { .. } => None,
  323. };
  324. create_error_type(error_name, &self.segments, child_type)
  325. }
  326. pub fn parse_query(&self) -> TokenStream2 {
  327. match &self.query {
  328. Some(query) => query.parse(),
  329. None => quote! {},
  330. }
  331. }
  332. }
  333. #[derive(Debug)]
  334. pub(crate) enum RouteType {
  335. Child(Field),
  336. Leaf { component: Path, props: Path },
  337. }