use darling::FromDeriveInput; use proc_macro::TokenStream; use proc_macro2::{Span as Span2, TokenStream as TokenStream2}; use quote::{ToTokens, quote}; use syn::{Ident, LitStr, Path, parse_quote, parse2}; #[derive(FromDeriveInput)] #[darling(supports(struct_unit))] #[darling(attributes(string_literal))] struct StringLiteral { ident: Ident, value: String, } impl ToTokens for StringLiteral { fn to_tokens(&self, tokens: &mut TokenStream2) { let Self { ident, value } = self; dbg!("reached this point"); let as_ref_trait: Path = parse_quote!(::core::convert::AsRef); let display_trait: Path = parse_quote!(::std::fmt::Display); let from_trait: Path = parse_quote!(::core::convert::From); let from_str_trait: Path = parse_quote!(::std::str::FromStr); let result: Path = parse_quote!(::core::result::Result); let wrong_literal_error_generic: Path = parse_quote!(::string_literal::WrongLiteralError); let wrong_literal_error: Path = parse_quote!(#wrong_literal_error_generic<#ident>); let write_macro: Path = parse_quote!(::std::write); let value_literal = LitStr::new(value.as_str(), Span2::call_site()); dbg!("reached this point"); let output = quote! { impl #from_trait<#ident> for &'static str { fn from(unit: #ident) -> Self { #value_literal } } impl #from_str_trait for #ident { type Err = #wrong_literal_error; fn from_str(s: &str) -> #result { if s == #value_literal { Ok(Self) } else { Err(#wrong_literal_error_generic { actual: s.to_owned(), expected: #ident }) } } } impl #display_trait for #ident { fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { #write_macro!(f, "state_changed") } } impl #as_ref_trait for #ident { fn as_ref(&self) -> &str { #value_literal } } }; dbg!(&output.to_string()); tokens.extend(output); } } fn string_literal_impl(input: TokenStream2) -> TokenStream2 { let derive_input = match parse2(input) { Ok(derive_input) => derive_input, Err(error) => return error.into_compile_error(), }; let string_literal = match StringLiteral::from_derive_input(&derive_input) { Ok(string_literal) => string_literal, Err(error) => return error.write_errors(), }; quote! { #string_literal } } #[proc_macro_derive(StringLiteral, attributes(string_literal))] pub fn string_literal(input: TokenStream) -> TokenStream { string_literal_impl(input.into()).into() }