Repository: dtolnay/paste
Pull request #21 (2020-04-01 03:28:13)

Add snake case idents
Now that there's lower and upper, it would be nice to also have more convenience idents: `snake` for snake case, `screaming` for screaming snake case, and `camel` for camel case.

```rust
get_[<$name:snake>]
```
would give you $name in snake case to better match up with naming conventions.

This is kind of a request for comments to see if it makes sense for paste to also have these, and if there's a simpler/cleaner way to do it.

Here's an example use case crate: [BQ24195](https://github.com/sameer/bq24195/)
there's a bunch of [structs being generated with camel case names](https://github.com/sameer/bq24195/blob/master/src/lib.rs#L141), and I'm [generating snake case getters/setters](https://github.com/sameer/bq24195/blob/master/src/lib.rs#L53) for them

sameer: >     * Consider designing it around composition, e.g. screaming is just `$var:snake:upper`.

That sounds like a good idea. So then there would be `:snake:upper` and `:snake:lower`?

>     * Which of these do you actually intend to use? Please only include functionality that you have a real use case for.
I'm only planning on using the snake lowercase so I'll exclude the camel case stuff.

Patch:
diff --git a/impl/src/lib.rs b/impl/src/lib.rs
index 4f19d5a..b755ac0 100644
--- a/impl/src/lib.rs
+++ b/impl/src/lib.rs
@@ -183,6 +183,18 @@ fn paste_segments(span: Span, segments: &[Segment]) -> Result<TokenStream> {
                     evaluated.push(last.to_lowercase());
                 } else if ident == "upper" {
                     evaluated.push(last.to_uppercase());
+                } else if ident == "snake" {
+                    if last.contains('_') {
+                        return Err(Error::new_spanned(span, "unexpected snake modifier: already contains underscore"));
+                    }
+                    let mut acc = String::new();
+                    last.chars().enumerate().for_each(|(i, c)| {
+                        if i != 0 && c.is_uppercase() {
+                            acc.push('_');
+                        }
+                        acc.push(c);
+                    });
+                    evaluated.push(acc);
                 } else {
                     return Err(Error::new_spanned(span, "unsupported modifier"));
                 }


Test patch:
diff --git a/tests/test.rs b/tests/test.rs
index 9d1d7e1..f25e375 100644
--- a/tests/test.rs
+++ b/tests/test.rs
@@ -225,3 +225,33 @@ fn test_env_to_upper() {
         let _ = LIBPASTE;
     }
 }
+
+mod test_to_snake {
+    macro_rules! m {
+        ($id:ident) => {
+            paste::item! {
+                const [<LOWER_CASE_ $id:snake:upper>]: &str = stringify!([<$id:snake:lower>]);
+                const [<UPPER_CASE_ $id:snake:upper>]: &str = stringify!([<$id:snake:upper>]);
+                const [<ORIGINAL_CASE_ $id:snake:upper>]: &str = stringify!([<$id:snake>]);
+            }
+        };
+    }
+
+    m!(ThisIsButATest);
+
+    #[test]
+    fn test_to_snake() {
+        assert_eq!(LOWER_CASE_THIS_IS_BUT_A_TEST, "this_is_but_a_test");
+        assert_eq!(UPPER_CASE_THIS_IS_BUT_A_TEST, "THIS_IS_BUT_A_TEST");
+        assert_eq!(ORIGINAL_CASE_THIS_IS_BUT_A_TEST, "This_Is_But_A_Test");
+    }
+}
+
+#[test]
+fn test_env_to_snake() {
+    paste::expr! {
+        const [<LIB env!("CARGO_PKG_NAME"):snake:upper>]: &str = "libpaste";
+
+        let _ = LIBPASTE;
+    }
+}