use enum_path::EnumPath;
#[derive(EnumPath, Clone, Debug, PartialEq, Eq)]
#[enum_path(FromStr, Display, rename_all = "snake_case")]
enum Action
{
Exit,
SendMessage(String),
SetState(State),
#[enum_path(rename = "quit")]
Terminate,
GetID,
}
#[derive(EnumPath, Clone, Debug, PartialEq, Eq)]
#[enum_path(FromStr, Display)]
enum State
{
Idle,
Ready,
Running(Phase),
}
#[derive(EnumPath, Clone, Debug, PartialEq, Eq)]
#[enum_path(FromStr, Display)]
enum Phase
{
Init,
Execute,
}
#[derive(EnumPath, Clone, Debug, PartialEq, Eq)]
#[enum_path(FromStr, Display, case_insensitive)]
enum Command
{
Start,
Stop,
Reload(String),
}
mod from_str
{
use super::*;
#[test]
fn unit()
{
let action: Action = "exit".parse().unwrap();
assert_eq!(action, Action::Exit);
}
#[test]
fn consecutive_capitals()
{
let action: Action = "get_id".parse().unwrap();
assert_eq!(action, Action::GetID);
}
#[test]
fn string_payload()
{
let action: Action = "send_message.hello world".parse().unwrap();
assert_eq!(action, Action::SendMessage("hello world".to_owned()));
}
#[test]
fn string_payload_with_delimiter()
{
let action: Action = "send_message.with.dot".parse().unwrap();
assert_eq!(action, Action::SendMessage("with.dot".to_owned()));
}
#[test]
fn nested_depth_0()
{
let action: Action = "set_state.Ready".parse().unwrap();
assert_eq!(action, Action::SetState(State::Ready));
}
#[test]
fn nested_depth_1()
{
let action: Action = "set_state.Running.Execute".parse().unwrap();
assert_eq!(action, Action::SetState(State::Running(Phase::Execute)));
}
#[test]
fn inner_enum()
{
let phase: Phase = "Init".parse().unwrap();
assert_eq!(phase, Phase::Init);
}
#[test]
fn variant_rename()
{
let action: Action = "quit".parse().unwrap();
assert_eq!(action, Action::Terminate);
}
#[test]
fn error()
{
let err = "nonexistent".parse::<Action>().unwrap_err();
assert_eq!(err.input, "nonexistent");
assert_eq!(err.expected, "Action");
}
}
mod display
{
use super::*;
#[test]
fn unit()
{
assert_eq!(Action::Exit.to_string(), "exit");
}
#[test]
fn consecutive_capitals()
{
assert_eq!(Action::GetID.to_string(), "get_id");
}
#[test]
fn string_payload()
{
assert_eq!(
Action::SendMessage("hello world".to_owned()).to_string(),
"send_message.hello world"
);
}
#[test]
fn nested_depth_0()
{
assert_eq!(
Action::SetState(State::Ready).to_string(),
"set_state.Ready"
);
}
#[test]
fn nested_depth_1()
{
assert_eq!(
Action::SetState(State::Running(Phase::Execute)).to_string(),
"set_state.Running.Execute"
);
}
#[test]
fn variant_rename()
{
assert_eq!(Action::Terminate.to_string(), "quit");
}
}
mod case_insensitive
{
use super::*;
#[test]
fn exact_case()
{
let cmd: Command = "Start".parse().unwrap();
assert_eq!(cmd, Command::Start);
}
#[test]
fn lower_case()
{
let cmd: Command = "start".parse().unwrap();
assert_eq!(cmd, Command::Start);
}
#[test]
fn upper_case()
{
let cmd: Command = "STOP".parse().unwrap();
assert_eq!(cmd, Command::Stop);
}
#[test]
fn mixed_case()
{
let cmd: Command = "sToP".parse().unwrap();
assert_eq!(cmd, Command::Stop);
}
#[test]
fn payload_case_insensitive_prefix()
{
let cmd: Command = "rELOAD.config.yaml".parse().unwrap();
assert_eq!(cmd, Command::Reload("config.yaml".to_owned()));
}
#[test]
fn display_preserves_canonical_case()
{
assert_eq!(Command::Start.to_string(), "Start");
assert_eq!(Command::Stop.to_string(), "Stop");
}
}
mod roundtrip
{
use super::*;
#[test]
fn all_variants()
{
let cases = [
Action::Exit,
Action::SendMessage("payload".to_owned()),
Action::SetState(State::Idle),
Action::SetState(State::Running(Phase::Init)),
Action::Terminate,
];
for original in &cases {
let serialized = original.to_string();
let parsed: Action = serialized.parse().unwrap();
assert_eq!(&parsed, original);
}
}
}
mod custom_error
{
use super::*;
#[derive(Debug)]
struct MyError(enum_path::Error);
impl core::fmt::Display for MyError
{
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result
{
write!(f, "custom: {}", self.0)
}
}
impl From<enum_path::Error> for MyError
{
fn from(e: enum_path::Error) -> Self
{
Self(e)
}
}
#[derive(EnumPath, Clone, Debug, PartialEq, Eq)]
#[enum_path(FromStr, error = MyError)]
enum Thing
{
Foo,
}
#[test]
fn uses_custom_error()
{
let err = "bar".parse::<Thing>().unwrap_err();
assert_eq!(err.0.expected, "Thing");
}
}