use std::{fmt::Debug, hash::Hash};
pub trait State: Debug + Clone + Eq + Hash + Default {}
impl<T: Debug + Clone + Eq + Hash + Default> State for T {}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Match<'a> {
Okay(Option<&'a str>),
None,
}
pub trait StateOpt {
type S: State;
fn name(&self) -> &str;
fn state(&self) -> &Self::S;
fn consume(&self) -> bool {
true
}
fn r#match<'a>(&self, arg: &'a str) -> Match<'a> {
if let Some((name, val)) = arg.split_once('=') {
if name == self.name() {
Match::Okay(Some(val))
} else {
Match::None
}
} else if self.name() == arg {
Match::Okay(None)
} else {
Match::None
}
}
}
pub struct Opt<'a, S> {
name: &'a str,
state: S,
consume: bool,
}
impl<'a, S> Opt<'a, S> {
pub fn new(name: &'a str, state: S, consume: bool) -> Self {
Self {
name,
state,
consume,
}
}
}
impl<S: State> StateOpt for Opt<'_, S> {
type S = S;
fn name(&self) -> &str {
self.name
}
fn state(&self) -> &Self::S {
&self.state
}
fn consume(&self) -> bool {
self.consume
}
}
pub fn switch<S: State>(name: &str, state: S) -> Opt<'_, S> {
Opt::new(name, state, false)
}
pub fn option<S: State>(name: &str, state: S) -> Opt<'_, S> {
Opt::new(name, state, true)
}
#[cfg(test)]
mod tests {
use super::*;
#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)]
enum TestState {
On,
#[default]
Unknown,
}
#[test]
fn test_opt_new() {
let opt = Opt::new("test", TestState::On, true);
assert_eq!(opt.name, "test");
assert!(opt.consume);
}
#[test]
fn test_stateopt_implementation() {
let opt = Opt::new("test", TestState::On, false);
assert_eq!(opt.name(), "test");
assert!(!opt.consume());
}
#[test]
fn test_match_method() {
let opt = Opt::new("test", TestState::On, true);
assert_eq!(opt.r#match("test"), Match::Okay(None));
assert_eq!(opt.r#match("test=value"), Match::Okay(Some("value")));
assert_eq!(opt.r#match("testing"), Match::None);
assert_eq!(opt.r#match("other"), Match::None);
}
#[test]
fn test_switch_function() {
let opt = switch("test", TestState::On);
assert_eq!(opt.name, "test");
assert!(!opt.consume);
}
#[test]
fn test_option_function() {
let opt = option("test", TestState::On);
assert_eq!(opt.name, "test");
assert!(opt.consume);
}
}