feat: add to_snake_case_string proc macro implementation
Co-authored-by: aider (openai/andrew/openrouter/deepseek/deepseek-chat-v3.1) <aider@aider.chat>
This commit is contained in:
12
macros/Cargo.toml
Normal file
12
macros/Cargo.toml
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
[package]
|
||||||
|
name = "to_snake_case_string"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
proc-macro = true
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
syn = { version = "2.0", features = ["full"] }
|
||||||
|
quote = "1.0"
|
||||||
|
proc-macro2 = "1.0"
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
extern crate proc_macro;
|
||||||
|
|
||||||
|
use proc_macro::TokenStream;
|
||||||
|
use quote::quote;
|
||||||
|
use syn::{parse_macro_input, Data, DeriveInput};
|
||||||
|
|
||||||
|
// First, define the trait that we want to implement.
|
||||||
|
// This could also live in a separate, non-proc-macro crate if you wanted.
|
||||||
|
pub trait ToSnakeCaseString {
|
||||||
|
fn to_snake_case(&self) -> String;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This is the derive macro function.
|
||||||
|
/// The `#[proc_macro_derive(ToSnakeCaseString)]` attribute tells the compiler
|
||||||
|
/// that this function implements the derive macro for the `ToSnakeCaseString` trait.
|
||||||
|
#[proc_macro_derive(ToSnakeCaseString)]
|
||||||
|
pub fn to_snake_case_string_derive(input: TokenStream) -> TokenStream {
|
||||||
|
// Parse the input tokens into a syntax tree
|
||||||
|
let ast = parse_macro_input!(input as DeriveInput);
|
||||||
|
|
||||||
|
// Get the name of the enum we're deriving for
|
||||||
|
let name = &ast.ident;
|
||||||
|
|
||||||
|
// Ensure we're working with an enum
|
||||||
|
let data_enum = match ast.data {
|
||||||
|
Data::Enum(data) => data,
|
||||||
|
_ => panic!("ToSnakeCaseString can only be derived for enums"),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Iterate over the enum variants and generate a match arm for each one
|
||||||
|
let match_arms = data_enum.variants.iter().map(|variant| {
|
||||||
|
let variant_ident = &variant.ident;
|
||||||
|
// Convert the PascalCase variant name to snake_case for the string literal
|
||||||
|
let snake_case_string = to_snake_case(variant_ident.to_string());
|
||||||
|
|
||||||
|
// Generate the code for a single match arm, e.g.,
|
||||||
|
// `Self::MyVariant => "my_variant".to_string(),`
|
||||||
|
// We ignore variant fields (e.g., `MyVariant(u32)`) by using `..`
|
||||||
|
quote! {
|
||||||
|
#name::#variant_ident { .. } => #snake_case_string.to_string(),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Generate the full implementation of the trait for the enum
|
||||||
|
let gen = quote! {
|
||||||
|
// This is the implementation of our custom trait
|
||||||
|
impl ToSnakeCaseString for #name {
|
||||||
|
// This is the implementation of the trait's method
|
||||||
|
fn to_snake_case(&self) -> String {
|
||||||
|
match self {
|
||||||
|
// Expand the generated match arms inside the match block
|
||||||
|
#( #match_arms )*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// We can also optionally implement the standard `ToString` or `Display` trait
|
||||||
|
impl std::string::ToString for #name {
|
||||||
|
fn to_string(&self) -> String {
|
||||||
|
self.to_snake_case()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for #name {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "{}", self.to_snake_case())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Return the generated code as a TokenStream
|
||||||
|
gen.into()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Helper function to convert a PascalCase string to a snake_case string.
|
||||||
|
fn to_snake_case(pascal_case: String) -> String {
|
||||||
|
let mut snake = String::new();
|
||||||
|
for (i, ch) in pascal_case.chars().enumerate() {
|
||||||
|
if i > 0 && ch.is_uppercase() {
|
||||||
|
snake.push('_');
|
||||||
|
}
|
||||||
|
snake.push(ch.to_ascii_lowercase());
|
||||||
|
}
|
||||||
|
snake
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user