#![allow(dead_code)] use proc_macro2::TokenStream; use quote::quote; use syn::parse::{Parse, ParseStream}; use syn::punctuated::Punctuated; use syn::spanned::Spanned; use syn::{Data, DeriveInput, Fields, Ident, LitStr, Variant}; const AT_ATTR_IDENT: &str = "at"; const NOT_FOUND_ATTR_IDENT: &str = "not_found"; pub struct Routable { ident: Ident, ats: Vec, variants: Punctuated, not_found_route: Option, } impl Parse for Routable { fn parse(input: ParseStream) -> syn::Result { let DeriveInput { ident, data, .. } = input.parse()?; let data = match data { Data::Enum(data) => data, Data::Struct(s) => { return Err(syn::Error::new( s.struct_token.span(), "expected enum, found struct", )) } Data::Union(u) => { return Err(syn::Error::new( u.union_token.span(), "expected enum, found union", )) } }; let (not_found_route, ats) = parse_variants_attributes(&data.variants)?; Ok(Self { ident, variants: data.variants, ats, not_found_route, }) } } fn parse_variants_attributes( variants: &Punctuated, ) -> syn::Result<(Option, Vec)> { let mut not_founds = vec![]; let mut ats: Vec = vec![]; let mut not_found_attrs = vec![]; for variant in variants.iter() { if let Fields::Unnamed(ref field) = variant.fields { return Err(syn::Error::new( field.span(), "only named fields are supported", )); } let attrs = &variant.attrs; let at_attrs = attrs .iter() .filter(|attr| attr.path.is_ident(AT_ATTR_IDENT)) .collect::>(); let attr = match at_attrs.len() { 1 => *at_attrs.first().unwrap(), 0 => { return Err(syn::Error::new( variant.span(), format!( "{} attribute must be present on every variant", AT_ATTR_IDENT ), )) } _ => { return Err(syn::Error::new_spanned( quote! { #(#at_attrs)* }, format!("only one {} attribute must be present", AT_ATTR_IDENT), )) } }; let lit = attr.parse_args::()?; ats.push(lit); for attr in attrs.iter() { if attr.path.is_ident(NOT_FOUND_ATTR_IDENT) { not_found_attrs.push(attr); not_founds.push(variant.ident.clone()) } } } if not_founds.len() > 1 { return Err(syn::Error::new_spanned( quote! { #(#not_found_attrs)* }, format!("there can only be one {}", NOT_FOUND_ATTR_IDENT), )); } Ok((not_founds.into_iter().next(), ats)) } impl Routable { // fn build_from_path(&self) -> TokenStream { // let from_path_matches = self.variants.iter().enumerate().map(|(i, variant)| { // let ident = &variant.ident; // let right = match &variant.fields { // Fields::Unit => quote! { Self::#ident }, // Fields::Named(field) => { // let fields = field.named.iter().map(|it| { // //named fields have idents // it.ident.as_ref().unwrap() // }); // quote! { Self::#ident { #(#fields: params.get(stringify!(#fields))?.parse().ok()?,)* } } // } // Fields::Unnamed(_) => unreachable!(), // already checked // }; // let left = self.ats.get(i).unwrap(); // quote! { // #left => ::std::option::Option::Some(#right) // } // }); // quote! { // fn from_path(path: &str, params: &::std::collections::HashMap<&str, &str>) -> ::std::option::Option { // match path { // #(#from_path_matches),*, // _ => ::std::option::Option::None, // } // } // } // } // fn build_to_path(&self) -> TokenStream { // let to_path_matches = self.variants.iter().enumerate().map(|(i, variant)| { // let ident = &variant.ident; // let mut right = self.ats.get(i).unwrap().value(); // match &variant.fields { // Fields::Unit => quote! { Self::#ident => ::std::string::ToString::to_string(#right) }, // Fields::Named(field) => { // let fields = field // .named // .iter() // .map(|it| it.ident.as_ref().unwrap()) // .collect::>(); // for field in fields.iter() { // // :param -> {param} // // so we can pass it to `format!("...", param)` // right = right.replace(&format!(":{}", field), &format!("{{{}}}", field)) // } // quote! { // Self::#ident { #(#fields),* } => ::std::format!(#right, #(#fields = #fields),*) // } // } // Fields::Unnamed(_) => unreachable!(), // already checked // } // }); // quote! { // fn to_path(&self) -> ::std::string::String { // match self { // #(#to_path_matches),*, // } // } // } // } } pub fn routable_derive_impl(input: Routable) -> TokenStream { let Routable { // ats, // not_found_route, // ident, .. } = &input; // let from_path = input.build_from_path(); // let to_path = input.build_to_path(); quote! { // #[automatically_derived] // impl ::dioxus::router::Routable for #ident { // fn recognize(pathname: &str) -> ::std::option::Option { // todo!() // // ::std::thread_local! { // // static ROUTER: ::dioxus::router::__macro::Router = ::dioxus::router::__macro::build_router::<#ident>(); // // } // // let route = ROUTER.with(|router| ::dioxus::router::__macro::recognize_with_router(router, pathname)); // // { // // let route = ::std::clone::Clone::clone(&route); // // #cache_thread_local_ident.with(move |val| { // // *val.borrow_mut() = route; // // }); // // } // // route // } // } } }