Skip to content
This repository has been archived by the owner on Jul 9, 2023. It is now read-only.

Attribute macro to apply prefix to name of serialized fields or variants #19

Closed
dtolnay opened this issue Jan 22, 2019 · 12 comments · Fixed by #32
Closed

Attribute macro to apply prefix to name of serialized fields or variants #19

dtolnay opened this issue Jan 22, 2019 · 12 comments · Fixed by #32

Comments

@dtolnay
Copy link
Owner

dtolnay commented Jan 22, 2019

Some JSON protocols represent variant types using a common prefix. For example a network result may be transmitted as "NetworkResult_Success" / "NetworkResult_Failure". In Rust we would like to represent such values as enum variants NetworkResult::Success and NetworkResult::Failure. This is possible with Serde rename attributes but somewhat verbose.

#[derive(Deserialize)]
enum NetworkResult {
    #[serde(rename = "NetworkResult_Success")]
    Success,
    #[serde(rename = "NetworkResult_Failure")]
    Failure,
}

It would be nicer to have an attribute macro that inserts a given prefix as a rename attribute on each field, generating the code above.

#[prefix_all("NetworkResult_")]
#[derive(Deserialize)]
enum NetworkResult {
    Success,
    Failure,
}
@TedDriggs
Copy link

This feels like it'd be better as a PR to serde; having it in the docs at serde.rs would make it more discoverable and protecting it under serde's duplicate field and other validations seems to be a friendlier API. would you be opposed to this coming in a PR there?

@Mingun
Copy link

Mingun commented Feb 7, 2019

Also then it is good to have prefix and suffix attribute for flatten:

struct X {
  #[serde(flatten(prefix = "Embed", suffix = "Field"))]
  a,
}

@dtolnay
Copy link
Owner Author

dtolnay commented Feb 7, 2019

I would not accept this as a PR to Serde. I don't think this pattern is sufficiently ubiquitous to justify a new attribute in serde_derive.

An attribute macro that expands to rename attributes would have just as much validation.

@jonathan-s
Copy link
Contributor

@dtolnay I'm currently looking at this. I was thinking of creating the #[serde(rename = "prefix_fieldname")] attributes using quote! {} and then inserting those in the AST tree. Right now I'm a bit stuck as it doesn't seem possible to create a new Attribute which I would then insert.

Do you have any suggestions?

@dtolnay
Copy link
Owner Author

dtolnay commented Jun 19, 2019

I would recommend using parse_quote to make attributes:
https://docs.rs/syn/0.15/syn/macro.parse_quote.html

@jonathan-s
Copy link
Contributor

jonathan-s commented Jun 19, 2019 via email

@jonathan-s
Copy link
Contributor

@dtolnay I've got a repo up now https://github.com/jonathan-s/serde_prefix I would be very grateful if you had a look at it. I still need to add some documentation for how it should be used.

But in short #[prefix_all("test_")] can be used for structs and enums to rename attributes. Inside the box it uses #[serde(rename = "test_attributeName")].

@dtolnay
Copy link
Owner Author

dtolnay commented Jun 22, 2019

Fantastic!

I would be interested in someone experimenting with what serde_derive could do to integrate these sorts of extensions (skip_serializing_none is another one) more nicely. For example you could imagine that serde_derive could apply this transformation:

use serde_prefix::prefix_all;

// written by caller:
#[derive(Serialize)]
#[serde(apply = "prefix_all", prefix = "test_")]
struct Struct {
    /* ... */
}

// expands to:
#[prefix_all(prefix = "test_")]
#[derive(Serialize)]
struct Struct {
    /* ... */
}

Or somewhat more cleverly:

// no `use` needed

// written by caller:
#[derive(Serialize)]
#[serde(prefix::all = "test_")] // anything with colons is recognized as extension
struct Struct {
    /* ... */
}

// expands to:
#[serde_prefix::all = "test_"] // prepend `serde_` on original extension
#[derive(Serialize)]
struct Struct {
    /* ... */
}

@jonathan-s
Copy link
Contributor

jonathan-s commented Jun 25, 2019

@dtolnay Would the #[serde(prefix-macro-here)] macro need to be defined in the serde crate first? Or is there a functionality like that already?

@jonathan-s
Copy link
Contributor

And to answer my own question, it looks like it would need to be defined here: https://github.com/serde-rs/serde/blob/master/serde_derive/src/internals/attr.rs when implementing the container struct in the from_ast method, if I am correct.

@jonathan-s
Copy link
Contributor

Another question, should prefix::all be considered meta? If that's the case we would need to change the syn crate as well.

@RReverser
Copy link

I'm not sure this issue is supposed to be closed, as the suffix part is not yet implemented anywhere (AFAIK).

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants