Add u16 argument type

This commit is contained in:
Ben Bridle 2022-08-25 21:16:47 +12:00
parent b61bf1f827
commit 3e97fee691
4 changed files with 166 additions and 33 deletions

View File

@ -2,7 +2,7 @@
name = "switches"
version = "1.0.0"
authors = ["Ben Bridle <bridle.benjamin@gmail.com>"]
edition = "2018"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

View File

@ -1,2 +1,46 @@
mod switches;
pub use switches::Switches;
pub use switches::{Switches, SwitchesError as Error};
#[macro_export]
macro_rules! generate {
(
$structname:ident {
$($field:ident: $type:tt ($($opt:expr),+)),* $(,)?
}
) => {
macro_rules! build {
($f:ident, PathBuf) => {$f.as_path()};
($f:ident, Option<PathBuf>) => {$f.as_opt_path()};
($f:ident, String) => {$f.as_string()};
($f:ident, Option<String>) => {$f.as_opt_string()};
($f:ident, bool) => {$f.as_flag()};
($f:ident, u16) => {$f.as_u16()};
}
pub struct $structname {
$(pub $field: $type),*
}
impl $structname {
pub fn parse() -> Result<Self, switches::Error> {
let mut switches = switches::Switches::from_env();
$(
let mut $field = switches$(.or($opt))+;
let $field = build!($field, $type)?;
)*
Ok(Self {
$($field,)*
})
}
pub fn parse_and_raise() -> Self {
match Self::parse() {
Ok(parsed) => parsed,
Err(err) => {
eprintln!("[ERROR] {}", err);
std::process::exit(1);
}
}
}
}
};
}

16
src/main.rs Normal file
View File

@ -0,0 +1,16 @@
use std::path::PathBuf;
switches::generate! {
Args {
source_folder: PathBuf ("-f", "--folder"),
name: String ("-n", "--name"),
delete: bool ("--delete"),
}
}
pub fn main() {
let args = Args::parse_and_raise();
println!(" Folder: {:?}", args.source_folder);
println!(" Name: {:?}", args.name);
println!(" Delete: {:?}", args.delete);
}

View File

