use proc_macro::TokenStream;
use proc_macro2::Span;
use proc_macro2::TokenStream as TokenStream2;
use quote::quote;
use syn::{
Ident, LitBool, LitChar, LitInt, LitStr, Token, bracketed,
ext::IdentExt,
parse::{Parse, ParseStream, Result},
parse_macro_input,
};
const IMPOSSIBLE_ERROR: &str = "Inner Parsing Error occured. Please be so kind and open a new Issue at https://github.com/revellan/revparse/issues called IMPOSSIBLE ERROR";
enum ArgSetting {
ExecName(LitStr),
Pos(LitStr),
PosHelp(LitStr),
PosMin(LitInt),
PosMax(LitInt),
PosInfinite(LitBool),
ModName(Ident),
}
enum ArgKind {
Both {
long: Ident,
short: LitChar,
val: LitStr,
help: LitStr,
},
NoShort {
long: Ident,
val: LitStr,
help: LitStr,
},
NoVal {
long: Ident,
short: LitChar,
help: LitStr,
},
Neither {
long: Ident,
help: LitStr,
},
Setting(ArgSetting),
}
impl ArgKind {
fn short_name(&self) -> Option<char> {
match self {
Self::Both { short, .. } => Some(short.value()),
Self::NoVal { short, .. } => Some(short.value()),
_ => None,
}
}
fn long_name(&self) -> String {
match self {
Self::Both { long, .. } => long.to_string(),
Self::NoVal { long, .. } => long.to_string(),
Self::NoShort { long, .. } => long.to_string(),
Self::Neither { long, .. } => long.to_string(),
_ => panic!("{}", IMPOSSIBLE_ERROR),
}
}
fn take_value(&self) -> Option<String> {
match self {
Self::Both { val, .. } => Some(val.value()),
Self::NoShort { val, .. } => Some(val.value()),
_ => None,
}
}
fn help_msg(&self) -> String {
match self {
Self::Both { help, .. } => help.value(),
Self::NoVal { help, .. } => help.value(),
Self::NoShort { help, .. } => help.value(),
Self::Neither { help, .. } => help.value(),
_ => panic!("{}", IMPOSSIBLE_ERROR),
}
}
}
impl Parse for ArgKind {
fn parse(input: ParseStream) -> Result<Self> {
let ident: Ident = input.call(Ident::parse_any)?;
if input.peek(Token![,]) {
input.parse::<Token![,]>()?;
if input.peek(LitStr) {
let help: LitStr = input.parse()?;
if input.peek(Token![,]) {
input.parse::<Token![,]>()?;
let val: LitStr = input.parse()?;
Ok(ArgKind::NoShort {
long: ident,
val,
help,
})
} else {
Ok(ArgKind::Neither { long: ident, help })
}
} else {
let short: LitChar = input.parse()?;
input.parse::<Token![,]>()?;
let help: LitStr = input.parse()?;
if input.peek(Token![,]) {
input.parse::<Token![,]>()?;
let val: LitStr = input.parse()?;
Ok(ArgKind::Both {
long: ident,
short,
val,
help,
})
} else {
Ok(ArgKind::NoVal {
long: ident,
short,
help,
})
}
}
} else {
input.parse::<Token![=>]>()?;
use ArgKind::Setting as ArgSet;
use ArgSetting::*;
match ident.to_string().as_ref() {
"ExecName" => Ok(ArgSet(ExecName(input.parse()?))),
"Pos" => Ok(ArgSet(Pos(input.parse()?))),
"PosMin" => Ok(ArgSet(PosMin(input.parse()?))),
"PosMax" => Ok(ArgSet(PosMax(input.parse()?))),
"PosInfinite" => Ok(ArgSet(PosInfinite(input.parse()?))),
"PosHelp" => Ok(ArgSet(PosHelp(input.parse()?))),
"ModName" => Ok(ArgSet(ModName(input.parse()?))),
ident_str => Err(syn::Error::new(
ident.span(),
format!("`{}` is not a valid setting!", ident_str),
)),
}
}
}
}
struct RevparseInt {
exec_name: String,
usage: String,
help: String,
min_pos_args: u64,
custom_max_pos_args: bool,
max_pos_args: u64,
pos_arg_help: Option<String>,
infinite_pos_args: bool,
args: Vec<ArgKind>,
pres_pos_args: Vec<String>,
mod_name: Ident,
}
impl RevparseInt {
fn match_setting(&mut self, setting: ArgSetting) {
use ArgSetting::*;
match setting {
ExecName(name) => self.exec_name = name.value(),
Pos(pos_arg) => self.pres_pos_args.push(pos_arg.value()),
PosHelp(pos_help) => self.pos_arg_help = Some(pos_help.value()),
PosMin(min_pos) => {
self.min_pos_args = min_pos
.base10_parse()
.expect("PosMin should be of type u64")
}
PosMax(max_pos) => {
self.max_pos_args = max_pos
.base10_parse()
.expect("PosMin should be of type u64");
self.custom_max_pos_args = true;
}
PosInfinite(boolean) => self.infinite_pos_args = boolean.value(),
ModName(mod_name) => self.mod_name = mod_name,
}
}
fn create_help(&mut self) {
self.help = match &self.pos_arg_help {
Some(msg) => format!("\n{}\n\nOptions:\n", msg),
None => String::from("\n\nOptions:\n"),
};
self.usage = String::from(format!("Usage: {} [OPTION]...", self.exec_name));
if self.pres_pos_args.len() != 0 {
for i in &self.pres_pos_args {
self.usage.push_str(&format!(" {}", i.to_uppercase()))
}
}
self.help
.push_str(" -h, --help display this help text and exit\n");
for arg in &self.args {
let mut length: usize; let i = arg.long_name().replace('_', "-");
match arg.short_name() {
Some(sn) => {
self.help.push_str(format!(" -{}, --{}", sn, i).as_str());
length = 20 - i.len();
}
None => {
self.help.push_str(format!(" --{}", i).as_str());
length = 24 - i.len();
}
}
if let Some(value) = arg.take_value() {
length -= value.len() + 1;
self.help
.push_str(format!("={}", value.to_uppercase()).as_str());
}
if length <= 2 {
self.help.push_str(" ");
} else {
self.help.push_str(&" ".repeat(length));
}
self.help.push_str(&arg.help_msg());
self.help.push('\n');
}
}
fn mk_add_pos_internal(&self) -> (TokenStream2, TokenStream2, TokenStream2, TokenStream2) {
let RevparseInt {
min_pos_args,
max_pos_args,
infinite_pos_args,
exec_name,
..
} = self;
let decide_type = |x: &u64, field_name: Ident| {
if *x <= std::u8::MAX as u64 {
quote! {
#field_name: RefCell<u8>,
}
} else if *x <= std::u16::MAX as u64 {
quote! {
#field_name: RefCell<u16>,
}
} else if *x <= std::u32::MAX as u64 {
quote! {
#field_name: RefCell<u32>,
}
} else {
quote! {
#field_name: RefCell<u64>,
}
}
};
let init_number_as_lowest_type = |x: &u64, field_name: Ident| {
if *x <= std::u8::MAX as u64 {
let num = *x as u8;
quote! {
#field_name: RefCell::new(#num),
}
} else if *x <= std::u16::MAX as u64 {
let num = *x as u16;
quote! {
#field_name: RefCell::new(#num),
}
} else if *x <= std::u32::MAX as u64 {
let num = *x as u32;
quote! {
#field_name: RefCell::new(#num),
}
} else {
let num = *x;
quote! {
#field_name: RefCell::new(#num),
}
}
};
let min_ident = Ident::new("min_pos_args", Span::call_site());
let max_ident = Ident::new("max_pos_args", Span::call_site());
let min_pos_args_field = decide_type(min_pos_args, min_ident.clone());
let max_pos_args_field = decide_type(max_pos_args, max_ident.clone());
let raw_add = quote! {
self.pos_args.borrow_mut().as_mut().unwrap().push(arg);
};
let pos_struct_field = quote! {
pos_args: RefCell<Option<Vec<String>>>,
};
let pos_init = quote! {
pos_args: RefCell::new(Some(Vec::new())),
};
let min_init = init_number_as_lowest_type(min_pos_args, min_ident);
let max_init = init_number_as_lowest_type(max_pos_args, max_ident);
let main_function;
let declare_fields;
let init_fields;
let control_function;
if *infinite_pos_args && *min_pos_args == 0 {
main_function = quote! {
fn add_pos_internal(&self, arg: String) {
#raw_add
}
};
declare_fields = quote! {
#pos_struct_field
};
init_fields = quote! {
#pos_init
};
control_function = quote! {
fn check_pos(&self) {
return;
}
};
} else if *infinite_pos_args {
main_function = quote! {
fn add_pos_internal(&self, arg: String) {
if *self.min_pos_args.borrow() != 0 {
*self.min_pos_args.borrow_mut() -= 1;
}
#raw_add
}
};
declare_fields = quote! {
#pos_struct_field
#min_pos_args_field
};
init_fields = quote! {
#pos_init
#min_init
};
control_function = quote! {
fn check_pos(&self) {
if *self.min_pos_args.borrow() != 0 {
print_usage();
exit(1);
}
}
};
} else if *min_pos_args == 0 {
main_function = quote! {
fn add_pos_internal(&self, arg: String) {
if *self.max_pos_args.borrow() == 0 {
err_extra_operand(&arg);
exit(1);
}
*self.max_pos_args.borrow_mut() -= 1;
#raw_add
}
};
declare_fields = quote! {
#pos_struct_field
#max_pos_args_field
};
init_fields = quote! {
#pos_init
#max_init
};
control_function = quote! {
fn check_pos(&self) {
return;
}
}
} else {
main_function = quote! {
fn add_pos_internal(&self, arg: String) {
if *self.max_pos_args.borrow() == 0 {
err_extra_operand(&arg);
exit(1);
}
*self.max_pos_args.borrow_mut() -= 1;
if *self.min_pos_args.borrow() != 0 {
*self.min_pos_args.borrow_mut() -= 1;
}
#raw_add
}
};
declare_fields = quote! {
#pos_struct_field
#min_pos_args_field
#max_pos_args_field
};
init_fields = quote! {
#pos_init
#min_init
#max_init
};
control_function = quote! {
fn check_pos(&self) {
if *self.min_pos_args.borrow() != 0 {
println!("{}\nTry '{} --help' for more information.", USAGE, #exec_name);
exit(1);
}
}
};
}
(main_function, declare_fields, init_fields, control_function)
}
fn dec_mut_struct_fields(&self) -> TokenStream2 {
self.args
.iter()
.map(|flag| {
use ArgKind::*;
match flag {
Both { long, .. } | NoShort { long, .. } => quote! {
#long: ArgVal,
},
NoVal { long, .. } | Neither { long, .. } => quote! {
#long: ArgBool,
},
_ => panic!("{}", IMPOSSIBLE_ERROR),
}
})
.collect()
}
fn dec_inner_struct_fields(&self, dec_pos_fields: Option<TokenStream2>) -> TokenStream2 {
let dec_fields = match dec_pos_fields {
Some(fields) => quote! {#fields},
None => TokenStream2::new(),
};
quote! {
hashmap_long: HashMap<&'static str, &'a dyn Argument>,
hashmap_short: HashMap<char, &'a dyn Argument>,
#dec_fields
}
}
fn init_inner_struct_fields(&self, init_pos_fields: Option<TokenStream2>) -> TokenStream2 {
match init_pos_fields {
Some(tokens) => quote! {
hashmap_long: HashMap::new(),
hashmap_short: HashMap::new(),
#tokens
},
None => quote! {
hashmap_long: HashMap::new(),
hashmap_short: HashMap::new(),
},
}
}
fn init_hashmap(&self) -> TokenStream2 {
(&self)
.args
.iter()
.map(|i| {
use ArgKind::*;
let (long, short) = match i {
Both { long, short, .. } | NoVal { long, short, .. } => (long, Some(short)),
NoShort { long, .. } | Neither { long, .. } => (long, None),
_ => panic!("{}", IMPOSSIBLE_ERROR),
};
let long_str = format!("--{}", long.unraw().to_string().replace('_', "-"));
if let Some(short) = short {
quote! {
inner.hashmap_long.insert(#long_str, &mut_rvp.#long);
inner.hashmap_short.insert(#short, &mut_rvp.#long);
}
} else {
quote! {
inner.hashmap_long.insert(#long_str, &mut_rvp.#long);
}
}
})
.collect()
}
fn init_mut_struct_fields(&self) -> TokenStream2 {
(&self)
.args
.iter()
.map(|i| {
use ArgKind::*;
match i {
Both { long, short, .. } => {
let long_str = format!("--{}", long.to_string().replace('_', "-"));
quote! {
#long: ArgVal { value: RefCell::new(None), kind: ArgKind::Short(#long_str, #short)},
}
},
NoVal { long, short, .. } => {
let long_str = format!("--{}", long.to_string().replace('_', "-"));
quote! {
#long: ArgBool { value: RefCell::new(false), kind: ArgKind::Short(#long_str, #short)},
}
},
NoShort { long, .. } => {
let long_str = format!("--{}", long.to_string().replace('_', "-"));
quote! {
#long: ArgVal { value: RefCell::new(None), kind: ArgKind::NoShort(#long_str)},
}
},
Neither { long, .. } => {
let long_str = format!("--{}", long.to_string().replace('_', "-"));
quote! {
#long: ArgBool { value: RefCell::new(false), kind: ArgKind::NoShort(#long_str)},
}
},
_ => panic!("{}", IMPOSSIBLE_ERROR),
}
})
.collect()
}
fn dec_pub_struct_fields(&self) -> TokenStream2 {
self.args
.iter()
.map(|i| {
use ArgKind::*;
match i {
Both { long, .. } | NoShort { long, .. } => {
quote! {
pub #long: Option<String>,
}
}
NoVal { long, .. } | Neither { long, .. } => {
quote! {
pub #long: bool,
}
}
_ => panic!("{}", IMPOSSIBLE_ERROR),
}
})
.collect()
}
fn init_pub_struct_fields(&self) -> TokenStream2 {
self.args
.iter()
.map(|i| {
use ArgKind::*;
match i {
Both { long, .. } | NoShort { long, .. } => {
quote! {
#long: mut_rvp.#long.value.borrow_mut().take(),
}
}
NoVal { long, .. } | Neither { long, .. } => {
quote! {
#long: *mut_rvp.#long.value.borrow(),
}
}
_ => panic!("{}", IMPOSSIBLE_ERROR),
}
})
.collect()
}
fn mk_module(&self) -> TokenStream2 {
let positional_logic =
self.pres_pos_args.len() != 0 || self.custom_max_pos_args || self.infinite_pos_args;
let RevparseInt {
help,
usage,
exec_name,
..
} = self;
let dec_mut_struct_fields = self.dec_mut_struct_fields();
let init_mut_struct_fields = self.init_mut_struct_fields();
let dec_pub_struct_fields = self.dec_pub_struct_fields();
let init_pub_struct_fields = self.init_pub_struct_fields();
let dec_inner_struct_fields;
let init_inner_struct_fields;
let pos_control_function;
let init_hashmap = self.init_hashmap();
let add_pos_internal;
let parser_logic;
let get_pos_args_function;
let rvp_pos_args_field_init;
let rvp_pos_args_field_dec;
if positional_logic {
let (main_function, declare_fields, init_fields, control_function) =
self.mk_add_pos_internal();
dec_inner_struct_fields = self.dec_inner_struct_fields(Some(declare_fields));
init_inner_struct_fields = self.init_inner_struct_fields(Some(init_fields));
pos_control_function = control_function;
add_pos_internal = main_function;
get_pos_args_function = quote! {
pub fn get_pos_args(&mut self) -> Vec<String> {
self._pos_args.take().unwrap_or_else(|| panic!("get_pos_args() was called before and moved the Vec<String>. Consider binding it to a variable."))
}
};
rvp_pos_args_field_init = quote! {
_pos_args: inner.pos_args.borrow_mut().take(),
};
rvp_pos_args_field_dec = quote! {
_pos_args: Option<Vec<String>>,
};
parser_logic = quote! {
let mut next_is_val: Option<(&dyn Argument, bool)> = None;
let mut next_is_pos = false;
for e_arg in args.skip(1) {
if let Some((arg_struct, _)) = next_is_val.take() {
(*arg_struct).insert(e_arg);
} else if next_is_pos {
next_is_pos = false;
inner.add_pos_internal(e_arg);
} else if e_arg == "--help" || e_arg == "-h" {
print_help();
exit(0);
} else if e_arg == "--" {
next_is_pos = true;
} else if e_arg.starts_with("--") {
match e_arg
.split_once('=')
.map(|(arg_name, val)| (arg_name, val.to_string()))
{
Some((arg_name, val)) => {
let arg_struct = inner.get_str(&arg_name);
if (*arg_struct).take_val() {
(*arg_struct).insert(val);
} else {
err_no_val_allowed(&arg_name);
exit(1);
}
},
None => {
let arg_struct = inner.get_str(&e_arg);
if (*arg_struct).take_val() {
next_is_val = Some((arg_struct, true));
} else {
(*arg_struct).was_called();
}
},
}
} else if e_arg == "-" {
err_arg_does_not_exist("-");
exit(1);
} else if e_arg.starts_with('-') {
let mut rest_is_val: Option<&dyn Argument> = None;
let mut value = String::new();
for e_char in e_arg.chars().skip(1) {
if rest_is_val.is_some() {
value.push(e_char);
} else {
if e_char == 'h' {
print_help();
exit(0);
}
let arg_struct = inner.get_char(e_char);
if (*arg_struct).take_val() {
rest_is_val = Some(arg_struct);
} else {
(*arg_struct).was_called();
}
}
}
if let Some(arg_struct) = rest_is_val.take() {
if value.len() == 0 {
next_is_val = Some((arg_struct, false));
} else {
(*arg_struct).insert(value);
}
}
} else {
inner.add_pos_internal(e_arg);
}
}
if let Some((arg_struct, is_long)) = next_is_val.take() {
if is_long {
err_opt_requires_arg((*arg_struct).long());
exit(1);
} else {
err_short_opt_requires_arg((*arg_struct).short());
exit(1);
}
}
inner.check_pos();
};
} else {
dec_inner_struct_fields = self.dec_inner_struct_fields(None);
init_inner_struct_fields = self.init_inner_struct_fields(None);
get_pos_args_function = TokenStream2::new();
pos_control_function = TokenStream2::new();
add_pos_internal = TokenStream2::new();
rvp_pos_args_field_init = TokenStream2::new();
rvp_pos_args_field_dec = TokenStream2::new();
parser_logic = quote! {
let mut next_is_val: Option<(&dyn Argument, bool)> = None;
for e_arg in args.skip(1) {
if let Some((arg_struct, _)) = next_is_val.take() {
(*arg_struct).insert(e_arg);
} else if e_arg == "--help" || e_arg == "-h" {
print_help();
exit(0);
} else if e_arg == "--" {
err_arg_does_not_exist("--");
exit(1);
} else if e_arg.starts_with("--") {
match e_arg
.split_once('=')
.map(|(arg_name, val)| (arg_name, val.to_string()))
{
Some((arg_name, val)) => {
let arg_struct = inner.get_str(&arg_name);
if (*arg_struct).take_val() {
(*arg_struct).insert(val);
} else {
err_no_val_allowed(&arg_name);
exit(1);
}
},
None => {
let arg_struct = inner.get_str(&e_arg);
if (*arg_struct).take_val() {
next_is_val = Some((arg_struct, true));
} else {
(*arg_struct).was_called();
}
},
}
} else if e_arg == "-" {
err_arg_does_not_exist("-");
exit(1);
} else if e_arg.starts_with('-') {
let mut rest_is_val: Option<&dyn Argument> = None;
let mut value = String::new();
for e_char in e_arg.chars().skip(1) {
if rest_is_val.is_some() {
value.push(e_char);
} else {
if e_char == 'h' {
print_help();
exit(0);
}
let arg_struct = inner.get_char(e_char);
if (*arg_struct).take_val() {
rest_is_val = Some(arg_struct);
} else {
(*arg_struct).was_called();
}
}
}
if let Some(arg_struct) = rest_is_val.take() {
if value.len() == 0 {
next_is_val = Some((arg_struct, false));
} else {
(*arg_struct).insert(value);
}
}
} else {
err_arg_does_not_exist(&e_arg);
exit(1);
}
}
if let Some((arg_struct, is_long)) = next_is_val.take() {
if is_long {
err_opt_requires_arg((*arg_struct).long());
exit(1);
} else {
err_short_opt_requires_arg((*arg_struct).short());
exit(1);
}
}
};
}
quote! {
#[cfg(test)]
fn exit(code: i32) -> ! {
panic!("Exit code: {}", code);
}
#[cfg(not(test))]
fn exit(code: i32) -> ! {
std::process::exit(code);
}
fn err_arg_does_not_exist(arg: &str) {
if arg == "--help" {
err_no_val_allowed(arg);
} else {
eprintln!(
"{}: unrecognized option '{}'\n{}\nTry '{} --help' for more information.",
#exec_name,
arg,
USAGE,
#exec_name
);
}
}
fn err_short_arg_does_not_exist(ch: char) {
eprintln!(
"{}: invalid option -- '{}'\n{}\nTry '{} --help' for more information.",
#exec_name,
ch,
USAGE,
#exec_name,
);
}
fn err_extra_operand(arg: &str) {
eprintln!(
"{}: extra operand '{}'\n{}\nTry '{} --help' for more information.",
#exec_name,
arg,
USAGE,
#exec_name,
);
}
fn err_no_val_allowed(arg: &str) {
eprintln!(
"{}: option '{}' doesn't allow an argument\n{}\nTry '{} --help' for more information.",
#exec_name,
arg,
USAGE,
#exec_name,
);
}
fn err_opt_requires_arg(arg: &str) {
eprintln!(
"{}: option '{}' requires an argument\n{}\nTry '{} --help' for more information.",
#exec_name,
arg,
USAGE,
#exec_name,
);
}
fn err_short_opt_requires_arg(ch: char) {
eprintln!(
"{}: option requires an argument -- '{}'\n{}\nTry '{} --help' for more information.",
#exec_name,
ch,
USAGE,
#exec_name,
);
}
use std::cell::RefCell;
use std::collections::HashMap;
const USAGE: &str = #usage;
const HELP: &str = #help;
#[derive(Debug)]
pub struct Revparse {
#rvp_pos_args_field_dec
#dec_pub_struct_fields
}
struct Inner<'a> {
#dec_inner_struct_fields
}
struct MutRevparse {
#dec_mut_struct_fields
}
enum ArgKind {
Short(&'static str, char),
NoShort(&'static str),
}
pub struct ArgVal {
pub value: RefCell<Option<String>>,
kind: ArgKind,
}
pub struct ArgBool {
pub value: RefCell<bool>,
kind: ArgKind,
}
trait Argument {
fn take_val(&self) -> bool;
fn insert(&self, arg: String);
fn was_called(&self);
fn long(&self) -> &str;
fn short(&self) -> char;
}
impl Argument for ArgVal {
fn take_val(&self) -> bool {
true
}
fn insert(&self, arg: String) {
*self.value.borrow_mut() = Some(arg);
}
fn was_called(&self) {
panic!("impossible");
}
fn long(&self) -> &str {
use ArgKind::*;
match self.kind {
Short(long, _) | NoShort(long) => long,
}
}
fn short(&self) -> char {
use ArgKind::*;
match self.kind {
Short(_, short) => short,
_ => panic!("logic error"),
}
}
}
impl Argument for ArgBool {
fn take_val(&self) -> bool {
false
}
fn insert(&self, _: String) {
panic!("impossible");
}
fn was_called(&self) {
*self.value.borrow_mut() = true;
}
fn long(&self) -> &str {
use ArgKind::*;
match self.kind {
Short(long, _) | NoShort(long) => long,
}
}
fn short(&self) -> char {
use ArgKind::*;
match self.kind {
Short(_, short) => short,
_ => panic!("logic error"),
}
}
}
pub fn print_help() {
print!("{}{}", USAGE, HELP);
}
pub fn print_usage() {
println!("{}", USAGE);
}
impl Revparse {
pub fn custom_new(args: impl Iterator<Item = String>) -> Self {
let mut_rvp = MutRevparse {
#init_mut_struct_fields
};
let mut inner = Inner {
#init_inner_struct_fields
};
#init_hashmap
#parser_logic
Self {
#rvp_pos_args_field_init
#init_pub_struct_fields
}
}
pub fn new() -> Self {
Self::custom_new(std::env::args())
}
#get_pos_args_function
}
impl Inner<'_> {
#pos_control_function
#add_pos_internal
fn get_str(&self, string: &str) -> &dyn Argument {
match self.hashmap_long.get(string) {
Some(arg_struct) => *arg_struct,
None => {
err_arg_does_not_exist(string);
exit(1);
},
}
}
fn get_char(&self, ch: char) -> &dyn Argument {
match self.hashmap_short.get(&ch) {
Some(arg_struct) => *arg_struct,
None => {
err_short_arg_does_not_exist(ch);
exit(1);
},
}
}
}
}
}
}
impl Parse for RevparseInt {
fn parse(input: ParseStream) -> Result<Self> {
let mut revparse_int = RevparseInt {
exec_name: String::from("program_name"),
usage: String::new(),
help: String::new(),
min_pos_args: 0,
custom_max_pos_args: false,
max_pos_args: 0,
pos_arg_help: None,
infinite_pos_args: false,
args: Vec::new(),
pres_pos_args: Vec::new(),
mod_name: Ident::new("revmod", Span::call_site()),
};
loop {
if input.is_empty() {
break;
}
let content;
bracketed!(content in input);
let parsed_content: ArgKind = content.parse()?;
match parsed_content {
ArgKind::Setting(arg_setting) => revparse_int.match_setting(arg_setting),
_ => revparse_int.args.push(parsed_content),
}
input.parse::<Token![;]>()?;
}
revparse_int.create_help();
if !revparse_int.custom_max_pos_args {
revparse_int.max_pos_args = revparse_int.pres_pos_args.len() as u64;
}
Ok(revparse_int)
}
}
#[proc_macro]
pub fn revparse(input: TokenStream) -> TokenStream {
let revparse_int = parse_macro_input!(input as RevparseInt);
let module = revparse_int.mk_module();
let RevparseInt { mod_name, .. } = revparse_int;
quote! {
mod #mod_name {
#module
}
}
.into()
}