Repository: dtolnay/paste (rust)

Issue:
Identifier case conversion
Use case provided to me by email:

```rust
macro_rules! ld_r16_expr {
    ($($reg:ident)*) => {
        $(
            paste::item! {
                pub fn [<ld_ $reg _expr>] (val: Expr) -> Token {
                    token_for_opcode_two_args(
                        Mnemonic::Ld,
                        Register16::$reg.into(),
                        val.into(),
                    )
                }
            }
        )*
    }
}

ld_r16_expr!(Af Bc De Hl);
```

We would like some way to end up with functions `ld_af_expr` `ld_bc_expr` etc rather than `ld_Af_expr` `ld_Bc_expr`.

I would be be prepared to consider a PR implementing this as something like `[<ld_ $reg:lower _expr>]`.

Pull request description:
implement $id:lower ident modifier
It's been a while since I touched procmacro, hope didn't miss anything important.

(#13)

Patch:
diff --git a/impl/src/lib.rs b/impl/src/lib.rs
index 805d644..529b49a 100644
--- a/impl/src/lib.rs
+++ b/impl/src/lib.rs
@@ -7,7 +7,7 @@ use proc_macro_hack::proc_macro_hack;
 use quote::{quote, ToTokens};
 use std::iter::FromIterator;
 use syn::parse::{Error, Parse, ParseStream, Parser, Result};
-use syn::{parenthesized, parse_macro_input, Lit, LitStr, Token};
+use syn::{parenthesized, parse_macro_input, Ident as SynIdent, Lit, LitStr, Token};
 
 #[proc_macro]
 pub fn item(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
@@ -131,6 +131,20 @@ fn parse_segments(input: ParseStream) -> Result<Vec<Segment>> {
             TokenTree::Punct(punct) => match punct.as_char() {
                 '_' => segments.push(Segment::String("_".to_string())),
                 '\'' => segments.push(Segment::Apostrophe(punct.span())),
+                ':' => {
+                    let cs = match segments.pop() {
+                        Some(Segment::String(cs)) => cs,
+                        _ => return Err(Error::new(punct.span(), "unexpected punct")),
+                    };
+                    if !input.peek(SynIdent) {
+                        return Err(Error::new(punct.span(), "unexpected punct"));
+                    }
+                    if input.parse::<SynIdent>()? != "lower" {
+                        return Err(Error::new(punct.span(), "unsupported ident modifier"));
+                    }
+
+                    segments.push(Segment::String(cs.to_lowercase()));
+                }
                 _ => return Err(Error::new(punct.span(), "unexpected punct")),
             },
             TokenTree::Group(group) => {


Test patch:
diff --git a/tests/test.rs b/tests/test.rs
index e96a2d2..cbadac6 100644
--- a/tests/test.rs
+++ b/tests/test.rs
@@ -171,3 +171,22 @@ mod test_none_delimited_single_ident {
         assert_eq!(f(), "i32x4");
     }
 }
+
+mod test_to_lower {
+    macro_rules! m {
+        ($id:ident) => {
+            paste::item! {
+                fn [<my_ $id:lower _here>](_arg: u8) -> &'static str {
+                    stringify!([<$id:lower>])
+                }
+            }
+        };
+    }
+
+    m!(Test);
+
+    #[test]
+    fn test_to_lower() {
+        assert_eq!(my_test_here(0), "test");
+    }
+}