I'm writing a testing tool, this needs to produce a test "job" with a large number of different fields, some of which are preset, some of which are read in and some of which are randomly generated. At the end the tool needs to encode all of these fields.
As you would expect I am using a struct to take all these fields, ensure the correct types and implement default values. However, while there are a large number of fields there aren't many different types i.e. I have a ~100 fields but the the only types used for the values are string
, i32
, std::net::Ipaddr
and chrono::naive::NaiveDateTime
.
Therefore for the encoding step it would be extremely helpful to turn the struct into either a hashmap of enums (with a variant for each different type) or a number of hashmaps (one for each type of value) so that I can iterate over the keys and values and write logic to encode 4 types rather than 100 or so fields.
I suspect there is a way to do this with macros but I haven't found it. Does one exist? Thanks,
e.g.
enum StringOrI32 {
StrField(String),
Int(i32)
}
struct Test {
a: i32,
b: i32,
c: String,
d: String
}
let a = Test {
a: 10,
b: 2,
...
c: "t1".to_string(),
d: "t2".to_string(),
...
};
// turn `a` into
HashMap::from(
[
("a", StringOrI32::Int(10)),
("b", StringOrI32::Int(2)),
("c", StringOrI32::StrField("t1".to_string())),
("d", StringOrI32::StrField("t2".to_string())),
]
);
I'm writing a testing tool, this needs to produce a test "job" with a large number of different fields, some of which are preset, some of which are read in and some of which are randomly generated. At the end the tool needs to encode all of these fields.
As you would expect I am using a struct to take all these fields, ensure the correct types and implement default values. However, while there are a large number of fields there aren't many different types i.e. I have a ~100 fields but the the only types used for the values are string
, i32
, std::net::Ipaddr
and chrono::naive::NaiveDateTime
.
Therefore for the encoding step it would be extremely helpful to turn the struct into either a hashmap of enums (with a variant for each different type) or a number of hashmaps (one for each type of value) so that I can iterate over the keys and values and write logic to encode 4 types rather than 100 or so fields.
I suspect there is a way to do this with macros but I haven't found it. Does one exist? Thanks,
e.g.
enum StringOrI32 {
StrField(String),
Int(i32)
}
struct Test {
a: i32,
b: i32,
c: String,
d: String
}
let a = Test {
a: 10,
b: 2,
...
c: "t1".to_string(),
d: "t2".to_string(),
...
};
// turn `a` into
HashMap::from(
[
("a", StringOrI32::Int(10)),
("b", StringOrI32::Int(2)),
("c", StringOrI32::StrField("t1".to_string())),
("d", StringOrI32::StrField("t2".to_string())),
]
);
Share
Improve this question
edited Mar 26 at 18:16
Pioneer_11
asked Mar 26 at 18:07
Pioneer_11Pioneer_11
1,3222 gold badges10 silver badges32 bronze badges
2
|
1 Answer
Reset to default 1You don't need to write a custom macro. You can make use of serde's introspection capabilities combined with the flexibility of the JSON data model:
#[derive(Serialize)]
struct Test {
// ...fields
}
#[derive(Deserialize, PartialEq, Debug)]
#[serde(untagged)]
enum StringOrI32 {
StrField(String),
Int(i32),
}
fn mapify(a: impl Serialize) -> HashMap<String, StringOrI32> {
// turn the struct into serde_json::Value that holds a map like
// json!({"a": 10, "b": 2, "c": "t1", "d": "t2"})
let val = serde_json::to_value(a).unwrap();
// turn that map into the desired HashMap
serde_json::from_value(val).unwrap()
}
Playground
Note on unwraps: serde returns errors for two reasons: underlying IO error, and the (de)serializer refusing to accept the data, such as when a field contains something other than string or number. The former can't happen when (de)serializing to values, and the latter can only happen if value's type contains fields incompatible with this kind of map, which is a programming bug and warrants a panic.
Test
and generate the hashmap from the same input. The latter can be probably even done withmacro_rules!
– Eugene Sh. Commented Mar 26 at 18:23