use std::fmt;
use std::str::FromStr;
pub trait FromArg: Sized {
fn from_arg(raw: &str) -> Result<Self, String>;
fn type_name() -> &'static str {
std::any::type_name::<Self>()
}
}
impl<T> FromArg for T
where
T: FromStr,
T::Err: fmt::Display,
{
fn from_arg(raw: &str) -> Result<Self, String> {
raw.parse::<T>().map_err(|e| e.to_string())
}
}
#[cfg(test)]
mod tests {
use super::*;
use runi_test::pretty_assertions::assert_eq;
use std::path::PathBuf;
#[test]
fn parses_strings_and_paths() {
assert_eq!(String::from_arg("hello").unwrap(), "hello".to_string());
assert_eq!(
PathBuf::from_arg("/tmp/x").unwrap(),
PathBuf::from("/tmp/x")
);
}
#[test]
fn parses_numbers() {
assert_eq!(i32::from_arg("-7").unwrap(), -7);
assert_eq!(u64::from_arg("42").unwrap(), 42u64);
assert!((f64::from_arg("2.5").unwrap() - 2.5).abs() < 1e-9);
}
#[test]
fn parses_booleans() {
assert!(bool::from_arg("true").unwrap());
assert!(!bool::from_arg("false").unwrap());
}
#[test]
fn reports_error_for_invalid_input() {
let err = i32::from_arg("not-a-number").unwrap_err();
assert!(!err.is_empty());
}
}