use std::ffi::OsString;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Subcommand {
Serve,
Migrate,
Queue,
Schedule,
Doctor,
About,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum SubcommandError {
Missing,
Unknown { name: String },
}
impl std::fmt::Display for SubcommandError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Missing => write!(
f,
"missing subcommand (expected one of: serve, migrate, queue, schedule, doctor, \
about)"
),
Self::Unknown { name } => write!(
f,
"unknown subcommand {name:?} (expected one of: serve, migrate, queue, schedule, \
doctor, about)"
),
}
}
}
impl std::error::Error for SubcommandError {}
pub fn parse<I, T>(arguments: I) -> Result<Subcommand, SubcommandError>
where
I: IntoIterator<Item = T>,
T: Into<OsString> + Clone,
{
let mut iter = arguments.into_iter();
let Some(first) = iter.next() else {
return Err(SubcommandError::Missing);
};
let name = first.into().to_string_lossy().into_owned();
match name.as_str() {
"serve" => Ok(Subcommand::Serve),
"migrate" => Ok(Subcommand::Migrate),
"queue" => Ok(Subcommand::Queue),
"schedule" => Ok(Subcommand::Schedule),
"doctor" => Ok(Subcommand::Doctor),
"about" => Ok(Subcommand::About),
other => Err(SubcommandError::Unknown {
name: other.to_owned(),
}),
}
}
#[cfg(test)]
mod tests {
use super::{Subcommand, SubcommandError, parse};
fn parse_args(args: &[&str]) -> Result<Subcommand, SubcommandError> {
parse(args.iter().copied())
}
#[test]
fn parses_all_known_subcommands() {
assert_eq!(parse_args(&["serve"]), Ok(Subcommand::Serve));
assert_eq!(parse_args(&["migrate"]), Ok(Subcommand::Migrate));
assert_eq!(parse_args(&["queue"]), Ok(Subcommand::Queue));
assert_eq!(parse_args(&["schedule"]), Ok(Subcommand::Schedule));
assert_eq!(parse_args(&["doctor"]), Ok(Subcommand::Doctor));
assert_eq!(parse_args(&["about"]), Ok(Subcommand::About));
}
#[test]
fn missing_subcommand_is_typed_error() {
assert_eq!(parse_args(&[]), Err(SubcommandError::Missing));
}
#[test]
fn unknown_subcommand_is_typed_error_with_name() {
assert_eq!(
parse_args(&["deploy"]),
Err(SubcommandError::Unknown {
name: "deploy".to_owned()
})
);
assert_eq!(
parse_args(&["--help"]),
Err(SubcommandError::Unknown {
name: "--help".to_owned()
})
);
}
#[test]
fn trailing_arguments_do_not_change_subcommand() {
assert_eq!(parse_args(&["migrate", "up"]), Ok(Subcommand::Migrate));
}
}