lib.rs 2.3 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576
  1. use proc_macro::TokenStream;
  2. use convert_case::{Case, Casing};
  3. use quote::{quote, ToTokens, TokenStreamExt};
  4. use syn::__private::TokenStream2;
  5. use syn::parse::{Parse, ParseStream};
  6. use syn::punctuated::Punctuated;
  7. use syn::{braced, parse_macro_input, Ident, Token};
  8. #[proc_macro]
  9. pub fn impl_extension_attributes(input: TokenStream) -> TokenStream {
  10. let input = parse_macro_input!(input as ImplExtensionAttributes);
  11. input.to_token_stream().into()
  12. }
  13. struct ImplExtensionAttributes {
  14. is_element: bool,
  15. name: Ident,
  16. attrs: Punctuated<Ident, Token![,]>,
  17. }
  18. impl Parse for ImplExtensionAttributes {
  19. fn parse(input: ParseStream) -> syn::Result<Self> {
  20. let content;
  21. let element: Ident = input.parse()?;
  22. let name = input.parse()?;
  23. braced!(content in input);
  24. let attrs = content.parse_terminated(Ident::parse, Token![,])?;
  25. Ok(ImplExtensionAttributes {
  26. is_element: element.to_string() == "ELEMENT",
  27. name,
  28. attrs,
  29. })
  30. }
  31. }
  32. impl ToTokens for ImplExtensionAttributes {
  33. fn to_tokens(&self, tokens: &mut TokenStream2) {
  34. let name = &self.name;
  35. let name_string = name.to_string();
  36. let camel_name = name_string
  37. .strip_prefix("r#")
  38. .unwrap_or(&name_string)
  39. .to_case(Case::UpperCamel);
  40. let impl_name = Ident::new(format!("{}Impl", &camel_name).as_str(), name.span());
  41. let extension_name = Ident::new(format!("{}Extension", &camel_name).as_str(), name.span());
  42. if !self.is_element {
  43. tokens.append_all(quote! {
  44. struct #impl_name;
  45. impl #name for #impl_name {}
  46. });
  47. }
  48. let impls = self.attrs.iter().map(|ident| {
  49. let d = if self.is_element {
  50. quote! { #name::#ident }
  51. } else {
  52. quote! { <#impl_name as #name>::#ident }
  53. };
  54. quote! {
  55. fn #ident(self, value: impl IntoAttributeValue<'a>) -> Self {
  56. let d = #d;
  57. self.push_attribute(d.0, d.1, value, d.2)
  58. }
  59. }
  60. });
  61. tokens.append_all(quote! {
  62. pub trait #extension_name<'a>: HasAttributes<'a> + Sized {
  63. #(#impls)*
  64. }
  65. });
  66. }
  67. }