In rust, the normal way to convert strings into other types is by using the FromStr
trait. However, FromStr
can, by default, not be derived.
I thought I could recommend you the derive_more
crate. Using this crate you can derive more traits, as the name implies. Unfortunately it does not support enums at this moment. I think that would be a fun addition to the library.
I think you can solve this problem with macros, but the clearest, cleanest, and most idiomatic way is just implementing FromStr
yourself. You can use a match statement, matching on each of the strings and producing an enum variant. Doing this, you can also change the format of the strings which produce certain variants. For example, your enum variants might have capitalized names, but you may not want the strings to be capitalized as well. You could also add aliasses this way. An example:
enum MyEnum {
A,
B
}
struct FromStrError;
impl FromStr for MyEnum {
type Err = FromStrError;
fn from_str(v: &str) -> Result<Self, Self::Err> {
match v {
"a" => Ok(Self::A),
"b" => Ok(Self::B),
_ => Err(FromStrError),
}
}
}
fn main() {
let parsed: MyEnum = "a".parse()?
}
I understand this may not be an answer to your exact question: You wanted something equialent to the X macro. I think this is the way you will want to implement this, but if you really prefer a macro solution please let me know. Your question feels to me a bit like an X-Y problem. "how can I do X in rust", while what you actually want may be Y. Anyway, let me know if I can help you further.
NOTE: the to_string functionality you definitely can achieve with derive_more
! See here
EDIT: if you really want a macro solution, and I want to stress again that I don't think this is the right solution, you can do something like this:
// define a macro generating both FromStr and Display implementations
macro_rules! stringy_enum {
(enum $name: ident {
$($variant: ident = $value: literal),* $(,)?
}) => {
#[derive(Debug, PartialEq)] // for tests, no need if you don't want it
enum $name {
$($variant = $value),*
}
impl std::str::FromStr for $name {
type Err = String;
fn from_str(value: &str) -> Result<Self, Self::Err> {
match value {
$(
stringify!($variant) => {Ok(Self::$variant)}
)*
_ => {
Err(format!("couldn't parse to {}", stringify!($name)))
}
}
}
}
impl std::fmt::Display for $name {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
$(
Self::$variant => {write!(f, stringify!($variant))}
)*
}
}
}
};
}
stringy_enum!(
enum MyEnum {
A = 3,
B = 4,
}
);
#[test]
fn example() {
// strings can be parsed into enums
assert_eq!(Ok(MyEnum::A), "A".parse());
// enums can be converted into strings
assert_eq!(MyEnum::A.to_string(), "A");
}
note that this solution does not accept custom derives on the enum (the macro doesn't parse those), it doesn't accept anything other than variant = name
pairs, it does not accept a pub keyword or other visibility attribues (though that's easy to add), it does not accept any other custom attributes other than derive, it does not support variants with values, etc. This is just an example. Don't literally use it.