You've already forked uutils-args
mirror of
https://github.com/uutils/uutils-args.git
synced 2026-06-10 16:13:08 -07:00
take positional arguments out of arguments enum
This commit is contained in:
@@ -57,7 +57,7 @@ jobs:
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: clippy
|
||||
args: --all-targets --all-features --workspace -- -D warnings
|
||||
args: --all-targets --features complete --workspace -- -D warnings
|
||||
|
||||
docs:
|
||||
name: Docs
|
||||
|
||||
@@ -7,13 +7,13 @@ behaviour that aligns with GNU coreutils.
|
||||
|
||||
## Features
|
||||
|
||||
- A derive macro for declarative argument definition.
|
||||
- Automatic help generation.
|
||||
- Positional and optional arguments.
|
||||
- Automatically parsing values into Rust types.
|
||||
- Define a custom exit code on errors.
|
||||
- Automatically accept unambiguous abbreviations of long options.
|
||||
- Handles invalid UTF-8 gracefully.
|
||||
- A derive macro for declarative argument definition.
|
||||
- Automatic help generation.
|
||||
- Positional and optional arguments.
|
||||
- Automatically parsing values into Rust types.
|
||||
- Define a custom exit code on errors.
|
||||
- Automatically accept unambiguous abbreviations of long options.
|
||||
- Handles invalid UTF-8 gracefully.
|
||||
|
||||
## When you should not use this library
|
||||
|
||||
@@ -50,16 +50,11 @@ enum Arg {
|
||||
/// Transform input text to uppercase
|
||||
#[arg("-c", "--caps")]
|
||||
Caps,
|
||||
|
||||
// This option takes a value:
|
||||
|
||||
// This option takes a value:
|
||||
/// Add exclamation marks to output
|
||||
#[arg("-e N", "--exclaim=N")]
|
||||
ExclamationMarks(u8),
|
||||
|
||||
// This is a positional argument, the range specifies that
|
||||
// at least one positional argument must be passed.
|
||||
#[arg("TEXT", 1..)]
|
||||
Text(String),
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
@@ -76,24 +71,17 @@ impl Options<Arg> for Settings {
|
||||
match arg {
|
||||
Arg::Caps => self.caps = true,
|
||||
Arg::ExclamationMarks(n) => self.exclamation_marks += n,
|
||||
Arg::Text(s) => {
|
||||
if self.text.is_empty() {
|
||||
self.text.push_str(&s);
|
||||
} else {
|
||||
self.text.push(' ');
|
||||
self.text.push_str(&s);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn run(args: &[&str]) -> String {
|
||||
let s = Settings::default().parse(args);
|
||||
let (s, operands) = Settings::default().parse(args);
|
||||
let text = operands.iter().map(|s| s.to_string_lossy()).collect::<Vec<_>>().join(" ");
|
||||
let mut output = if s.caps {
|
||||
s.text.to_uppercase()
|
||||
text.to_uppercase()
|
||||
} else {
|
||||
s.text
|
||||
text
|
||||
};
|
||||
for i in 0..s.exclamation_marks {
|
||||
output.push('!');
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
// For the full copyright and license information, please view the LICENSE
|
||||
// file that was distributed with this source code.
|
||||
|
||||
use std::ops::RangeInclusive;
|
||||
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::quote;
|
||||
use syn::{Attribute, Fields, FieldsUnnamed, Ident, Meta, Variant};
|
||||
@@ -27,10 +25,6 @@ pub enum ArgType {
|
||||
takes_value: bool,
|
||||
default: TokenStream,
|
||||
},
|
||||
Positional {
|
||||
num_args: RangeInclusive<usize>,
|
||||
last: bool,
|
||||
},
|
||||
Free {
|
||||
filters: Vec<syn::Ident>,
|
||||
},
|
||||
@@ -93,13 +87,6 @@ pub fn parse_argument(v: Variant) -> Vec<Argument> {
|
||||
hidden: opt.hidden,
|
||||
}
|
||||
}
|
||||
ArgAttr::Positional(pos) => {
|
||||
assert!(field.is_some(), "Positional arguments must have a field");
|
||||
ArgType::Positional {
|
||||
num_args: pos.num_args,
|
||||
last: pos.last,
|
||||
}
|
||||
}
|
||||
ArgAttr::Free(free) => ArgType::Free {
|
||||
filters: free.filters,
|
||||
},
|
||||
@@ -157,7 +144,6 @@ pub fn short_handling(args: &[Argument]) -> (TokenStream, Vec<char>) {
|
||||
ref default,
|
||||
hidden: _,
|
||||
} => (flags, takes_value, default),
|
||||
ArgType::Positional { .. } => continue,
|
||||
ArgType::Free { .. } => continue,
|
||||
};
|
||||
|
||||
@@ -207,7 +193,6 @@ pub fn long_handling(args: &[Argument], help_flags: &Flags) -> TokenStream {
|
||||
ref default,
|
||||
hidden: _,
|
||||
} => (flags, takes_value, default),
|
||||
ArgType::Positional { .. } => continue,
|
||||
ArgType::Free { .. } => continue,
|
||||
};
|
||||
|
||||
@@ -275,7 +260,6 @@ pub fn free_handling(args: &[Argument]) -> TokenStream {
|
||||
for arg @ Argument { arg_type, .. } in args {
|
||||
let filters = match arg_type {
|
||||
ArgType::Free { filters } => filters,
|
||||
ArgType::Positional { .. } => continue,
|
||||
ArgType::Option { .. } => continue,
|
||||
};
|
||||
|
||||
@@ -299,7 +283,6 @@ pub fn free_handling(args: &[Argument]) -> TokenStream {
|
||||
let flags = match arg_type {
|
||||
ArgType::Option { flags, .. } => flags,
|
||||
ArgType::Free { .. } => continue,
|
||||
ArgType::Positional { .. } => continue,
|
||||
};
|
||||
|
||||
for (prefix, _) in &flags.dd_style {
|
||||
@@ -338,74 +321,6 @@ pub fn free_handling(args: &[Argument]) -> TokenStream {
|
||||
)
|
||||
}
|
||||
|
||||
pub fn positional_handling(args: &[Argument]) -> (TokenStream, TokenStream) {
|
||||
let mut match_arms = Vec::new();
|
||||
// The largest index of the previous argument, so the the argument after this should
|
||||
// belong to the next argument.
|
||||
let mut last_index = 0;
|
||||
|
||||
// The minimum number of arguments needed to not return a missing argument error.
|
||||
let mut minimum_needed = 0;
|
||||
let mut missing_argument_checks = vec![];
|
||||
|
||||
for arg @ Argument { name, arg_type, .. } in args {
|
||||
let (num_args, last) = match arg_type {
|
||||
ArgType::Positional { num_args, last } => (num_args, last),
|
||||
ArgType::Option { .. } => continue,
|
||||
ArgType::Free { .. } => continue,
|
||||
};
|
||||
|
||||
if *num_args.start() > 0 {
|
||||
minimum_needed = last_index + num_args.start();
|
||||
missing_argument_checks.push(quote!(if positional_idx < #minimum_needed {
|
||||
missing.push(#name);
|
||||
}));
|
||||
}
|
||||
|
||||
last_index += num_args.end();
|
||||
|
||||
let expr = if *last {
|
||||
last_positional_expression(&arg.ident)
|
||||
} else {
|
||||
positional_expression(&arg.ident)
|
||||
};
|
||||
match_arms.push(quote!(0..=#last_index => { #expr }));
|
||||
}
|
||||
|
||||
let value_handling = quote!(
|
||||
*positional_idx += 1;
|
||||
Ok(Some(Argument::Custom(
|
||||
match positional_idx {
|
||||
#(#match_arms)*
|
||||
_ => return Err(lexopt::Arg::Value(value).unexpected().into()),
|
||||
}
|
||||
)))
|
||||
);
|
||||
|
||||
let missing_argument_checks = quote!(
|
||||
// We have the minimum number of required arguments overall.
|
||||
// So we don't need to check the others.
|
||||
if positional_idx >= #minimum_needed {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let mut missing: Vec<&str> = vec![];
|
||||
#(#missing_argument_checks)*
|
||||
if !missing.is_empty() {
|
||||
Err(uutils_args::Error {
|
||||
exit_code: Self::EXIT_CODE,
|
||||
kind: uutils_args::ErrorKind::MissingPositionalArguments(
|
||||
missing.iter().map(ToString::to_string).collect::<Vec<String>>()
|
||||
)
|
||||
})
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
);
|
||||
|
||||
(value_handling, missing_argument_checks)
|
||||
}
|
||||
|
||||
fn no_value_expression(ident: &Ident) -> TokenStream {
|
||||
quote!(Self::#ident)
|
||||
}
|
||||
@@ -424,22 +339,3 @@ fn optional_value_expression(ident: &Ident, default_expr: &TokenStream) -> Token
|
||||
fn required_value_expression(ident: &Ident) -> TokenStream {
|
||||
quote!(Self::#ident(::uutils_args::internal::parse_value_for_option(&option, &parser.value()?)?))
|
||||
}
|
||||
|
||||
fn positional_expression(ident: &Ident) -> TokenStream {
|
||||
// TODO: Add option name in this from_value call
|
||||
quote!(
|
||||
Self::#ident(::uutils_args::internal::parse_value_for_option("", &value)?)
|
||||
)
|
||||
}
|
||||
|
||||
fn last_positional_expression(ident: &Ident) -> TokenStream {
|
||||
// TODO: Add option name in this from_value call
|
||||
quote!({
|
||||
let raw_args = parser.raw_args()?;
|
||||
let collection = std::iter::once(value)
|
||||
.chain(raw_args)
|
||||
.map(|v| ::uutils_args::internal::parse_value_for_option("", &v))
|
||||
.collect::<Result<_,_>>()?;
|
||||
Self::#ident(collection)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
// For the full copyright and license information, please view the LICENSE
|
||||
// file that was distributed with this source code.
|
||||
|
||||
use std::ops::RangeInclusive;
|
||||
|
||||
use syn::{
|
||||
meta::ParseNestedMeta, parse::ParseStream, Attribute, Expr, ExprLit, ExprRange, Ident, Lit,
|
||||
LitInt, LitStr, RangeLimits, Token,
|
||||
meta::ParseNestedMeta, parse::ParseStream, Attribute, Expr, Ident, LitInt, LitStr, Token,
|
||||
};
|
||||
|
||||
use crate::flags::Flags;
|
||||
@@ -69,7 +66,6 @@ impl ArgumentsAttr {
|
||||
|
||||
pub enum ArgAttr {
|
||||
Option(OptionAttr),
|
||||
Positional(PositionalAttr),
|
||||
Free(FreeAttr),
|
||||
}
|
||||
|
||||
@@ -84,7 +80,7 @@ impl ArgAttr {
|
||||
if v.starts_with('-') || v.contains('=') {
|
||||
OptionAttr::from_args(v, s).map(Self::Option)
|
||||
} else {
|
||||
PositionalAttr::from_args(v, s).map(Self::Positional)
|
||||
panic!("Could not determine type of argument");
|
||||
}
|
||||
} else if let Ok(v) = s.parse::<syn::Ident>() {
|
||||
FreeAttr::from_args(v, s).map(Self::Free)
|
||||
@@ -170,82 +166,6 @@ impl FreeAttr {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct PositionalAttr {
|
||||
pub num_args: RangeInclusive<usize>,
|
||||
pub last: bool,
|
||||
}
|
||||
|
||||
impl Default for PositionalAttr {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
num_args: 1..=1,
|
||||
last: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PositionalAttr {
|
||||
pub fn from_args(_first_value: String, s: ParseStream) -> syn::Result<Self> {
|
||||
let mut positional_attr = Self::default();
|
||||
parse_args(s, |s| {
|
||||
if (s.peek(LitInt) && s.peek2(Token![..])) || s.peek(Token![..]) {
|
||||
let range = s.parse::<ExprRange>()?;
|
||||
// We're dealing with a range
|
||||
let from = match range.start.as_deref() {
|
||||
Some(Expr::Lit(ExprLit {
|
||||
lit: Lit::Int(i), ..
|
||||
})) => i.base10_parse::<usize>().unwrap(),
|
||||
None => 0,
|
||||
_ => panic!("Range must consist of usize"),
|
||||
};
|
||||
|
||||
let inclusive = matches!(range.limits, RangeLimits::Closed(_));
|
||||
let to = match range.end.as_deref() {
|
||||
Some(Expr::Lit(ExprLit {
|
||||
lit: Lit::Int(i), ..
|
||||
})) => {
|
||||
let n = i.base10_parse::<usize>().unwrap();
|
||||
if inclusive {
|
||||
Some(n)
|
||||
} else {
|
||||
Some(n - 1)
|
||||
}
|
||||
}
|
||||
None => None,
|
||||
_ => panic!("Range must consist of usize"),
|
||||
};
|
||||
|
||||
positional_attr.num_args = match to {
|
||||
Some(to) => from..=to,
|
||||
None => from..=usize::MAX,
|
||||
};
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if let Ok(int) = s.parse::<LitInt>() {
|
||||
let suffix = int.suffix();
|
||||
// FIXME: should be a proper error instead of assert!
|
||||
assert!(
|
||||
suffix.is_empty() || suffix == "usize",
|
||||
"The position index must be usize"
|
||||
);
|
||||
let n = int.base10_parse::<usize>().unwrap();
|
||||
positional_attr.num_args = n..=n;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let ident = s.parse::<Ident>()?;
|
||||
match ident.to_string().as_str() {
|
||||
"last" => positional_attr.last = true,
|
||||
_ => return Err(s.error("unrecognized keyword in value attribute")),
|
||||
}
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
Ok(positional_attr)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct ValueAttr {
|
||||
pub keys: Vec<String>,
|
||||
|
||||
@@ -51,7 +51,6 @@ pub fn help_string(
|
||||
}
|
||||
// Hidden arguments should not show up in --help
|
||||
ArgType::Option { hidden: true, .. } => {}
|
||||
ArgType::Positional { .. } => {}
|
||||
// TODO: Free arguments should show up in help
|
||||
ArgType::Free { .. } => {}
|
||||
}
|
||||
|
||||
+4
-11
@@ -9,8 +9,7 @@ mod help;
|
||||
mod help_parser;
|
||||
|
||||
use argument::{
|
||||
free_handling, long_handling, parse_argument, parse_arguments_attr, positional_handling,
|
||||
short_handling,
|
||||
free_handling, long_handling, parse_argument, parse_arguments_attr, short_handling,
|
||||
};
|
||||
use attributes::ValueAttr;
|
||||
use help::{help_handling, help_string, version_handling};
|
||||
@@ -36,9 +35,7 @@ pub fn arguments(input: TokenStream) -> TokenStream {
|
||||
let exit_code = arguments_attr.exit_code;
|
||||
let (short, short_flags) = short_handling(&arguments);
|
||||
let long = long_handling(&arguments, &arguments_attr.help_flags);
|
||||
// let number_argument = number_handling(&arguments);
|
||||
let free = free_handling(&arguments);
|
||||
let (positional, missing_argument_checks) = positional_handling(&arguments);
|
||||
let help_string = help_string(
|
||||
&arguments,
|
||||
&arguments_attr.help_flags,
|
||||
@@ -72,9 +69,9 @@ pub fn arguments(input: TokenStream) -> TokenStream {
|
||||
|
||||
#[allow(unreachable_code)]
|
||||
fn next_arg(
|
||||
parser: &mut uutils_args::lexopt::Parser, positional_idx: &mut usize
|
||||
parser: &mut ::uutils_args::lexopt::Parser
|
||||
) -> Result<Option<::uutils_args::Argument<Self>>, ::uutils_args::ErrorKind> {
|
||||
use uutils_args::{Value, lexopt, Error, Argument};
|
||||
use ::uutils_args::{Value, lexopt, Error, Argument};
|
||||
|
||||
#free
|
||||
|
||||
@@ -90,14 +87,10 @@ pub fn arguments(input: TokenStream) -> TokenStream {
|
||||
match arg {
|
||||
lexopt::Arg::Short(short) => { #short },
|
||||
lexopt::Arg::Long(long) => { #long },
|
||||
lexopt::Arg::Value(value) => { #positional },
|
||||
lexopt::Arg::Value(value) => { Ok(Some(::uutils_args::Argument::Positional(value))) },
|
||||
}
|
||||
}
|
||||
|
||||
fn check_missing(positional_idx: usize) -> Result<(), uutils_args::Error> {
|
||||
#missing_argument_checks
|
||||
}
|
||||
|
||||
fn help(bin_name: &str) -> ::std::io::Result<()> {
|
||||
#help_string
|
||||
}
|
||||
|
||||
@@ -43,8 +43,8 @@ impl Options<Arg> for Settings {
|
||||
}
|
||||
|
||||
fn main() {
|
||||
assert_eq!(Settings::default().parse(["test", "-10"]).n1, 10usize);
|
||||
assert_eq!(Settings::default().parse(["test", "-10"]).0.n1, 10usize);
|
||||
assert!(Settings::default().try_parse(["test", "--10"]).is_err());
|
||||
assert_eq!(Settings::default().parse(["test", "+10"]).n2, 10isize);
|
||||
assert_eq!(Settings::default().parse(["test", "+-10"]).n2, -10isize);
|
||||
assert_eq!(Settings::default().parse(["test", "+10"]).0.n2, 10isize);
|
||||
assert_eq!(Settings::default().parse(["test", "+-10"]).0.n2, -10isize);
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ impl Options<Arg> for Settings {
|
||||
}
|
||||
|
||||
fn main() -> Result<(), uutils_args::Error> {
|
||||
let settings = Settings {
|
||||
let (settings, _operands) = Settings {
|
||||
name: String::new(),
|
||||
count: 1,
|
||||
}
|
||||
|
||||
+2
-2
@@ -33,6 +33,6 @@ impl Options<Arg> for Settings {
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let color = Settings::default().parse(std::env::args_os()).color;
|
||||
println!("{:?}", color);
|
||||
let (settings, _operands) = Settings::default().parse(std::env::args_os());
|
||||
println!("{:?}", settings.color);
|
||||
}
|
||||
|
||||
+21
-30
@@ -21,6 +21,7 @@ use std::{ffi::OsString, marker::PhantomData};
|
||||
pub enum Argument<T: Arguments> {
|
||||
Help,
|
||||
Version,
|
||||
Positional(OsString),
|
||||
Custom(T),
|
||||
}
|
||||
|
||||
@@ -59,18 +60,7 @@ pub trait Arguments: Sized {
|
||||
/// Parse the next argument from the lexopt parser.
|
||||
///
|
||||
/// This method is called by [`ArgumentIter::next_arg`].
|
||||
fn next_arg(
|
||||
parser: &mut lexopt::Parser,
|
||||
positional_idx: &mut usize,
|
||||
) -> Result<Option<Argument<Self>>, ErrorKind>;
|
||||
|
||||
/// Check for any required arguments that have not been found.
|
||||
///
|
||||
/// If any missing arguments are found, the appropriate error is returned.
|
||||
/// The `positional_idx` parameter specifies how many positional arguments
|
||||
/// have been passed so far. This method is called at the end of
|
||||
/// [`Options::parse`] and [`Options::try_parse`].
|
||||
fn check_missing(positional_idx: usize) -> Result<(), Error>;
|
||||
fn next_arg(parser: &mut lexopt::Parser) -> Result<Option<Argument<Self>>, ErrorKind>;
|
||||
|
||||
/// Print the help string for this command.
|
||||
///
|
||||
@@ -117,7 +107,7 @@ pub trait Arguments: Sized {
|
||||
/// [`Options::try_parse`].
|
||||
pub struct ArgumentIter<T: Arguments> {
|
||||
parser: lexopt::Parser,
|
||||
pub positional_idx: usize,
|
||||
positional_arguments: Vec<OsString>,
|
||||
t: PhantomData<T>,
|
||||
}
|
||||
|
||||
@@ -129,18 +119,16 @@ impl<T: Arguments> ArgumentIter<T> {
|
||||
{
|
||||
Self {
|
||||
parser: lexopt::Parser::from_iter(args),
|
||||
positional_idx: 0,
|
||||
positional_arguments: Vec::new(),
|
||||
t: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn next_arg(&mut self) -> Result<Option<T>, Error> {
|
||||
if let Some(arg) =
|
||||
T::next_arg(&mut self.parser, &mut self.positional_idx).map_err(|kind| Error {
|
||||
exit_code: T::EXIT_CODE,
|
||||
kind,
|
||||
})?
|
||||
{
|
||||
while let Some(arg) = T::next_arg(&mut self.parser).map_err(|kind| Error {
|
||||
exit_code: T::EXIT_CODE,
|
||||
kind,
|
||||
})? {
|
||||
match arg {
|
||||
Argument::Help => {
|
||||
self.help().unwrap();
|
||||
@@ -150,11 +138,17 @@ impl<T: Arguments> ArgumentIter<T> {
|
||||
print!("{}", self.version());
|
||||
std::process::exit(0);
|
||||
}
|
||||
Argument::Custom(arg) => Ok(Some(arg)),
|
||||
Argument::Positional(arg) => {
|
||||
self.positional_arguments.push(arg);
|
||||
}
|
||||
Argument::Custom(arg) => return Ok(Some(arg)),
|
||||
}
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
fn get_positional_arguments(self) -> Vec<OsString> {
|
||||
self.positional_arguments
|
||||
}
|
||||
|
||||
fn help(&self) -> std::io::Result<()> {
|
||||
@@ -176,15 +170,13 @@ impl<T: Arguments> ArgumentIter<T> {
|
||||
///
|
||||
/// By default, the [`Options::parse`] method will
|
||||
/// 1. repeatedly call [`ArgumentIter::next_arg`] and call [`Options::apply`]
|
||||
/// on the result until the arguments are exhausted,
|
||||
/// 2. and finally call [`Arguments::check_missing`] to check whether all
|
||||
/// required arguments were given.
|
||||
/// on the result until the arguments are exhausted.
|
||||
pub trait Options<Arg: Arguments>: Sized {
|
||||
/// Apply a single argument to the options.
|
||||
fn apply(&mut self, arg: Arg);
|
||||
|
||||
/// Parse an iterator of arguments into the options
|
||||
fn parse<I>(self, args: I) -> Self
|
||||
fn parse<I>(self, args: I) -> (Self, Vec<OsString>)
|
||||
where
|
||||
I: IntoIterator,
|
||||
I::Item: Into<OsString>,
|
||||
@@ -193,7 +185,7 @@ pub trait Options<Arg: Arguments>: Sized {
|
||||
}
|
||||
|
||||
#[allow(unused_mut)]
|
||||
fn try_parse<I>(mut self, args: I) -> Result<Self, Error>
|
||||
fn try_parse<I>(mut self, args: I) -> Result<(Self, Vec<OsString>), Error>
|
||||
where
|
||||
I: IntoIterator,
|
||||
I::Item: Into<OsString>,
|
||||
@@ -216,8 +208,7 @@ pub trait Options<Arg: Arguments>: Sized {
|
||||
while let Some(arg) = iter.next_arg()? {
|
||||
self.apply(arg);
|
||||
}
|
||||
Arg::check_missing(iter.positional_idx)?;
|
||||
Ok(self)
|
||||
Ok((self, iter.get_positional_arguments()))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -12,5 +12,4 @@ fn no_args() {
|
||||
fn one_arg_fails() {
|
||||
assert!(Arg::try_check(["arch", "-f"]).is_err());
|
||||
assert!(Arg::try_check(["arch", "--foo"]).is_err());
|
||||
assert!(Arg::try_check(["arch", "foo"]).is_err());
|
||||
}
|
||||
|
||||
+25
-17
@@ -1,4 +1,4 @@
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::ffi::OsString;
|
||||
use uutils_args::{Arguments, Options};
|
||||
|
||||
#[derive(Clone, Arguments)]
|
||||
@@ -26,9 +26,6 @@ enum Arg {
|
||||
|
||||
#[arg("-w", "--warn")]
|
||||
Warn,
|
||||
|
||||
#[arg("FILE", ..)]
|
||||
File(PathBuf),
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, PartialEq, Eq)]
|
||||
@@ -46,7 +43,6 @@ struct Settings {
|
||||
tag: bool,
|
||||
check_output: CheckOutput,
|
||||
strict: bool,
|
||||
files: Vec<PathBuf>,
|
||||
}
|
||||
|
||||
impl Options<Arg> for Settings {
|
||||
@@ -60,58 +56,68 @@ impl Options<Arg> for Settings {
|
||||
Arg::Status => self.check_output = CheckOutput::Status,
|
||||
Arg::Strict => self.strict = true,
|
||||
Arg::Warn => self.check_output = CheckOutput::Warn,
|
||||
Arg::File(f) => self.files.push(f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn binary() {
|
||||
assert!(!Settings::default().parse(["b2sum"]).binary);
|
||||
assert!(!Settings::default().parse(["b2sum", "--text"]).binary);
|
||||
assert!(!Settings::default().parse(["b2sum", "-t"]).binary);
|
||||
assert!(!Settings::default().parse(["b2sum"]).0.binary);
|
||||
assert!(!Settings::default().parse(["b2sum", "--text"]).0.binary);
|
||||
assert!(!Settings::default().parse(["b2sum", "-t"]).0.binary);
|
||||
assert!(
|
||||
!Settings::default()
|
||||
.parse(["b2sum", "--binary", "--text"])
|
||||
.0
|
||||
.binary
|
||||
);
|
||||
assert!(!Settings::default().parse(["b2sum", "-b", "-t"]).binary);
|
||||
assert!(!Settings::default().parse(["b2sum", "-b", "-t"]).0.binary);
|
||||
|
||||
assert!(Settings::default().parse(["b2sum", "--binary"]).binary);
|
||||
assert!(Settings::default().parse(["b2sum", "-b"]).binary);
|
||||
assert!(Settings::default().parse(["b2sum", "--binary"]).0.binary);
|
||||
assert!(Settings::default().parse(["b2sum", "-b"]).0.binary);
|
||||
assert!(
|
||||
Settings::default()
|
||||
.parse(["b2sum", "--text", "--binary"])
|
||||
.0
|
||||
.binary
|
||||
);
|
||||
assert!(Settings::default().parse(["b2sum", "-t", "-b"]).binary);
|
||||
assert!(Settings::default().parse(["b2sum", "-t", "-b"]).0.binary);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_output() {
|
||||
assert_eq!(
|
||||
Settings::default().parse(["b2sum", "--warn"]).check_output,
|
||||
Settings::default()
|
||||
.parse(["b2sum", "--warn"])
|
||||
.0
|
||||
.check_output,
|
||||
CheckOutput::Warn
|
||||
);
|
||||
assert_eq!(
|
||||
Settings::default().parse(["b2sum", "--quiet"]).check_output,
|
||||
Settings::default()
|
||||
.parse(["b2sum", "--quiet"])
|
||||
.0
|
||||
.check_output,
|
||||
CheckOutput::Quiet
|
||||
);
|
||||
assert_eq!(
|
||||
Settings::default()
|
||||
.parse(["b2sum", "--status"])
|
||||
.0
|
||||
.check_output,
|
||||
CheckOutput::Status
|
||||
);
|
||||
assert_eq!(
|
||||
Settings::default()
|
||||
.parse(["b2sum", "--status", "--warn"])
|
||||
.0
|
||||
.check_output,
|
||||
CheckOutput::Warn
|
||||
);
|
||||
assert_eq!(
|
||||
Settings::default()
|
||||
.parse(["b2sum", "--status", "--warn"])
|
||||
.0
|
||||
.check_output,
|
||||
CheckOutput::Warn
|
||||
);
|
||||
@@ -119,6 +125,7 @@ fn check_output() {
|
||||
assert_eq!(
|
||||
Settings::default()
|
||||
.parse(["b2sum", "--warn", "--quiet"])
|
||||
.0
|
||||
.check_output,
|
||||
CheckOutput::Quiet
|
||||
);
|
||||
@@ -126,6 +133,7 @@ fn check_output() {
|
||||
assert_eq!(
|
||||
Settings::default()
|
||||
.parse(["b2sum", "--quiet", "--status"])
|
||||
.0
|
||||
.check_output,
|
||||
CheckOutput::Status
|
||||
);
|
||||
@@ -134,7 +142,7 @@ fn check_output() {
|
||||
#[test]
|
||||
fn files() {
|
||||
assert_eq!(
|
||||
Settings::default().parse(["b2sum", "foo", "bar"]).files,
|
||||
vec![Path::new("foo"), Path::new("bar")]
|
||||
Settings::default().parse(["b2sum", "foo", "bar"]).1,
|
||||
vec![OsString::from("foo"), OsString::from("bar")]
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
use std::path::PathBuf;
|
||||
|
||||
use uutils_args::{Arguments, Options};
|
||||
|
||||
#[derive(Clone, Arguments)]
|
||||
@@ -12,16 +10,12 @@ enum Arg {
|
||||
|
||||
#[arg("-w COLS", "--wrap=COLS")]
|
||||
Wrap(usize),
|
||||
|
||||
#[arg("FILE", ..=1)]
|
||||
File(PathBuf),
|
||||
}
|
||||
|
||||
struct Settings {
|
||||
decode: bool,
|
||||
ignore_garbage: bool,
|
||||
wrap: Option<usize>,
|
||||
file: Option<PathBuf>,
|
||||
}
|
||||
|
||||
impl Default for Settings {
|
||||
@@ -30,7 +24,6 @@ impl Default for Settings {
|
||||
wrap: Some(76),
|
||||
decode: Default::default(),
|
||||
ignore_garbage: Default::default(),
|
||||
file: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -42,21 +35,20 @@ impl Options<Arg> for Settings {
|
||||
Arg::IgnoreGarbage => self.ignore_garbage = true,
|
||||
Arg::Wrap(0) => self.wrap = None,
|
||||
Arg::Wrap(x) => self.wrap = Some(x),
|
||||
Arg::File(f) => self.file = Some(f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn wrap() {
|
||||
assert_eq!(Settings::default().parse(["base32"]).wrap, Some(76));
|
||||
assert_eq!(Settings::default().parse(["base32", "-w0"]).wrap, None);
|
||||
assert_eq!(Settings::default().parse(["base32"]).0.wrap, Some(76));
|
||||
assert_eq!(Settings::default().parse(["base32", "-w0"]).0.wrap, None);
|
||||
assert_eq!(
|
||||
Settings::default().parse(["base32", "-w100"]).wrap,
|
||||
Settings::default().parse(["base32", "-w100"]).0.wrap,
|
||||
Some(100)
|
||||
);
|
||||
assert_eq!(
|
||||
Settings::default().parse(["base32", "--wrap=100"]).wrap,
|
||||
Settings::default().parse(["base32", "--wrap=100"]).0.wrap,
|
||||
Some(100)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
use std::ffi::OsString;
|
||||
|
||||
use uutils_args::{Arguments, Options};
|
||||
|
||||
#[derive(Clone, Arguments)]
|
||||
@@ -6,21 +8,18 @@ enum Arg {
|
||||
Multiple,
|
||||
|
||||
#[arg("-s SUFFIX", "--suffix=SUFFIX")]
|
||||
Suffix(String),
|
||||
Suffix(OsString),
|
||||
|
||||
#[arg("-z", "--zero")]
|
||||
Zero,
|
||||
|
||||
#[arg("NAMES", last, ..)]
|
||||
Names(Vec<String>),
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct Settings {
|
||||
multiple: bool,
|
||||
suffix: String,
|
||||
suffix: OsString,
|
||||
zero: bool,
|
||||
names: Vec<String>,
|
||||
names: Vec<OsString>,
|
||||
}
|
||||
|
||||
impl Options<Arg> for Settings {
|
||||
@@ -32,13 +31,13 @@ impl Options<Arg> for Settings {
|
||||
self.suffix = s
|
||||
}
|
||||
Arg::Zero => self.zero = true,
|
||||
Arg::Names(names) => self.names = names,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn parse(args: &[&str]) -> Settings {
|
||||
let mut settings = Settings::default().parse(args);
|
||||
let (mut settings, operands) = Settings::default().parse(args);
|
||||
settings.names = operands;
|
||||
if !settings.multiple {
|
||||
assert_eq!(settings.names.len(), 2);
|
||||
settings.suffix = settings.names.pop().unwrap();
|
||||
|
||||
+8
-15
@@ -1,5 +1,3 @@
|
||||
use std::path::PathBuf;
|
||||
|
||||
use uutils_args::{Arguments, Options};
|
||||
|
||||
#[derive(Default)]
|
||||
@@ -38,9 +36,6 @@ enum Arg {
|
||||
|
||||
#[arg("-v", "--show-nonprinting")]
|
||||
ShowNonPrinting,
|
||||
|
||||
#[arg("FILES", ..)]
|
||||
File(PathBuf),
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
@@ -50,7 +45,6 @@ struct Settings {
|
||||
show_nonprinting: bool,
|
||||
number: NumberingMode,
|
||||
squeeze_blank: bool,
|
||||
files: Vec<PathBuf>,
|
||||
}
|
||||
|
||||
impl Options<Arg> for Settings {
|
||||
@@ -75,34 +69,33 @@ impl Options<Arg> for Settings {
|
||||
Arg::Number => self.number = NumberingMode::All,
|
||||
Arg::NumberNonblank => self.number = NumberingMode::NonEmpty,
|
||||
Arg::SqueezeBlank => self.squeeze_blank = true,
|
||||
Arg::File(f) => self.files.push(f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn show() {
|
||||
let s = Settings::default().parse(["cat", "-v"]);
|
||||
let (s, _) = Settings::default().parse(["cat", "-v"]);
|
||||
assert!(!s.show_ends && !s.show_tabs && s.show_nonprinting);
|
||||
|
||||
let s = Settings::default().parse(["cat", "-E"]);
|
||||
let (s, _) = Settings::default().parse(["cat", "-E"]);
|
||||
assert!(s.show_ends && !s.show_tabs && !s.show_nonprinting);
|
||||
|
||||
let s = Settings::default().parse(["cat", "-T"]);
|
||||
let (s, _) = Settings::default().parse(["cat", "-T"]);
|
||||
assert!(!s.show_ends && s.show_tabs && !s.show_nonprinting);
|
||||
|
||||
let s = Settings::default().parse(["cat", "-e"]);
|
||||
let (s, _) = Settings::default().parse(["cat", "-e"]);
|
||||
assert!(s.show_ends && !s.show_tabs && s.show_nonprinting);
|
||||
|
||||
let s = Settings::default().parse(["cat", "-t"]);
|
||||
let (s, _) = Settings::default().parse(["cat", "-t"]);
|
||||
assert!(!s.show_ends && s.show_tabs && s.show_nonprinting);
|
||||
|
||||
let s = Settings::default().parse(["cat", "-A"]);
|
||||
let (s, _) = Settings::default().parse(["cat", "-A"]);
|
||||
assert!(s.show_ends && s.show_tabs && s.show_nonprinting);
|
||||
|
||||
let s = Settings::default().parse(["cat", "-te"]);
|
||||
let (s, _) = Settings::default().parse(["cat", "-te"]);
|
||||
assert!(s.show_ends && s.show_tabs && s.show_nonprinting);
|
||||
|
||||
let s = Settings::default().parse(["cat", "-vET"]);
|
||||
let (s, _) = Settings::default().parse(["cat", "-vET"]);
|
||||
assert!(s.show_ends && s.show_tabs && s.show_nonprinting);
|
||||
}
|
||||
|
||||
+7
-14
@@ -116,16 +116,13 @@ impl Options<Arg> for Settings {
|
||||
|
||||
#[test]
|
||||
fn empty() {
|
||||
assert_eq!(
|
||||
Settings::default().try_parse(["dd"]).unwrap(),
|
||||
Settings::default()
|
||||
)
|
||||
assert_eq!(Settings::default().parse(["dd"]).0, Settings::default())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn infile() {
|
||||
assert_eq!(
|
||||
Settings::default().try_parse(["dd", "if=hello"]).unwrap(),
|
||||
Settings::default().parse(["dd", "if=hello"]).0,
|
||||
Settings {
|
||||
infile: Some(PathBuf::from("hello")),
|
||||
..Settings::default()
|
||||
@@ -136,7 +133,7 @@ fn infile() {
|
||||
#[test]
|
||||
fn outfile() {
|
||||
assert_eq!(
|
||||
Settings::default().try_parse(["dd", "of=hello"]).unwrap(),
|
||||
Settings::default().parse(["dd", "of=hello"]).0,
|
||||
Settings {
|
||||
outfile: Some(PathBuf::from("hello")),
|
||||
..Settings::default()
|
||||
@@ -147,7 +144,7 @@ fn outfile() {
|
||||
#[test]
|
||||
fn bs() {
|
||||
assert_eq!(
|
||||
Settings::default().try_parse(["dd", "ibs=1"]).unwrap(),
|
||||
Settings::default().parse(["dd", "ibs=1"]).0,
|
||||
Settings {
|
||||
ibs: 1,
|
||||
obs: 512,
|
||||
@@ -155,7 +152,7 @@ fn bs() {
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
Settings::default().try_parse(["dd", "obs=1"]).unwrap(),
|
||||
Settings::default().parse(["dd", "obs=1"]).0,
|
||||
Settings {
|
||||
ibs: 512,
|
||||
obs: 1,
|
||||
@@ -163,9 +160,7 @@ fn bs() {
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
Settings::default()
|
||||
.try_parse(["dd", "ibs=10", "obs=1"])
|
||||
.unwrap(),
|
||||
Settings::default().parse(["dd", "ibs=10", "obs=1"]).0,
|
||||
Settings {
|
||||
ibs: 10,
|
||||
obs: 1,
|
||||
@@ -173,9 +168,7 @@ fn bs() {
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
Settings::default()
|
||||
.try_parse(["dd", "ibs=10", "bs=1"])
|
||||
.unwrap(),
|
||||
Settings::default().parse(["dd", "ibs=10", "bs=1"]).0,
|
||||
Settings {
|
||||
ibs: 1,
|
||||
obs: 1,
|
||||
|
||||
+7
-11
@@ -15,16 +15,12 @@ enum Arg {
|
||||
/// Disable interpretation of backslash escapes
|
||||
#[arg("-E")]
|
||||
DisableEscape,
|
||||
|
||||
#[arg("STRING", last)]
|
||||
String(Vec<OsString>),
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct Settings {
|
||||
trailing_newline: bool,
|
||||
escape: bool,
|
||||
strings: Vec<OsString>,
|
||||
}
|
||||
|
||||
impl Options<Arg> for Settings {
|
||||
@@ -33,7 +29,6 @@ impl Options<Arg> for Settings {
|
||||
Arg::NoNewline => self.trailing_newline = false,
|
||||
Arg::EnableEscape => self.escape = true,
|
||||
Arg::DisableEscape => self.escape = false,
|
||||
Arg::String(s) => self.strings = s,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -42,17 +37,18 @@ impl Options<Arg> for Settings {
|
||||
// support explicitly.
|
||||
|
||||
#[test]
|
||||
#[ignore = "needs to be fixed after positional argument refactor"]
|
||||
fn double_hyphen() {
|
||||
let s = Settings::default().parse(["echo", "--"]);
|
||||
assert_eq!(s.strings, vec![OsString::from("--")]);
|
||||
let (_, operands) = Settings::default().parse(["echo", "--"]);
|
||||
assert_eq!(operands, vec![OsString::from("--")]);
|
||||
|
||||
let s = Settings::default().parse(["echo", "--", "-n"]);
|
||||
assert_eq!(s.strings, vec![OsString::from("--"), OsString::from("-n")]);
|
||||
let (_, operands) = Settings::default().parse(["echo", "--", "-n"]);
|
||||
assert_eq!(operands, vec![OsString::from("--"), OsString::from("-n")]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn nonexistent_options_are_values() {
|
||||
let s = Settings::default().parse(["echo", "-f"]);
|
||||
assert_eq!(s.strings, vec![OsString::from("-f")]);
|
||||
let (_, operands) = Settings::default().parse(["echo", "-f"]);
|
||||
assert_eq!(operands, vec![OsString::from("-f")]);
|
||||
}
|
||||
|
||||
+25
-28
@@ -8,7 +8,7 @@ use uutils_args::{Arguments, Options, Value};
|
||||
// from this function are not relevant, so we can just return an `Option`.
|
||||
// Once this gets into uutils, I highly recommend that we make this format
|
||||
// optional at compile time. As the GNU docs explain, it's very error-prone.
|
||||
fn parse_deprecated<I>(iter: I) -> Option<Settings>
|
||||
fn parse_deprecated<I>(iter: I) -> Option<(Settings, Vec<OsString>)>
|
||||
where
|
||||
I: IntoIterator + Clone,
|
||||
I::Item: Into<OsString>,
|
||||
@@ -73,13 +73,15 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
Some(Settings {
|
||||
number: SigNum::Negative(num),
|
||||
mode,
|
||||
inputs: vec![input.into().into()],
|
||||
verbose,
|
||||
zero,
|
||||
})
|
||||
Some((
|
||||
Settings {
|
||||
number: SigNum::Negative(num),
|
||||
mode,
|
||||
verbose,
|
||||
zero,
|
||||
},
|
||||
vec![input.into()],
|
||||
))
|
||||
}
|
||||
|
||||
#[derive(Arguments)]
|
||||
@@ -98,9 +100,6 @@ enum Arg {
|
||||
|
||||
#[arg("-z", "--zero-terminated")]
|
||||
Zero,
|
||||
|
||||
#[arg("FILES", ..)]
|
||||
File(PathBuf),
|
||||
}
|
||||
|
||||
// We need both negative and positive 0
|
||||
@@ -185,7 +184,6 @@ struct Settings {
|
||||
number: SigNum,
|
||||
// TODO: Should be a dedicated PID type
|
||||
verbose: bool,
|
||||
inputs: Vec<PathBuf>,
|
||||
zero: bool,
|
||||
}
|
||||
|
||||
@@ -203,12 +201,11 @@ impl Options<Arg> for Settings {
|
||||
Arg::Quiet => self.verbose = false,
|
||||
Arg::Verbose => self.verbose = true,
|
||||
Arg::Zero => self.zero = true,
|
||||
Arg::File(input) => self.inputs.push(input),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_head<I>(iter: I) -> Result<Settings, uutils_args::Error>
|
||||
fn parse_head<I>(iter: I) -> Result<(Settings, Vec<OsString>), uutils_args::Error>
|
||||
where
|
||||
I: IntoIterator + Clone,
|
||||
I::Item: Into<OsString>,
|
||||
@@ -221,51 +218,51 @@ where
|
||||
|
||||
#[test]
|
||||
fn shorthand() {
|
||||
let s = parse_head(["head", "-20", "some_file"]).unwrap();
|
||||
let (s, _operands) = parse_head(["head", "-20", "some_file"]).unwrap();
|
||||
assert_eq!(s.number, SigNum::Negative(20));
|
||||
assert_eq!(s.mode, Mode::Lines);
|
||||
|
||||
let s = parse_head(["head", "-100cq", "some_file"]).unwrap();
|
||||
let (s, _operands) = parse_head(["head", "-100cq", "some_file"]).unwrap();
|
||||
assert_eq!(s.number, SigNum::Negative(100));
|
||||
assert_eq!(s.mode, Mode::Bytes);
|
||||
|
||||
// Corner case where the shorthand does not apply
|
||||
let s = parse_head(["head", "-c", "42"]).unwrap();
|
||||
let (s, operands) = parse_head(["head", "-c", "42"]).unwrap();
|
||||
assert_eq!(s.number, SigNum::Negative(42));
|
||||
assert_eq!(s.mode, Mode::Bytes);
|
||||
assert_eq!(s.inputs, Vec::<PathBuf>::new());
|
||||
assert_eq!(operands, Vec::<PathBuf>::new());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn standard_input() {
|
||||
let s = parse_head(["head", "-"]).unwrap();
|
||||
assert_eq!(s.inputs, vec![PathBuf::from("-")])
|
||||
let (_s, operands) = parse_head(["head", "-"]).unwrap();
|
||||
assert_eq!(operands, vec![PathBuf::from("-")])
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn normal_format() {
|
||||
let s = parse_head(["head", "-c", "20", "some_file"]).unwrap();
|
||||
let (s, _operands) = parse_head(["head", "-c", "20", "some_file"]).unwrap();
|
||||
assert_eq!(s.number, SigNum::Negative(20));
|
||||
assert_eq!(s.mode, Mode::Bytes);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn signum() {
|
||||
let s = parse_head(["head", "-n", "20"]).unwrap();
|
||||
let (s, _operands) = parse_head(["head", "-n", "20"]).unwrap();
|
||||
assert_eq!(s.number, SigNum::Negative(20));
|
||||
let s = parse_head(["head", "-n", "-20"]).unwrap();
|
||||
let (s, _operands) = parse_head(["head", "-n", "-20"]).unwrap();
|
||||
assert_eq!(s.number, SigNum::Negative(20));
|
||||
let s = parse_head(["head", "-n", "+20"]).unwrap();
|
||||
let (s, _operands) = parse_head(["head", "-n", "+20"]).unwrap();
|
||||
assert_eq!(s.number, SigNum::Positive(20));
|
||||
|
||||
let s = parse_head(["head", "-n", "20b"]).unwrap();
|
||||
let (s, _operands) = parse_head(["head", "-n", "20b"]).unwrap();
|
||||
assert_eq!(s.number, SigNum::Negative(20 * 512));
|
||||
let s = parse_head(["head", "-n", "+20b"]).unwrap();
|
||||
let (s, _operands) = parse_head(["head", "-n", "+20b"]).unwrap();
|
||||
assert_eq!(s.number, SigNum::Positive(20 * 512));
|
||||
|
||||
let s = parse_head(["head", "-n", "b"]).unwrap();
|
||||
let (s, _operands) = parse_head(["head", "-n", "b"]).unwrap();
|
||||
assert_eq!(s.number, SigNum::Negative(512));
|
||||
let s = parse_head(["head", "-n", "+b"]).unwrap();
|
||||
let (s, _operands) = parse_head(["head", "-n", "+b"]).unwrap();
|
||||
assert_eq!(s.number, SigNum::Positive(512));
|
||||
|
||||
assert!(parse_head(["head", "-n", "20invalid_suffix"]).is_err());
|
||||
|
||||
+23
-31
@@ -1,4 +1,3 @@
|
||||
use std::path::PathBuf;
|
||||
use uutils_args::{Arguments, Options, Value};
|
||||
|
||||
#[derive(Default, Debug, PartialEq, Eq, Value)]
|
||||
@@ -276,9 +275,6 @@ enum Arg {
|
||||
|
||||
#[arg("--group-directories-first")]
|
||||
GroupDirectoriesFirst,
|
||||
|
||||
#[arg("FILES", ..)]
|
||||
File(PathBuf),
|
||||
}
|
||||
|
||||
fn default_terminal_size() -> u16 {
|
||||
@@ -305,7 +301,6 @@ fn default_terminal_size() -> u16 {
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
struct Settings {
|
||||
format: Format,
|
||||
files: Vec<PathBuf>,
|
||||
sort: Sort,
|
||||
recursive: bool,
|
||||
reverse: bool,
|
||||
@@ -340,7 +335,6 @@ impl Default for Settings {
|
||||
eol: '\n',
|
||||
width: default_terminal_size(),
|
||||
format: Default::default(),
|
||||
files: Default::default(),
|
||||
sort: Default::default(),
|
||||
recursive: Default::default(),
|
||||
reverse: Default::default(),
|
||||
@@ -422,7 +416,6 @@ impl Options<Arg> for Settings {
|
||||
// TODO: Zero changes more than just this
|
||||
}
|
||||
Arg::GroupDirectoriesFirst => self.group_directories_first = true,
|
||||
Arg::File(f) => self.files.push(f),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -430,10 +423,9 @@ impl Options<Arg> for Settings {
|
||||
#[test]
|
||||
fn default() {
|
||||
assert_eq!(
|
||||
Settings::default().parse(["ls"]),
|
||||
Settings::default().parse(["ls"]).0,
|
||||
Settings {
|
||||
format: Format::Columns,
|
||||
files: Vec::new(),
|
||||
sort: Sort::Name,
|
||||
recursive: false,
|
||||
reverse: false,
|
||||
@@ -462,87 +454,87 @@ fn default() {
|
||||
|
||||
#[test]
|
||||
fn color() {
|
||||
let s = Settings::default().parse(["ls", "--color"]);
|
||||
let (s, _operands) = Settings::default().parse(["ls", "--color"]);
|
||||
assert!(s.color);
|
||||
|
||||
let s = Settings::default().parse(["ls", "--color=always"]);
|
||||
let (s, _operands) = Settings::default().parse(["ls", "--color=always"]);
|
||||
assert!(s.color);
|
||||
|
||||
let s = Settings::default().parse(["ls", "--color=never"]);
|
||||
let (s, _operands) = Settings::default().parse(["ls", "--color=never"]);
|
||||
assert!(!s.color);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn format() {
|
||||
let s = Settings::default().parse(["ls", "-l"]);
|
||||
let (s, _operands) = Settings::default().parse(["ls", "-l"]);
|
||||
assert_eq!(s.format, Format::Long);
|
||||
|
||||
let s = Settings::default().parse(["ls", "-m"]);
|
||||
let (s, _operands) = Settings::default().parse(["ls", "-m"]);
|
||||
assert_eq!(s.format, Format::Commas);
|
||||
|
||||
let s = Settings::default().parse(["ls", "--format=across"]);
|
||||
let (s, _operands) = Settings::default().parse(["ls", "--format=across"]);
|
||||
assert_eq!(s.format, Format::Across);
|
||||
|
||||
let s = Settings::default().parse(["ls", "--format=acr"]);
|
||||
let (s, _operands) = Settings::default().parse(["ls", "--format=acr"]);
|
||||
assert_eq!(s.format, Format::Across);
|
||||
|
||||
let s = Settings::default().parse(["ls", "-o"]);
|
||||
let (s, _operands) = Settings::default().parse(["ls", "-o"]);
|
||||
assert_eq!(s.format, Format::Long);
|
||||
assert!(s.long_no_group && !s.long_no_owner && !s.long_numeric_uid_gid);
|
||||
|
||||
let s = Settings::default().parse(["ls", "-g"]);
|
||||
let (s, _operands) = Settings::default().parse(["ls", "-g"]);
|
||||
assert_eq!(s.format, Format::Long);
|
||||
assert!(!s.long_no_group && s.long_no_owner && !s.long_numeric_uid_gid);
|
||||
|
||||
let s = Settings::default().parse(["ls", "-n"]);
|
||||
let (s, _operands) = Settings::default().parse(["ls", "-n"]);
|
||||
assert_eq!(s.format, Format::Long);
|
||||
assert!(!s.long_no_group && !s.long_no_owner && s.long_numeric_uid_gid);
|
||||
|
||||
let s = Settings::default().parse(["ls", "-og"]);
|
||||
let (s, _operands) = Settings::default().parse(["ls", "-og"]);
|
||||
assert_eq!(s.format, Format::Long);
|
||||
assert!(s.long_no_group && s.long_no_owner && !s.long_numeric_uid_gid);
|
||||
|
||||
let s = Settings::default().parse(["ls", "-on"]);
|
||||
let (s, _operands) = Settings::default().parse(["ls", "-on"]);
|
||||
assert_eq!(s.format, Format::Long);
|
||||
assert!(s.long_no_group && !s.long_no_owner && s.long_numeric_uid_gid);
|
||||
|
||||
let s = Settings::default().parse(["ls", "-onCl"]);
|
||||
let (s, _operands) = Settings::default().parse(["ls", "-onCl"]);
|
||||
assert_eq!(s.format, Format::Long);
|
||||
assert!(s.long_no_group && !s.long_no_owner && s.long_numeric_uid_gid);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn time() {
|
||||
let s = Settings::default().parse(["ls", "--time=access"]);
|
||||
let (s, _operands) = Settings::default().parse(["ls", "--time=access"]);
|
||||
assert_eq!(s.time, Time::Access);
|
||||
|
||||
let s = Settings::default().parse(["ls", "--time=a"]);
|
||||
let (s, _operands) = Settings::default().parse(["ls", "--time=a"]);
|
||||
assert_eq!(s.time, Time::Access);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn classify() {
|
||||
let s = Settings::default().parse(["ls", "--indicator-style=classify"]);
|
||||
let (s, _operands) = Settings::default().parse(["ls", "--indicator-style=classify"]);
|
||||
assert_eq!(s.indicator_style, IndicatorStyle::Classify);
|
||||
|
||||
let s = Settings::default().parse(["ls", "--classify"]);
|
||||
let (s, _operands) = Settings::default().parse(["ls", "--classify"]);
|
||||
assert_eq!(s.indicator_style, IndicatorStyle::Classify);
|
||||
|
||||
let s = Settings::default().parse(["ls", "--classify=always"]);
|
||||
let (s, _operands) = Settings::default().parse(["ls", "--classify=always"]);
|
||||
assert_eq!(s.indicator_style, IndicatorStyle::Classify);
|
||||
|
||||
let s = Settings::default().parse(["ls", "--classify=none"]);
|
||||
let (s, _operands) = Settings::default().parse(["ls", "--classify=none"]);
|
||||
assert_eq!(s.indicator_style, IndicatorStyle::None);
|
||||
|
||||
let s = Settings::default().parse(["ls", "-F"]);
|
||||
let (s, _operands) = Settings::default().parse(["ls", "-F"]);
|
||||
assert_eq!(s.indicator_style, IndicatorStyle::Classify);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sort() {
|
||||
let s = Settings::default().parse(["ls", "--sort=time"]);
|
||||
let (s, _operands) = Settings::default().parse(["ls", "--sort=time"]);
|
||||
assert_eq!(s.sort, Sort::Time);
|
||||
|
||||
let s = Settings::default().parse(["ls", "-X"]);
|
||||
let (s, _operands) = Settings::default().parse(["ls", "-X"]);
|
||||
assert_eq!(s.sort, Sort::Extension);
|
||||
}
|
||||
|
||||
@@ -21,9 +21,6 @@ enum Arg {
|
||||
|
||||
#[arg("-p DIR", "--tmpdir[=DIR]", value = ".".into())]
|
||||
TmpDir(PathBuf),
|
||||
|
||||
#[arg("TEMPLATE", 0..=1)]
|
||||
Template(String),
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
@@ -34,7 +31,6 @@ struct Settings {
|
||||
tmp_dir: Option<PathBuf>,
|
||||
suffix: Option<String>,
|
||||
treat_as_template: bool,
|
||||
template: String,
|
||||
}
|
||||
|
||||
impl Options<Arg> for Settings {
|
||||
@@ -46,41 +42,40 @@ impl Options<Arg> for Settings {
|
||||
Arg::Suffix(s) => self.suffix = Some(s),
|
||||
Arg::TreatAsTemplate => self.treat_as_template = true,
|
||||
Arg::TmpDir(dir) => self.tmp_dir = Some(dir),
|
||||
Arg::Template(s) => self.template = s,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn suffix() {
|
||||
let s = Settings::default().parse(["mktemp", "--suffix=hello"]);
|
||||
let (s, _operands) = Settings::default().parse(["mktemp", "--suffix=hello"]);
|
||||
assert_eq!(s.suffix.unwrap(), "hello");
|
||||
|
||||
let s = Settings::default().parse(["mktemp", "--suffix="]);
|
||||
let (s, _operands) = Settings::default().parse(["mktemp", "--suffix="]);
|
||||
assert_eq!(s.suffix.unwrap(), "");
|
||||
|
||||
let s = Settings::default().parse(["mktemp", "--suffix="]);
|
||||
let (s, _operands) = Settings::default().parse(["mktemp", "--suffix="]);
|
||||
assert_eq!(s.suffix.unwrap(), "");
|
||||
|
||||
let s = Settings::default().parse(["mktemp"]);
|
||||
let (s, _operands) = Settings::default().parse(["mktemp"]);
|
||||
assert_eq!(s.suffix, None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tmpdir() {
|
||||
let s = Settings::default().parse(["mktemp", "--tmpdir"]);
|
||||
let (s, _operands) = Settings::default().parse(["mktemp", "--tmpdir"]);
|
||||
assert_eq!(s.tmp_dir.unwrap(), Path::new("."));
|
||||
|
||||
let s = Settings::default().parse(["mktemp", "--tmpdir="]);
|
||||
let (s, _operands) = Settings::default().parse(["mktemp", "--tmpdir="]);
|
||||
assert_eq!(s.tmp_dir.unwrap(), Path::new(""));
|
||||
|
||||
let s = Settings::default().parse(["mktemp", "-p", "foo"]);
|
||||
let (s, _operands) = Settings::default().parse(["mktemp", "-p", "foo"]);
|
||||
assert_eq!(s.tmp_dir.unwrap(), Path::new("foo"));
|
||||
|
||||
let s = Settings::default().parse(["mktemp", "-pfoo"]);
|
||||
let (s, _operands) = Settings::default().parse(["mktemp", "-pfoo"]);
|
||||
assert_eq!(s.tmp_dir.unwrap(), Path::new("foo"));
|
||||
|
||||
let s = Settings::default().parse(["mktemp", "-p", ""]);
|
||||
let (s, _operands) = Settings::default().parse(["mktemp", "-p", ""]);
|
||||
assert_eq!(s.tmp_dir.unwrap(), Path::new(""));
|
||||
|
||||
assert!(Settings::default().try_parse(["mktemp", "-p"]).is_err());
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user