use migformatting::Formatting;
use std::{env};
mod argument;
use argument::Content;
pub use argument::{Argument, ArgumentOption, ArgumentType, DataType, ExtractFromContents, ListType, ContentList};
#[derive(Clone)]
pub struct ArgumentParser {
arguments: Vec<Argument>,
positional_cursor: i32,
}
impl ArgumentParser {
pub fn new() -> ArgumentParser {
ArgumentParser {
arguments: vec![],
positional_cursor: 1,
}
}
fn add_flag(
&mut self,
name: String,
identifiers: Vec<String>,
options: Option<Vec<ArgumentOption>>,
default_val: Option<Content>,
) -> Result<(), String> {
self.arguments
.push(Argument::new_flag(&name, identifiers, options, default_val));
Ok(())
}
fn add_positional(
&mut self,
name: String,
identifiers: Vec<String>,
data_type: DataType,
options: Option<Vec<ArgumentOption>>,
index: i32,
) -> Result<(), String> {
self.arguments.push(Argument::new_positional(
&name,
identifiers,
data_type,
options,
index,
));
Ok(())
}
fn add_optional(
&mut self,
name: String,
identifiers: Vec<String>,
data_type: DataType,
options: Option<Vec<ArgumentOption>>,
default_val: Option<Content>,
n_args_: usize
) -> Result<(), String> {
self.arguments.push(Argument::new_optional(
&name,
identifiers,
data_type,
options,
default_val,
n_args_
));
Ok(())
}
fn parse_text<T: std::str::FromStr>(text: &String) -> Option<T> {
let parsed = text.parse::<T>().ok();
match parsed {
Some(c) => Some(c),
None => None,
}
}
fn parse_value(text: &String, type_: &DataType) -> Option<Content> {
let res: Option<Content> = match type_ {
DataType::Int => {
let parsed = ArgumentParser::parse_text::<i32>(text);
if let Some(c) = parsed {
return Some(Content::Int(c));
} else {
return None;
}
}
DataType::Uint => {
let parsed = ArgumentParser::parse_text::<u32>(text);
if let Some(c) = parsed {
return Some(Content::Uint(c));
} else {
return None;
}
}
DataType::Bool => {
let parsed = ArgumentParser::parse_text::<bool>(text);
if let Some(c) = parsed {
return Some(Content::Bool(c));
} else {
return None;
}
}
DataType::String => Some(Content::String(text.clone())),
DataType::Float => {
let parsed = ArgumentParser::parse_text::<f32>(text);
if let Some(c) = parsed {
return Some(Content::Float(c));
} else {
return None;
}
},
DataType::List(t) => {
let values_txt = text.trim().split(' ').collect::<Vec<&str>>();
let mut result = ContentList::new(t.clone());
match t {
ListType::Bool => {
for i in values_txt {
let v = ArgumentParser::parse_text::<bool>(&i.to_owned()).unwrap();
result.data.push(Content::Bool(v));
}
},
ListType::Int => {
for i in values_txt {
let v = ArgumentParser::parse_text::<i32>(&i.to_owned()).unwrap();
result.data.push(Content::Int(v));
}
},
ListType::Uint => {
for i in values_txt {
let v = ArgumentParser::parse_text::<u32>(&i.to_owned()).unwrap();
result.data.push(Content::Uint(v));
}
},
ListType::String => {
for i in values_txt {
let v = ArgumentParser::parse_text::<String>(&i.to_owned()).unwrap();
result.data.push(Content::String(v));
}
},
ListType::Float => {
for i in values_txt {
let v = ArgumentParser::parse_text::<f32>(&i.to_owned()).unwrap();
result.data.push(Content::Float(v));
}
},
}
Some(Content::List(result))
}
};
res
}
fn parse_arg(
&mut self,
cl_arguments: &Vec<String>,
used_cl_args: &mut Vec<bool>,
argument_ix: usize,
) {
let mut argument = self.arguments[argument_ix].clone();
let arg_name = argument.name.clone();
let arg_type = argument.get_type();
let data_type = argument.data_type.clone();
let cl_n_args: usize = cl_arguments.len();
for (i, arg) in cl_arguments.iter().enumerate() {
if i != 0 {
match arg_type {
ArgumentType::Flag => {
if argument.has_identifier(arg) && !used_cl_args[i]
{
if argument.has_option(ArgumentOption::StoreTrue) {
argument.set_data(Content::Bool(true));
} else {
argument.set_data(Content::Bool(false));
}
argument.set_parsed();
}
}
ArgumentType::Positional => {
if i32::try_from(i).unwrap() == argument.get_index() && !used_cl_args[i] {
let data = ArgumentParser::parse_value(
&cl_arguments[i],
&data_type,
);
if let Some(d) = data {
argument.set_data(d);
}
argument.set_parsed();
} else if i32::try_from(i).unwrap() > argument.get_index() {
break;
}
}
ArgumentType::Optional => {
if argument.has_identifier(arg) && !used_cl_args[i]
{
let n_args = argument.n_args.clone();
if i + n_args > cl_n_args {
println!("Not enough arguments for list ({n_args},{cl_n_args})! ");
panic!();
}
let data_args = cl_arguments[i + 1 .. i + argument.n_args + 1].to_vec();
let mut data_txt: String = String::new();
for i in data_args {
data_txt.push_str(&i);
data_txt.push(' ');
}
let data =
ArgumentParser::parse_value(&data_txt, &data_type);
if let Some(d) = data {
argument.set_data(d);
}
argument.set_parsed();
for j in 0..n_args {
used_cl_args[i + j] = true;
}
}
}
}
if argument.is_parsed(){
used_cl_args[i] = true;
self.arguments[argument_ix] = argument.clone();
break;
}
}
}
if !argument.is_parsed() && argument.has_option(ArgumentOption::Necessary) {
println!(
"{}",
format!("Necessary argument '{}' is not present", arg_name).error()
);
panic!();
}
}
pub fn add_argument(
&mut self,
name: &str,
alias: Option<Vec<String>>,
data_type: DataType,
options_: Option<Vec<ArgumentOption>>,
default_value: Option<Content>,
) -> Result<(), String> {
let mut data: Option<Content> = default_value.clone();
let mut options = options_.unwrap_or_default();
if data_type == DataType::Bool {
if options.contains(&ArgumentOption::StoreFalse) {
data = Some(Content::Bool(true));
} else {
data = Some(Content::Bool(false));
if !options.contains(&ArgumentOption::StoreFalse) {
options.push(ArgumentOption::StoreTrue);
}
}
}
let arg_name = match Argument::parse_name(name) {
Some(n) => n,
None => name.into(),
};
let cl_name = name.to_owned(); let mut identifiers = vec![cl_name.clone()];
if let Some(mut i) = alias {
identifiers.append(&mut i);
}
let n_args = Argument::get_n_args(&options);
let argument_type = Argument::guess_type(name, &options, &data_type);
match argument_type {
Some(t) => {
match t {
ArgumentType::Optional => {
self.add_optional(arg_name, identifiers, data_type, Some(options), data, n_args)?;
}
ArgumentType::Positional => {
if !options.contains(&ArgumentOption::Necessary) {
options.push(ArgumentOption::Necessary);
}
let index = self.positional_cursor;
self.positional_cursor = self.positional_cursor + 1;
self.add_positional(
arg_name,
identifiers,
data_type,
Some(options),
index,
)?;
}
ArgumentType::Flag => {
self.add_flag(arg_name, identifiers, Some(options), data)?;
}
}
}
None => {
return Err("Invalid name for arg!".to_owned());
}
}
Ok(())
}
pub fn parse_arguments(&mut self) {
let arguments: Vec<String> = env::args().collect();
println!("{}", format!("Arguments: \n {arguments:?}"));
let mut used_arguments: Vec<bool> = vec![false; arguments.len()];
for arg_ix in 0..self.arguments.len() {
self.parse_arg(&arguments, &mut used_arguments, arg_ix);
}
}
pub fn parse_arguments_from_text(&mut self, text: String) {
let mut arguments: Vec<String> = text.split(" ").map(|f| f.to_owned()).collect();
arguments.insert(0, "program_name".to_owned());
let mut used_arguments: Vec<bool> = vec![false; arguments.len()];
for arg_ix in 0..self.arguments.len() {
self.parse_arg(&arguments, &mut used_arguments, arg_ix);
}
}
pub fn get_value<T: ExtractFromContents>(&self, arg: &str) -> Option<T> {
let mut ret: Option<Content> = None;
for a in self.arguments.iter() {
if &a.name == arg {
ret = a.get_data();
}
}
match ret {
Some(c) => c.get_value(),
None => None,
}
}
pub fn print_data(&self) {
println!("##### Arguments");
for d in self.arguments.iter() {
let data = if let Some(c) = d.get_data() {
c.get_value_str()
} else {
"None".to_string()
};
println!("{:?} [{:?}] : {:?}", d.name, d.data_type, data);
}
println!("------");
}
}