1use crate::*;
2use clap;
3use std::{
4 cell::UnsafeCell,
5 collections::{HashMap, LinkedList},
6 fmt,
7 ops::Deref,
8 str::FromStr,
9 sync::atomic::{AtomicI8, Ordering},
10};
11
12struct item {
13 opt: &'static OptVal,
14 sk: &'static str,
15 lk: Option<&'static str>,
16 lv: Option<&'static str>,
17 dv: Option<&'static str>,
18 help: Option<&'static str>,
19}
20
21fn cmd_hash_get() -> &'static mut HashMap<&'static str, LinkedList<item>> {
22 static mut HASH: *mut HashMap<&'static str, LinkedList<item>> = nil!();
23 unsafe {
24 if HASH == nil!() {
27 HASH = Box::leak(Box::new(HashMap::new()));
28 }
29 &mut *HASH
30 }
31}
32
33fn cmd_args_list(k: &'static str) -> &mut LinkedList<item> {
34 let h = cmd_hash_get();
35 let opt = h.get_mut(k);
36 if opt.is_none() {
37 let v = LinkedList::new();
38 h.insert(k, v);
39 }
40
41 let opt = h.get_mut(k);
42 match opt {
43 Some(v) => v,
44 None => unreachable!(),
45 }
46}
47
48pub fn declare_argment<'a: 'static>(
49 module: &'a str,
50 name: &'a str,
51 opt: &'a OptVal,
52 cmd: Option<&'a str>,
53 sk: &'a str,
54 lk: Option<&'a str>,
55 lv: Option<&'a str>,
56 dv: Option<&'a str>,
57 help: Option<&'a str>,
58) {
59 let mut module = module.to_owned();
60 module.push_str(name);
61 unsafe {
62 *opt.name.get() = Box::leak(module.into_boxed_str());
63 }
64
65 let k = cmd.unwrap_or("");
66 cmd_args_list(k).push_back(item {
67 sk,
68 lk,
69 lv,
70 dv,
71 help,
72 opt,
73 });
74}
75
76pub struct OptVal {
77 name: UnsafeCell<&'static str>,
78 stat: AtomicI8,
79}
80make!(OptVal: Sync);
81
82impl OptVal {
83 pub const fn new() -> Self {
84 OptVal {
85 name: UnsafeCell::new(""),
86 stat: AtomicI8::new(0),
87 }
88 }
89
90 fn name(&self) -> &'static str {
91 unsafe { *self.name.get() }
92 }
93
94 pub fn str(&self) -> Result<&'static str, String> {
95 let stat = self.stat.load(Ordering::Relaxed);
96 if stat & 4 == 0 {
97 return Err("can't called before main".to_owned());
98 }
99
100 if stat & 8 != 0 {
101 return Err("not in subcommand context".to_owned());
102 }
103 Ok(self.name())
104 }
105
106 pub fn parse<T: FromStr>(&self) -> Result<T, String>
107 where
108 T::Err: fmt::Display,
109 {
110 let stat = self.stat.load(Ordering::Relaxed);
111 if stat & 4 == 0 {
112 return Err("can't called before main".to_owned());
113 }
114
115 if stat & 8 != 0 {
116 return Err("not in subcommand context".to_owned());
117 }
118
119 match self.name().parse() {
120 Ok(v) => Ok(v),
121 Err(e) => Err(format!("{}", e)),
122 }
123 }
124}
125
126pub struct optv(pub &'static dyn Deref<Target = &'static OptVal>);
127impl Deref for optv {
128 type Target = &'static OptVal;
129 fn deref(&self) -> &Self::Target {
130 self.0.deref()
131 }
132}
133make!(optv: Sync);
134
135fn apply<'a, 'b>(itm: &item, mut arg: clap::Arg<'a, 'b>, mask: i8) -> clap::Arg<'a, 'b> {
136 arg = arg.short(itm.sk);
137 if itm.lk.is_some() {
138 arg = arg.long(itm.lk.unwrap());
139 }
140 let mut stat = mask;
141
142 if itm.lv.is_some() {
143 arg = arg.value_name(itm.lv.unwrap());
144 stat |= 1;
145 }
146
147 if itm.dv.is_some() {
148 if stat == mask {
149 arg = arg.value_name("unspecified");
150 }
151 arg = arg.default_value(itm.dv.unwrap());
152 stat |= 3;
153 }
154
155 itm.opt.stat.store(stat, Ordering::Relaxed);
156 if itm.help.is_some() {
157 arg = arg.help(itm.help.unwrap())
158 }
159 arg
160}
161
162fn app_menu<'a, 'b>(
163 name: &'static str,
164 mut app: clap::App<'a, 'b>,
165 args: LinkedList<item>,
166 opts: &mut Vec<(&str, &OptVal)>,
167 mask: i8,
168) -> clap::App<'a, 'b> {
169 for itm in args {
170 let mut arg = clap::Arg::with_name(itm.opt.name());
171 arg = apply(&itm, arg, mask);
172 app = app.arg(arg);
173 opts.push((name, itm.opt));
174 }
175 app
176}
177
178pub fn parse(mut m: clap::App) {
179 let mut opts: Vec<(&str, &OptVal)> = vec![];
180 let h = cmd_hash_get();
181 let opt = h.remove(&"");
182 if let Some(args) = opt {
183 m = app_menu("", m, args, &mut opts, 0x00);
184 }
185
186 for (name, args) in h.drain() {
187 let mut sc = clap::SubCommand::with_name(name);
188 sc = app_menu(name, sc, args, &mut opts, -128);
189 m = m.subcommand(sc);
190 }
191
192 let matches = m.get_matches();
193 for (sc, opt) in opts {
194 let name = opt.name();
195 let mut vals = &matches;
196 let stat = opt.stat.load(Ordering::Relaxed);
197 if stat < 0 {
198 vals = if let Some(x) = matches.subcommand_matches(sc) {
199 x
200 } else {
201 opt.stat.store(stat | 12, Ordering::Relaxed);
202 continue;
203 }
204 }
205
206 let val: String;
207 if stat & 1 == 0 {
208 val = vals.is_present(name).to_string();
209 } else {
210 val = vals.value_of(name).unwrap_or_default().to_owned();
211 }
212
213 unsafe {
214 let ptr = opt.name.get();
215 Box::<str>::from(*ptr);
216 *ptr = Box::leak(val.into_boxed_str());
217 }
218 opt.stat.store(stat | 4, Ordering::Relaxed);
219 }
220}
221
222#[macro_export]
223#[rustfmt::skip]
224macro_rules! option {
225 (None) => { None };
226 (@ None) => { None };
227 (@ $val:literal) => { Some( stringify!($val)) };
228 ($val:literal) => { Some($val) };
229 ($cmd:tt, $sk:literal, $lk:tt, $lv:tt, $dv:tt, $help:tt) => {
230 $crate::flags::optv({
231 #[ctor]
232 static opt: &'static $crate::flags::OptVal = {
233 static x: $crate::flags::OptVal = $crate::flags::OptVal::new();
234 $crate::flags::declare_argment(
235 module_path!(),
236 str64!(),
237 &x,
238 option!($cmd),
239 $sk,
240 option!($lk),
241 option!($lv),
242 option!(@ $dv),
243 option!($help),
244 );
245 &x
246 };
247 &opt
248 })
249 };
250}
251
252#[macro_export]
253macro_rules! option_parse {
254 ($app:literal, $version:literal) => {{
255 extern crate clap;
256 $crate::flags::parse(clap::App::new($app).version($version));
257 }};
258}