dynomite icon indicating copy to clipboard operation
dynomite copied to clipboard

Request: Implement a `rename_all = ...` attribute for types

Open phrohdoh opened this issue 6 years ago • 2 comments

💡 Feature description

Similar to serde's rename_all attribute, this would reformat all of the type's fields / variants for de/serialization to the format specified.

Individual field / variant rename must take precedence over rename_all.

Valid format values should be, at minimum:

  • snake_case
  • kebab-case
  • camelCase
  • PascalCase
  • SCREAMING_SNAKE_CASE
  • SCREAMING-KEBAB-CASE
  • UPPERCASE
  • lowercase

This rename_all attribute should probably be applicable to types and enum variants.

💻 Basic example

#[derive(Item, PartialEq, Debug, Clone)]
#[dynomite(rename_all = "kebab-case")]
struct Recipe {
    #[dynomite(partition_key]
    #[dynomite(rename = "recipe_id")]
    id: String,
    net_carbs: u64,
}

Should be serialized (to JSON just as an example) as:

{
    "recipe_id": "...",
    "net-carbs": 0
}

references / prior work

phrohdoh avatar Aug 23 '19 14:08 phrohdoh

We should consider adding heck, an opinionated & non-customizable case-conversion 3rd-party library crate, as a dependency to the dynomite-derive crate to reduce rework and (potentially) simplify implementation (and testing!) of this feature (since it reformats the existing name, instead of providing a new name entirely).

phrohdoh avatar Jan 21 '21 13:01 phrohdoh

I've written code that makes the following test pass (using the heck crate, just an implementation detail though). If there is any interest (not seeing any so far on the ticket; no thumbs-up etc), I could continue working on the implementation I currently have. Of course I prefer to not unnecessarily bloat dynomite.

rename_all type-level dynomite attr
#[test]
#[allow(non_snake_case /* specifically testing mixed-case fields */)]
fn derive_item_rename_all_attr_kebab_case() {
    // GIVEN
    #[derive(Item)]
    #[dynomite(rename_all = "kebab-case")]
    struct MyItem {
        #[dynomite(partition_key)]
        _pk: String,
        #[dynomite(sort_key)]
        sk: String,
        #[dynomite(rename = "fooBar")]
        with_Field_levelRename: Option<i32>,

        snake_case: bool,
        camelCase: bool,
        PascalCase: bool,
        SCREAMING_SNAKE_CASE: bool,
        UPPERCASE: bool,
        lowercase: bool,
    }

    let original = MyItem {
        _pk: "6956abd4-665f-433a-b9ad-c038f9c64601".into(),
        sk: "2021-01-20T22:41:41.603Z".into(),
        with_Field_levelRename: Some(1024),
        snake_case: true,
        camelCase: true,
        PascalCase: true,
        SCREAMING_SNAKE_CASE: true,
        UPPERCASE: true,
        lowercase: true,
    };

    // WHEN
    let attrs: Attributes = original.into();

    // THEN
    assert_eq!(attrs.len(), 9);
    assert!(attrs.contains_key("_pk"), "leading underscore retained");
    assert!(attrs.contains_key("sk"), "unchanged");
    assert!(attrs.contains_key("fooBar"), "field-level rename has precedent");
    assert!(attrs.contains_key("snake-case"));
    assert!(attrs.contains_key("camel-case"));
    assert!(attrs.contains_key("pascal-case"));
    assert!(attrs.contains_key("screaming-snake-case"));
    assert!(attrs.contains_key("uppercase"));
    assert!(attrs.contains_key("lowercase"));
}

phrohdoh avatar Jan 22 '21 14:01 phrohdoh