From 3f94015b4f1cd70ac3f659fcfbfac31bb41f0735 Mon Sep 17 00:00:00 2001 From: J / Jacob Babich Date: Sun, 12 Jul 2026 13:54:57 -0400 Subject: [PATCH] chore: remove `AsRef` impl from the `StringLiteral` macro since I don't like that behavior, reorganize it to prevent leaking `proc-macro2` / `syn` / `quote` --- Cargo.lock | 7 +++ Cargo.toml | 1 + string-literal-macros-impl/Cargo.toml | 11 ++++ string-literal-macros-impl/src/lib.rs | 71 ++++++++++++++++++++++ string-literal-macros/Cargo.toml | 5 +- string-literal-macros/src/lib.rs | 84 +-------------------------- string-literal/src/lib.rs | 4 +- 7 files changed, 94 insertions(+), 89 deletions(-) create mode 100644 string-literal-macros-impl/Cargo.toml create mode 100644 string-literal-macros-impl/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index ed2a45b..22465ac 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2230,6 +2230,13 @@ dependencies = [ [[package]] name = "string-literal-macros" version = "0.1.0" +dependencies = [ + "string-literal-macros-impl", +] + +[[package]] +name = "string-literal-macros-impl" +version = "0.1.0" dependencies = [ "darling", "proc-macro2", diff --git a/Cargo.toml b/Cargo.toml index 67f84ff..49c2263 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,6 +12,7 @@ members = [ "python-utils-macros-impl", "string-literal", "string-literal-macros", + "string-literal-macros-impl", ] resolver = "3" diff --git a/string-literal-macros-impl/Cargo.toml b/string-literal-macros-impl/Cargo.toml new file mode 100644 index 0000000..6ec6f8f --- /dev/null +++ b/string-literal-macros-impl/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "string-literal-macros-impl" +version = "0.1.0" +edition = "2024" +license.workspace = true + +[dependencies] +darling = "0.23.0" +proc-macro2 = { workspace = true } +quote = { workspace = true } +syn = { workspace = true } diff --git a/string-literal-macros-impl/src/lib.rs b/string-literal-macros-impl/src/lib.rs new file mode 100644 index 0000000..c8aeb6a --- /dev/null +++ b/string-literal-macros-impl/src/lib.rs @@ -0,0 +1,71 @@ +use darling::FromDeriveInput; +use proc_macro2::{Span, TokenStream}; +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 TokenStream) { + let Self { ident, value } = self; + + 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(), Span::call_site()); + + 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") + } + } + }; + dbg!(&output.to_string()); + + tokens.extend(output); + } +} + +pub fn string_literal_impl(input: TokenStream) -> TokenStream { + 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 } +} diff --git a/string-literal-macros/Cargo.toml b/string-literal-macros/Cargo.toml index 125022c..684c502 100644 --- a/string-literal-macros/Cargo.toml +++ b/string-literal-macros/Cargo.toml @@ -8,7 +8,4 @@ license.workspace = true proc-macro = true [dependencies] -darling = "0.23.0" -proc-macro2 = { workspace = true } -quote = { workspace = true } -syn = { workspace = true } +string-literal-macros-impl = { path = "../string-literal-macros-impl" } diff --git a/string-literal-macros/src/lib.rs b/string-literal-macros/src/lib.rs index 1bd0fe9..1433118 100644 --- a/string-literal-macros/src/lib.rs +++ b/string-literal-macros/src/lib.rs @@ -1,88 +1,6 @@ -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() + string_literal_macros_impl::string_literal_impl(input.into()).into() } diff --git a/string-literal/src/lib.rs b/string-literal/src/lib.rs index 4968698..fa9b2b3 100644 --- a/string-literal/src/lib.rs +++ b/string-literal/src/lib.rs @@ -11,11 +11,11 @@ pub struct WrongLiteralError { impl Display for WrongLiteralError where - S: AsRef, + &'static str: for<'a> From<&'a S>, { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let Self { actual, expected } = self; - let expected_str = expected.as_ref(); + let expected_str = <&'static str>::from(&expected); write!(f, "expected {expected_str:?} but got {actual:?}") }