zflags 0.1.2

my common rust code
Documentation
#![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 {
        // 这个函数只在 ctor 中被调用
        // 因此并没有并发问题
        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));
    }};
}