@ -1,3 +1,14 @@
use std::path::PathBuf;
macro_rules! return_none {
($x:expr) => {
match $x {
Some(v) => v,
None => return Ok(None),
}
};
}
pub struct Switches {
args: Vec<String>,
}
@ -11,24 +22,20 @@ impl Switches {
}
pub fn get(&mut self, switch: &str) -> SwitchBuilder {
let builder = SwitchBuilder::new(&mut self.args);
builder.or(switch)
SwitchBuilder::new(&mut self.args).or(switch)
}
pub fn check_for_unused_arguments(&mut self) -> Result<(), Error> {
if self.args.len() > 0 {
Err(Error::UnexpectedArguments(self.args.clone()))
pub fn or(&mut self, switch: &str) -> SwitchBuilder {
self.get(switch)
}
pub fn check_for_unused_arguments(&mut self) -> Result<(), SwitchesError> {
if !self.args.is_empty() {
Err(SwitchesError::UnexpectedArguments(self.args.clone()))
} else {
Ok(())
}
}
pub fn get_value(&mut self) -> Option<String> {
if self.args.is_empty() {
return None;
};
Some(self.args.remove(0))
}
}
pub struct SwitchBuilder<'a> {
@ -39,7 +46,7 @@ pub struct SwitchBuilder<'a> {
}
impl<'a> SwitchBuilder<'a> {
pub fn new(args: &'a mut Vec<String>) -> Self {
fn new(args: &'a mut Vec<String>) -> Self {
Self {
args,
matched_indices: Vec::new(),
@ -56,24 +63,30 @@ impl<'a> SwitchBuilder<'a> {
self.matched_switches.push(arg.to_string());
}
}
return self;
self
}
pub fn as_flag(mut self) -> Result<bool, Error> {
pub fn as_flag(&mut self) -> Result<bool, SwitchesError> {
let result = match self.matched_indices.len() {
0 => Ok(false),
1 => Ok(true),
_ => Err(Error::MultipleMatchesFound(self.matched_switches.clone())),
_ => Err(SwitchesError::MultipleMatchesFound(
self.matched_switches.clone(),
)),
};
self.remove_matches();
return result;
result
}
pub fn as_opt_string(&mut self) -> Result<Option<String>, Error> {
pub fn as_opt_string(&mut self) -> Result<Option<String>, SwitchesError> {
let switch_index = match self.matched_indices.len() {
0 => return Ok(None),
1 => self.matched_indices[0],
_ => return Err(Error::MultipleMatchesFound(self.matched_switches.clone())),
_ => {
return Err(SwitchesError::MultipleMatchesFound(
self.matched_switches.clone(),
))
}
};
let switch = self.matched_switches[0].clone();
let value_index = switch_index + 1;
@ -82,19 +95,48 @@ impl<'a> SwitchBuilder<'a> {
self.matched_indices.push(value_index);
value.to_string()
}
None => return Err(Error::NoValueForSwitch(switch)),
None => return Err(SwitchesError::NoValueForSwitch(switch)),
};
if value.starts_with("-") {
return Err(Error::NoValueForSwitch(switch));
if value.starts_with('-') {
return Err(SwitchesError::NoValueForSwitch(switch));
};
self.remove_matches();
return Ok(Some(value));
Ok(Some(value))
}
pub fn as_string(&mut self) -> Result<String, Error> {
match self.as_opt_string()? {
pub fn as_opt_path(&mut self) -> Result<Option<PathBuf>, SwitchesError> {
let string = return_none!(self.as_opt_string()?);
let mut path = std::env::current_dir().unwrap();
path.push(PathBuf::from(string));
Ok(Some(path))
}
pub fn as_opt_u16(&mut self) -> Result<Option<u16>, SwitchesError> {
let string = return_none!(self.as_opt_string()?);
let int = string.parse().unwrap();
Ok(Some(int))
}
pub fn as_string(&mut self) -> Result<String, SwitchesError> {
let string = self.as_opt_string();
self.make_mandatory(string)
}
pub fn as_path(&mut self) -> Result<PathBuf, SwitchesError> {
let path = self.as_opt_path();
self.make_mandatory(path)
}
pub fn as_u16(&mut self) -> Result<u16, SwitchesError> {
let int = self.as_opt_u16();
self.make_mandatory(int)
}
fn make_mandatory<T>(
&mut self,
result: Result<Option<T>, SwitchesError>,
) -> Result<T, SwitchesError> {
match result? {
Some(value) => Ok(value),
None => Err(Error::NoMatchFound(self.all_switches.clone())),
None => Err(SwitchesError::NoMatchFound(self.all_switches.clone())),
}
}
@ -105,9 +147,40 @@ impl<'a> SwitchBuilder<'a> {
}
}
pub enum Error {
MultipleMatchesFound(Vec<String>), // All of the switches that were matched
NoMatchFound(Vec<String>), // All of the switches that were looked for
NoValueForSwitch(String), //
UnexpectedArguments(Vec<String>),
fn join(vec: &[String]) -> String {
let mut output = String::new();
for (i, string) in vec.iter().enumerate() {
match i == vec.len() - 1 {
true => output.push_str(&format!("'{}'", string)),
false => output.push_str(&format!("'{}', ", string)),
}
}
output
}
pub enum SwitchesError {
MultipleMatchesFound(Vec<String>), // contains all of the switches that were matched
NoMatchFound(Vec<String>), // contains all of the switches that were looked for
NoValueForSwitch(String),
UnexpectedArguments(Vec<String>),
InvalidValue(String, String, String),
}
impl std::fmt::Display for SwitchesError {
#[rustfmt::skip]
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
let string = match self {
SwitchesError::MultipleMatchesFound(matches) => {
format!("Multiple conflicting arguments were provided: {}", join(matches))}
SwitchesError::NoMatchFound(switches) => {
format!("One of these arguments must be provided: {}", join(switches))}
SwitchesError::NoValueForSwitch(switch) => {
format!("This argument needs to be followed by a value: '{}'", switch)}
SwitchesError::UnexpectedArguments(args) => {
format!("Unexpected arguments were provided: {}", join(args))}
SwitchesError::InvalidValue(value, arg, ty) => {
format!("A non-{} value was passed to '{}': {}", ty, arg, value)}
};
write!(f, "{}", string)
}
}