#![allow(nonstandard_style)]
pub use comn_pms::*;
pub use ctor::*;
use clap;
use std::{
cell::UnsafeCell,
collections::{HashMap, LinkedList},
fmt,
ops::Deref,
ptr::null_mut,
str::FromStr,
sync::atomic::{AtomicI8, Ordering},
};
struct item {
opt: &'static OptVal,
sk: &'static str,
lk: Option<&'static str>,
lv: Option<&'static str>,
dv: Option<&'static str>,
help: Option<&'static str>,
}
fn cmd_hash_get() -> &'static mut HashMap<&'static str, LinkedList<item>> {
static mut HASH: *mut HashMap<&'static str, LinkedList<item>> = null_mut::<u8>().cast();
unsafe {
if HASH == null_mut::<u8>().cast() {
HASH = Box::leak(Box::new(HashMap::new()));
}
&mut *HASH
}
}
fn cmd_args_list(k: &'static str) -> &mut LinkedList<item> {
let h = cmd_hash_get();
let opt = h.get_mut(k);
if opt.is_none() {
let v = LinkedList::new();
h.insert(k, v);
}
let opt = h.get_mut(k);
match opt {
Some(v) => v,
None => unreachable!(),
}
}
pub fn declare_argment<'a: 'static>(
module: &'a str,
name: &'a str,
opt: &'a OptVal,
cmd: Option<&'a str>,
sk: &'a str,
lk: Option<&'a str>,
lv: Option<&'a str>,
dv: Option<&'a str>,
help: Option<&'a str>,
) {
let mut module = module.to_owned();
module.push_str(name);
unsafe {
*opt.name.get() = Box::leak(module.into_boxed_str());
}
let k = cmd.unwrap_or("");
cmd_args_list(k).push_back(item {
sk,
lk,
lv,
dv,
help,
opt,
});
}
pub struct OptVal {
name: UnsafeCell<&'static str>,
stat: AtomicI8,
}
make!(OptVal: Sync);
impl OptVal {
pub const fn new() -> Self {
OptVal {
name: UnsafeCell::new(""),
stat: AtomicI8::new(0),
}
}
fn name(&self) -> &'static str {
unsafe { *self.name.get() }
}
pub fn str(&self) -> Result<&'static str, String> {
let stat = self.stat.load(Ordering::Relaxed);
if stat & 4 == 0 {
return Err("can't called before main".to_owned());
}
if stat & 8 != 0 {
return Err("not in subcommand context".to_owned());
}
Ok(self.name())
}
pub fn parse<T: FromStr>(&self) -> Result<T, String>
where
T::Err: fmt::Display,
{
let stat = self.stat.load(Ordering::Relaxed);
if stat & 4 == 0 {
return Err("can't called before main".to_owned());
}
if stat & 8 != 0 {
return Err("not in subcommand context".to_owned());
}
match self.name().parse() {
Ok(v) => Ok(v),
Err(e) => Err(format!("{}", e)),
}
}
}
pub struct optv(pub &'static dyn Deref<Target = &'static OptVal>);
impl Deref for optv {
type Target = &'static OptVal;
fn deref(&self) -> &Self::Target {
self.0.deref()
}
}
make!(optv: Sync);
fn apply<'a, 'b>(itm: &item, mut arg: clap::Arg<'a, 'b>, mask: i8) -> clap::Arg<'a, 'b> {
arg = arg.short(itm.sk);
if itm.lk.is_some() {
arg = arg.long(itm.lk.unwrap());
}
let mut stat = mask;
if itm.lv.is_some() {
arg = arg.value_name(itm.lv.unwrap());
stat |= 1;
}
if itm.dv.is_some() {
if stat == mask {
arg = arg.value_name("unspecified");
}
arg = arg.default_value(itm.dv.unwrap());
stat |= 3;
}
itm.opt.stat.store(stat, Ordering::Relaxed);
if itm.help.is_some() {
arg = arg.help(itm.help.unwrap())
}
arg
}
fn app_menu<'a, 'b>(
name: &'static str,
mut app: clap::App<'a, 'b>,
args: LinkedList<item>,
opts: &mut Vec<(&str, &OptVal)>,
mask: i8,
) -> clap::App<'a, 'b> {
for itm in args {
let mut arg = clap::Arg::with_name(itm.opt.name());
arg = apply(&itm, arg, mask);
app = app.arg(arg);
opts.push((name, itm.opt));
}
app
}
pub fn parse(mut m: clap::App) {
let mut opts: Vec<(&str, &OptVal)> = vec![];
let h = cmd_hash_get();
let opt = h.remove(&"");
if let Some(args) = opt {
m = app_menu("", m, args, &mut opts, 0x00);
}
for (name, args) in h.drain() {
let mut sc = clap::SubCommand::with_name(name);
sc = app_menu(name, sc, args, &mut opts, -128);
m = m.subcommand(sc);
}
let matches = m.get_matches();
for (sc, opt) in opts {
let name = opt.name();
let mut vals = &matches;
let stat = opt.stat.load(Ordering::Relaxed);
if stat < 0 {
vals = if let Some(x) = matches.subcommand_matches(sc) {
x
} else {
opt.stat.store(stat | 12, Ordering::Relaxed);
continue;
}
}
let val: String;
if stat & 1 == 0 {
val = vals.is_present(name).to_string();
} else {
val = vals.value_of(name).unwrap_or_default().to_owned();
}
unsafe {
let ptr = opt.name.get();
Box::<str>::from(*ptr);
*ptr = Box::leak(val.into_boxed_str());
}
opt.stat.store(stat | 4, Ordering::Relaxed);
}
}
#[macro_export]
#[rustfmt::skip]
macro_rules! option {
(None) => { None };
(@ None) => { None };
(@ $val:literal) => { Some( stringify!($val)) };
($val:literal) => { Some($val) };
($cmd:tt, $sk:literal, $lk:tt, $lv:tt, $dv:tt, $help:tt) => {
$crate::optv({
#[ctor]
static opt: &'static $crate::OptVal = {
static x: $crate::OptVal = $crate::OptVal::new();
$crate::declare_argment(
module_path!(),
str64!(),
&x,
option!($cmd),
$sk,
option!($lk),
option!($lv),
option!(@ $dv),
option!($help),
);
&x
};
&opt
})
};
}
#[macro_export]
macro_rules! option_parse {
($app:literal, $version:literal) => {{
extern crate clap;
$crate::parse(clap::App::new($app).version($version));
}};
}