1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91
#![allow(non_snake_case)] #![doc(html_root_url = "https://docs.rs/heck-but-macros/0.0.1")] #![forbid(unsafe_code)] #![warn(clippy::pedantic)] extern crate proc_macro; use heck::{ CamelCase, KebabCase, MixedCase, ShoutySnakeCase, ShoutySnekCase, SnakeCase, SnekCase, TitleCase, }; use proc_macro::{ Delimiter, Group as pmGroup, Ident as pmIdent, Literal as pmLiteral, TokenStream, TokenTree, }; macro_rules! case_macro { ($Trait:ident::$fn:ident => $name:ident $stringify_name:ident) => { #[proc_macro] pub fn $name(input: TokenStream) -> TokenStream { case(input, $Trait::$fn) } #[proc_macro] pub fn $stringify_name(input: TokenStream) -> TokenStream { stringify(case(input, $Trait::$fn)) } }; } macro_rules! case_macros { ($($Trait:ident::$fn:ident => $name:ident $stringify_name:ident,)*) => { $(case_macro!($Trait::$fn => $name $stringify_name);)* }; } case_macros! { CamelCase::to_camel_case => camel_case stringify_camel_case, KebabCase::to_kebab_case => kebab_case stringify_kebab_case, MixedCase::to_mixed_case => mixed_case stringify_mixed_case, ShoutySnakeCase::to_shouty_snake_case => shouty_snake_case stringify_shouty_snake_case, ShoutySnekCase::TO_SHOUTY_SNEK_CASE => SHOUTY_SNEK_CASE stringify_SHOUTY_SNEK_CASE, SnakeCase::to_snake_case => snake_case stringify_snake_case, SnekCase::to_snek_case => snek_case stringify_snek_case, TitleCase::to_title_case => title_case stringify_title_case, } fn case(input: TokenStream, case_str: fn(&str) -> String) -> TokenStream { input .into_iter() .map(|token| { let tree: TokenTree = match token { TokenTree::Group(group) => { pmGroup::new(group.delimiter(), case(group.stream(), case_str)).into() } TokenTree::Ident(ident) => { pmIdent::new(&case_str(&format!("{}", ident)), ident.span()).into() } punct @ TokenTree::Punct(_) => punct, TokenTree::Literal(_) => todo!("Literals are not supported yet."), }; tree }) .collect() } fn stringify(input: TokenStream) -> TokenStream { let mut input = input.into_iter(); let result = match input.next() { Some(token) => match token { TokenTree::Group(group) if group.delimiter() == Delimiter::None => { stringify(group.stream()) } TokenTree::Ident(ident) => { TokenTree::Literal(pmLiteral::string(&ident.to_string())).into() } other => todo!("Stringifcation of tokens other than Ident ({:?})", other), }, None => TokenStream::new(), }; if input.next().is_some() { todo!("Stringification of multiple input tokens") } result }