#[forbid(unsafe_code)]
use crate::DynArgError::{NoSuchArg, NotOfType};
use indexmap::IndexMap;
use snafu::prelude::*;
use std::any::{type_name, Any};
#[derive(Debug)]
pub struct ArgData(Box<dyn Any>);
#[derive(Debug, Snafu, PartialEq)]
pub enum DynArgError<'a> {
#[snafu(display("No such arg: {}", name))]
NoSuchArg { name: &'a str },
#[snafu(display("Not of type: {}", name))]
NotOfType { name: &'a str },
}
pub struct Arg {
data: ArgData,
#[cfg(feature = "used")]
used: bool,
}
impl Arg {
pub fn new(arg: Box<dyn Any>) -> Self {
Self {
data: ArgData(arg),
#[cfg(feature = "used")]
used: false,
}
}
pub fn from_argdata(arg_data: ArgData) -> Self {
Self {
data: arg_data,
#[cfg(feature = "used")]
used: false,
}
}
#[cfg(feature = "used")]
pub fn poke<T>(&mut self) -> Result<&T, DynArgError>
where
T: 'static,
{
match self.data.0.downcast_ref::<T>() {
Some(value) => {
self.used = true;
Ok(value)
}
None => Err(NotOfType {
name: type_name::<T>(),
}),
}
}
pub fn get<T>(&self) -> Result<&T, DynArgError>
where
T: 'static,
{
match self.data.0.downcast_ref::<T>() {
Some(value) => Ok(value),
None => Err(NotOfType {
name: type_name::<T>(),
}),
}
}
#[cfg(feature = "used")]
pub fn used(&self) -> bool {
self.used
}
}
#[derive(Default)]
pub struct Args<'a>(IndexMap<&'a str, Arg>);
macro_rules! insert_get_fn {
($insert_fn:ident, $get_fn:ident, $poke_fn: ident, $t:ty) => {
pub fn $insert_fn(&mut self, name: &'a str, value: $t) {
self.0.insert(name, Arg::new(Box::new(value)));
}
pub fn $get_fn(&mut self, name: &'a str) -> Result<&$t, DynArgError> {
self.get::<$t>(name)
}
#[cfg(feature = "used")]
pub fn $poke_fn(&mut self, name: &'a str) -> Result<&$t, DynArgError> {
self.poke::<$t>(name)
}
};
}
impl<'a> Args<'a> {
pub fn new() -> Self {
Self::default()
}
pub fn with_capacity(capacity: usize) -> Self {
Self(IndexMap::with_capacity(capacity))
}
#[cfg(feature = "used")]
pub fn poke<T>(&mut self, name: &'a str) -> Result<&T, DynArgError>
where
T: 'static,
{
match self.0.get_mut(name) {
None => Err(NoSuchArg { name }),
Some(arg) => Ok(arg.poke::<T>()?),
}
}
pub fn get<T>(&self, name: &'a str) -> Result<&T, DynArgError>
where
T: 'static,
{
match self.0.get(name) {
None => Err(NoSuchArg { name }),
Some(arg) => Ok(arg.get::<T>()?),
}
}
pub fn insert(&mut self, name: &'a str, value: Box<dyn Any>) {
self.0.insert(name, Arg::new(value));
}
#[cfg(feature = "used")]
pub fn all_used(&self) -> bool {
self.iter().map(|x| x.1.used).all(|b| b)
}
#[cfg(feature = "used")]
pub fn iter_not_used_name(&self) -> impl Iterator<Item = &str> {
self.iter().filter_map(|x| {
let (arg_name, arg) = x;
match arg.used {
false => Some(arg_name),
true => None,
}
})
}
#[cfg(feature = "used")]
pub fn iter_used_name(&self) -> impl Iterator<Item = &str> {
self.iter().filter_map(|x| {
let (arg_name, arg) = x;
match arg.used {
true => Some(arg_name),
false => None,
}
})
}
#[cfg(feature = "used")]
pub fn reset_used_status(&mut self) {
for (_arg_name, arg) in &mut self.0 {
arg.used = false;
}
}
pub fn iter(&self) -> impl Iterator<Item = (&str, &Arg)> {
self.0.iter().map(|x| {
let (arg_name, arg) = x;
(*arg_name, arg)
})
}
insert_get_fn!(insert_string, get_string, poke_string, String);
insert_get_fn!(insert_f32, get_f32, poke_f32, f32);
insert_get_fn!(insert_f64, get_f64, poke_f64, f64);
insert_get_fn!(insert_i32, get_i32, poke_i32, i32);
insert_get_fn!(insert_i64, get_i64, poke_i64, i64);
insert_get_fn!(insert_bool, get_bool, poke_bool, bool);
}