From 462898ca91fa0e2cdf474d2e790fa14faa421242 Mon Sep 17 00:00:00 2001 From: Terts Diepraam Date: Thu, 14 Dec 2023 12:28:24 +0100 Subject: [PATCH] take positional arguments out of arguments enum --- .github/workflows/ci.yml | 2 +- README.md | 38 +++------ derive/src/argument.rs | 104 ----------------------- derive/src/attributes.rs | 84 +------------------ derive/src/help.rs | 1 - derive/src/lib.rs | 15 +--- examples/deprecated.rs | 6 +- examples/hello_world.rs | 2 +- examples/value.rs | 4 +- src/lib.rs | 51 +++++------ tests/coreutils/arch.rs | 1 - tests/coreutils/b2sum.rs | 42 ++++++---- tests/coreutils/base32.rs | 16 +--- tests/coreutils/basename.rs | 15 ++-- tests/coreutils/cat.rs | 23 ++--- tests/coreutils/dd.rs | 21 ++--- tests/coreutils/echo.rs | 18 ++-- tests/coreutils/head.rs | 53 ++++++------ tests/coreutils/ls.rs | 54 +++++------- tests/coreutils/mktemp.rs | 23 ++--- tests/coreutils/tail.rs | 72 ++++++++-------- tests/coreutils/uniq.rs | 22 ++--- tests/flags.rs | 72 ++++++++-------- tests/options.rs | 73 ++++++++-------- tests/positionals.rs | 163 ------------------------------------ 25 files changed, 285 insertions(+), 690 deletions(-) delete mode 100644 tests/positionals.rs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c03e0c1..d3e5f46 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -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 diff --git a/README.md b/README.md index 48ecec8..839a510 100644 --- a/README.md +++ b/README.md @@ -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 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::>().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('!'); diff --git a/derive/src/argument.rs b/derive/src/argument.rs index c6b2f2d..ddb4a5a 100644 --- a/derive/src/argument.rs +++ b/derive/src/argument.rs @@ -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, - last: bool, - }, Free { filters: Vec, }, @@ -93,13 +87,6 @@ pub fn parse_argument(v: Variant) -> Vec { 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) { 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::>() - ) - }) - } 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::>()?; - Self::#ident(collection) - }) -} diff --git a/derive/src/attributes.rs b/derive/src/attributes.rs index 1a77353..fa068b4 100644 --- a/derive/src/attributes.rs +++ b/derive/src/attributes.rs @@ -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::() { FreeAttr::from_args(v, s).map(Self::Free) @@ -170,82 +166,6 @@ impl FreeAttr { } } -pub struct PositionalAttr { - pub num_args: RangeInclusive, - 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 { - let mut positional_attr = Self::default(); - parse_args(s, |s| { - if (s.peek(LitInt) && s.peek2(Token![..])) || s.peek(Token![..]) { - let range = s.parse::()?; - // We're dealing with a range - let from = match range.start.as_deref() { - Some(Expr::Lit(ExprLit { - lit: Lit::Int(i), .. - })) => i.base10_parse::().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::().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::() { - 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::().unwrap(); - positional_attr.num_args = n..=n; - return Ok(()); - } - - let ident = s.parse::()?; - 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, diff --git a/derive/src/help.rs b/derive/src/help.rs index ad5d5ae..a606ecf 100644 --- a/derive/src/help.rs +++ b/derive/src/help.rs @@ -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 { .. } => {} } diff --git a/derive/src/lib.rs b/derive/src/lib.rs index bfeb314..4842374 100644 --- a/derive/src/lib.rs +++ b/derive/src/lib.rs @@ -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>, ::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 } diff --git a/examples/deprecated.rs b/examples/deprecated.rs index 3e68ba2..25484b6 100644 --- a/examples/deprecated.rs +++ b/examples/deprecated.rs @@ -43,8 +43,8 @@ impl Options 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); } diff --git a/examples/hello_world.rs b/examples/hello_world.rs index 1adf4b1..a8eb551 100644 --- a/examples/hello_world.rs +++ b/examples/hello_world.rs @@ -32,7 +32,7 @@ impl Options for Settings { } fn main() -> Result<(), uutils_args::Error> { - let settings = Settings { + let (settings, _operands) = Settings { name: String::new(), count: 1, } diff --git a/examples/value.rs b/examples/value.rs index a0139f3..997d136 100644 --- a/examples/value.rs +++ b/examples/value.rs @@ -33,6 +33,6 @@ impl Options 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); } diff --git a/src/lib.rs b/src/lib.rs index 342bf9b..5f4ecc5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -21,6 +21,7 @@ use std::{ffi::OsString, marker::PhantomData}; pub enum Argument { 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>, 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>, ErrorKind>; /// Print the help string for this command. /// @@ -117,7 +107,7 @@ pub trait Arguments: Sized { /// [`Options::try_parse`]. pub struct ArgumentIter { parser: lexopt::Parser, - pub positional_idx: usize, + positional_arguments: Vec, t: PhantomData, } @@ -129,18 +119,16 @@ impl ArgumentIter { { Self { parser: lexopt::Parser::from_iter(args), - positional_idx: 0, + positional_arguments: Vec::new(), t: PhantomData, } } pub fn next_arg(&mut self) -> Result, 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 ArgumentIter { 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 { + self.positional_arguments } fn help(&self) -> std::io::Result<()> { @@ -176,15 +170,13 @@ impl ArgumentIter { /// /// 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: Sized { /// Apply a single argument to the options. fn apply(&mut self, arg: Arg); /// Parse an iterator of arguments into the options - fn parse(self, args: I) -> Self + fn parse(self, args: I) -> (Self, Vec) where I: IntoIterator, I::Item: Into, @@ -193,7 +185,7 @@ pub trait Options: Sized { } #[allow(unused_mut)] - fn try_parse(mut self, args: I) -> Result + fn try_parse(mut self, args: I) -> Result<(Self, Vec), Error> where I: IntoIterator, I::Item: Into, @@ -216,8 +208,7 @@ pub trait Options: 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())) } } diff --git a/tests/coreutils/arch.rs b/tests/coreutils/arch.rs index d3198d3..e69b639 100644 --- a/tests/coreutils/arch.rs +++ b/tests/coreutils/arch.rs @@ -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()); } diff --git a/tests/coreutils/b2sum.rs b/tests/coreutils/b2sum.rs index 0fcfc81..adae103 100644 --- a/tests/coreutils/b2sum.rs +++ b/tests/coreutils/b2sum.rs @@ -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, } impl Options for Settings { @@ -60,58 +56,68 @@ impl Options 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")] ); } diff --git a/tests/coreutils/base32.rs b/tests/coreutils/base32.rs index 3ec67ad..be54c57 100644 --- a/tests/coreutils/base32.rs +++ b/tests/coreutils/base32.rs @@ -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, - file: Option, } 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 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) ); } diff --git a/tests/coreutils/basename.rs b/tests/coreutils/basename.rs index 66d598a..accb8ce 100644 --- a/tests/coreutils/basename.rs +++ b/tests/coreutils/basename.rs @@ -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), } #[derive(Default)] struct Settings { multiple: bool, - suffix: String, + suffix: OsString, zero: bool, - names: Vec, + names: Vec, } impl Options for Settings { @@ -32,13 +31,13 @@ impl Options 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(); diff --git a/tests/coreutils/cat.rs b/tests/coreutils/cat.rs index b809b91..190d6c1 100644 --- a/tests/coreutils/cat.rs +++ b/tests/coreutils/cat.rs @@ -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, } impl Options for Settings { @@ -75,34 +69,33 @@ impl Options 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); } diff --git a/tests/coreutils/dd.rs b/tests/coreutils/dd.rs index 6d9c28a..20ae14c 100644 --- a/tests/coreutils/dd.rs +++ b/tests/coreutils/dd.rs @@ -116,16 +116,13 @@ impl Options 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, diff --git a/tests/coreutils/echo.rs b/tests/coreutils/echo.rs index 69f482e..4016c46 100644 --- a/tests/coreutils/echo.rs +++ b/tests/coreutils/echo.rs @@ -15,16 +15,12 @@ enum Arg { /// Disable interpretation of backslash escapes #[arg("-E")] DisableEscape, - - #[arg("STRING", last)] - String(Vec), } #[derive(Default)] struct Settings { trailing_newline: bool, escape: bool, - strings: Vec, } impl Options for Settings { @@ -33,7 +29,6 @@ impl Options 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 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")]); } diff --git a/tests/coreutils/head.rs b/tests/coreutils/head.rs index c1257c7..ab08a9e 100644 --- a/tests/coreutils/head.rs +++ b/tests/coreutils/head.rs @@ -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(iter: I) -> Option +fn parse_deprecated(iter: I) -> Option<(Settings, Vec)> where I: IntoIterator + Clone, I::Item: Into, @@ -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, zero: bool, } @@ -203,12 +201,11 @@ impl Options 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(iter: I) -> Result +fn parse_head(iter: I) -> Result<(Settings, Vec), uutils_args::Error> where I: IntoIterator + Clone, I::Item: Into, @@ -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::::new()); + assert_eq!(operands, Vec::::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()); diff --git a/tests/coreutils/ls.rs b/tests/coreutils/ls.rs index 1ebcfa4..1a23c92 100644 --- a/tests/coreutils/ls.rs +++ b/tests/coreutils/ls.rs @@ -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, 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 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 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); } diff --git a/tests/coreutils/mktemp.rs b/tests/coreutils/mktemp.rs index 344cb06..c90d019 100644 --- a/tests/coreutils/mktemp.rs +++ b/tests/coreutils/mktemp.rs @@ -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, suffix: Option, treat_as_template: bool, - template: String, } impl Options for Settings { @@ -46,41 +42,40 @@ impl Options 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()); diff --git a/tests/coreutils/tail.rs b/tests/coreutils/tail.rs index 7e6b63e..f8f75d2 100644 --- a/tests/coreutils/tail.rs +++ b/tests/coreutils/tail.rs @@ -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(iter: I) -> Option +fn parse_deprecated(iter: I) -> Option<(Settings, Vec)> where I: IntoIterator + Clone, I::Item: Into, @@ -95,13 +95,15 @@ where return None; } - Some(Settings { - number: sig(num), - mode, - follow, - inputs: vec![input.into().into()], - ..Settings::default() - }) + Some(( + Settings { + number: sig(num), + mode, + follow, + ..Settings::default() + }, + vec![input.into()], + )) } #[derive(Arguments)] @@ -141,9 +143,6 @@ enum Arg { #[arg("---presume-input-pipe", hidden)] PresumeInputPipe, - - #[arg("FILES", ..)] - File(PathBuf), } // We need both negative and positive 0 @@ -267,13 +266,12 @@ impl Options for Settings { Arg::SleepInterval(n) => self.sleep_sec = n, Arg::Verbose => self.verbose = true, Arg::Zero => self.zero = true, - Arg::File(input) => self.inputs.push(input), Arg::PresumeInputPipe => self.presume_input_pipe = true, } } } -fn parse_tail(iter: I) -> Result +fn parse_tail(iter: I) -> Result<(Settings, Vec), uutils_args::Error> where I: IntoIterator + Clone, I::Item: Into, @@ -286,23 +284,23 @@ where #[test] fn shorthand() { - let s = parse_tail(["tail", "-20", "some_file"]).unwrap(); + let (s, _operands) = parse_tail(["tail", "-20", "some_file"]).unwrap(); assert_eq!(s.number, SigNum::Negative(20)); assert_eq!(s.mode, Mode::Lines); assert_eq!(s.follow, None); - let s = parse_tail(["tail", "+20", "some_file"]).unwrap(); + let (s, _operands) = parse_tail(["tail", "+20", "some_file"]).unwrap(); assert_eq!(s.number, SigNum::Positive(20)); assert_eq!(s.mode, Mode::Lines); assert_eq!(s.follow, None); - let s = parse_tail(["tail", "-100cf", "some_file"]).unwrap(); + let (s, _operands) = parse_tail(["tail", "-100cf", "some_file"]).unwrap(); assert_eq!(s.number, SigNum::Negative(100)); assert_eq!(s.mode, Mode::Bytes); assert_eq!(s.follow, Some(FollowMode::Descriptor)); // Corner case where the shorthand does not apply - let s = parse_tail(["tail", "-c", "42"]).unwrap(); + let (s, _operands) = parse_tail(["tail", "-c", "42"]).unwrap(); assert_eq!(s.number, SigNum::Negative(42)); assert_eq!(s.mode, Mode::Bytes); assert_eq!(s.inputs, Vec::::new()); @@ -310,34 +308,34 @@ fn shorthand() { #[test] fn standard_input() { - let s = parse_tail(["tail", "-"]).unwrap(); - assert_eq!(s.inputs, vec![PathBuf::from("-")]) + let (_s, operands) = parse_tail(["tail", "-"]).unwrap(); + assert_eq!(operands, vec![PathBuf::from("-")]) } #[test] fn normal_format() { - let s = parse_tail(["tail", "-c", "20", "some_file"]).unwrap(); + let (s, _operands) = parse_tail(["tail", "-c", "20", "some_file"]).unwrap(); assert_eq!(s.number, SigNum::Negative(20)); assert_eq!(s.mode, Mode::Bytes); } #[test] fn signum() { - let s = parse_tail(["tail", "-n", "20"]).unwrap(); + let (s, _operands) = parse_tail(["tail", "-n", "20"]).unwrap(); assert_eq!(s.number, SigNum::Negative(20)); - let s = parse_tail(["tail", "-n", "-20"]).unwrap(); + let (s, _operands) = parse_tail(["tail", "-n", "-20"]).unwrap(); assert_eq!(s.number, SigNum::Negative(20)); - let s = parse_tail(["tail", "-n", "+20"]).unwrap(); + let (s, _operands) = parse_tail(["tail", "-n", "+20"]).unwrap(); assert_eq!(s.number, SigNum::Positive(20)); - let s = parse_tail(["tail", "-n", "20b"]).unwrap(); + let (s, _operands) = parse_tail(["tail", "-n", "20b"]).unwrap(); assert_eq!(s.number, SigNum::Negative(20 * 512)); - let s = parse_tail(["tail", "-n", "+20b"]).unwrap(); + let (s, _operands) = parse_tail(["tail", "-n", "+20b"]).unwrap(); assert_eq!(s.number, SigNum::Positive(20 * 512)); - let s = parse_tail(["tail", "-n", "b"]).unwrap(); + let (s, _operands) = parse_tail(["tail", "-n", "b"]).unwrap(); assert_eq!(s.number, SigNum::Negative(512)); - let s = parse_tail(["tail", "-n", "+b"]).unwrap(); + let (s, _operands) = parse_tail(["tail", "-n", "+b"]).unwrap(); assert_eq!(s.number, SigNum::Positive(512)); assert!(parse_tail(["tail", "-n", "20invalid_suffix"]).is_err()); @@ -346,35 +344,35 @@ fn signum() { #[test] fn follow_mode() { // Sanity check: should be None initially - let s = parse_tail(["tail"]).unwrap(); + let (s, _operands) = parse_tail(["tail"]).unwrap(); assert_eq!(s.follow, None); - let s = parse_tail(["tail", "--follow"]).unwrap(); + let (s, _operands) = parse_tail(["tail", "--follow"]).unwrap(); assert_eq!(s.follow, Some(FollowMode::Descriptor)); - let s = parse_tail(["tail", "-f"]).unwrap(); + let (s, _operands) = parse_tail(["tail", "-f"]).unwrap(); assert_eq!(s.follow, Some(FollowMode::Descriptor)); - let s = parse_tail(["tail", "--follow=descriptor"]).unwrap(); + let (s, _operands) = parse_tail(["tail", "--follow=descriptor"]).unwrap(); assert_eq!(s.follow, Some(FollowMode::Descriptor)); - let s = parse_tail(["tail", "--follow=des"]).unwrap(); + let (s, _operands) = parse_tail(["tail", "--follow=des"]).unwrap(); assert_eq!(s.follow, Some(FollowMode::Descriptor)); - let s = parse_tail(["tail", "--follow=d"]).unwrap(); + let (s, _operands) = parse_tail(["tail", "--follow=d"]).unwrap(); assert_eq!(s.follow, Some(FollowMode::Descriptor)); - let s = parse_tail(["tail", "--follow=name"]).unwrap(); + let (s, _operands) = parse_tail(["tail", "--follow=name"]).unwrap(); assert_eq!(s.follow, Some(FollowMode::Name)); - let s = parse_tail(["tail", "--follow=na"]).unwrap(); + let (s, _operands) = parse_tail(["tail", "--follow=na"]).unwrap(); assert_eq!(s.follow, Some(FollowMode::Name)); - let s = parse_tail(["tail", "--follow=n"]).unwrap(); + let (s, _operands) = parse_tail(["tail", "--follow=n"]).unwrap(); assert_eq!(s.follow, Some(FollowMode::Name)); assert!(parse_tail(["tail", "--follow="]).is_err()); - let s = parse_tail(["tail", "-F"]).unwrap(); + let (s, _operands) = parse_tail(["tail", "-F"]).unwrap(); assert_eq!(s.follow, Some(FollowMode::Name)); } diff --git a/tests/coreutils/uniq.rs b/tests/coreutils/uniq.rs index b8eb7e4..28a3264 100644 --- a/tests/coreutils/uniq.rs +++ b/tests/coreutils/uniq.rs @@ -5,10 +5,10 @@ use uutils_args::{Arguments, Initial, Options, Value}; enum Arg { #[option("-f N", "--skip-fields=n")] SkipFields(usize), - + #[option("-s N", "--skip-chars=N")] SkipChars(usize), - + #[option("-c", "--count")] Count, @@ -69,16 +69,16 @@ impl Options for Settings { match arg { Arg::SkipFields(n) => { self.skip_fields = Some(n); - }, + } Arg::SkipChars(n) => { self.slice_start = Some(n); - }, + } Arg::Count => { self.show_counts = true; - }, + } Arg::IgnoreCase => { self.ignore_case = true; - }, + } Arg::Repeated => { self.repeats_only = true; } @@ -86,20 +86,20 @@ impl Options for Settings { self.repeats_only = true; self.all_repeated = true; self.delimiters = d; - }, + } Arg::Group(d) => { self.all_repeated = true; self.delimiters = d; - }, + } Arg::Unique => { self.uniques_only = true; - }, + } Arg::CheckChars(n) => { self.slice_stop = Some(n); } Arg::ZeroTerminated => { self.zero_terminated = true; - }, + } } } -} \ No newline at end of file +} diff --git a/tests/flags.rs b/tests/flags.rs index 1c4bd64..c1960cf 100644 --- a/tests/flags.rs +++ b/tests/flags.rs @@ -21,7 +21,7 @@ fn one_flag() { } } - let settings = Settings::default().parse(["test", "-f"]); + let (settings, _) = Settings::default().parse(["test", "-f"]); assert!(settings.foo); } @@ -51,19 +51,19 @@ fn two_flags() { } assert_eq!( - Settings::default().parse(["test", "-a"]), + Settings::default().parse(["test", "-a"]).0, Settings { a: true, b: false } ); assert_eq!( - Settings::default().parse(["test"]), + Settings::default().parse(["test"]).0, Settings { a: false, b: false } ); assert_eq!( - Settings::default().parse(["test", "-b"]), + Settings::default().parse(["test", "-b"]).0, Settings { a: false, b: true } ); assert_eq!( - Settings::default().parse(["test", "-a", "-b"]), + Settings::default().parse(["test", "-a", "-b"]).0, Settings { a: true, b: true } ); } @@ -87,9 +87,9 @@ fn long_and_short_flag() { } } - assert!(!Settings::default().parse(["test"]).foo); - assert!(Settings::default().parse(["test", "--foo"]).foo); - assert!(Settings::default().parse(["test", "-f"]).foo); + assert!(!Settings::default().parse(["test"]).0.foo); + assert!(Settings::default().parse(["test", "--foo"]).0.foo); + assert!(Settings::default().parse(["test", "-f"]).0.foo); } #[test] @@ -111,7 +111,7 @@ fn short_alias() { } } - assert!(Settings::default().parse(["test", "-b"]).foo); + assert!(Settings::default().parse(["test", "-b"]).0.foo); } #[test] @@ -133,7 +133,7 @@ fn long_alias() { } } - assert!(Settings::default().parse(["test", "--bar"]).foo); + assert!(Settings::default().parse(["test", "--bar"]).0.foo); } #[test] @@ -171,10 +171,10 @@ fn short_and_long_alias() { bar: true, }; - assert_eq!(Settings::default().parse(["test", "--bar"]), foo_true); - assert_eq!(Settings::default().parse(["test", "-b"]), foo_true); - assert_eq!(Settings::default().parse(["test", "--foo"]), bar_true); - assert_eq!(Settings::default().parse(["test", "-f"]), bar_true); + assert_eq!(Settings::default().parse(["test", "--bar"]).0, foo_true); + assert_eq!(Settings::default().parse(["test", "-b"]).0, foo_true); + assert_eq!(Settings::default().parse(["test", "--foo"]).0, bar_true); + assert_eq!(Settings::default().parse(["test", "-f"]).0, bar_true); } #[test] @@ -217,7 +217,7 @@ fn xyz_map_to_abc() { } assert_eq!( - Settings::default().parse(["test", "-x"]), + Settings::default().parse(["test", "-x"]).0, Settings { a: true, b: true, @@ -226,7 +226,7 @@ fn xyz_map_to_abc() { ); assert_eq!( - Settings::default().parse(["test", "-y"]), + Settings::default().parse(["test", "-y"]).0, Settings { a: false, b: true, @@ -235,7 +235,7 @@ fn xyz_map_to_abc() { ); assert_eq!( - Settings::default().parse(["test", "-xy"]), + Settings::default().parse(["test", "-xy"]).0, Settings { a: true, b: true, @@ -244,7 +244,7 @@ fn xyz_map_to_abc() { ); assert_eq!( - Settings::default().parse(["test", "-z"]), + Settings::default().parse(["test", "-z"]).0, Settings { a: true, b: true, @@ -279,7 +279,9 @@ fn non_rust_ident() { } assert_eq!( - Settings::default().parse(["test", "--foo-bar", "--super"]), + Settings::default() + .parse(["test", "--foo-bar", "--super"]) + .0, Settings { a: true, b: true } ) } @@ -302,7 +304,7 @@ fn number_flag() { } } - assert!(Settings::default().parse(["test", "-1"]).one) + assert!(Settings::default().parse(["test", "-1"]).0.one) } #[test] @@ -329,12 +331,12 @@ fn false_bool() { } } - assert!(Settings::default().parse(["test", "-a"]).foo); - assert!(!Settings::default().parse(["test", "-b"]).foo); - assert!(!Settings::default().parse(["test", "-ab"]).foo); - assert!(Settings::default().parse(["test", "-ba"]).foo); - assert!(!Settings::default().parse(["test", "-a", "-b"]).foo); - assert!(Settings::default().parse(["test", "-b", "-a"]).foo); + assert!(Settings::default().parse(["test", "-a"]).0.foo); + assert!(!Settings::default().parse(["test", "-b"]).0.foo); + assert!(!Settings::default().parse(["test", "-ab"]).0.foo); + assert!(Settings::default().parse(["test", "-ba"]).0.foo); + assert!(!Settings::default().parse(["test", "-a", "-b"]).0.foo); + assert!(Settings::default().parse(["test", "-b", "-a"]).0.foo); } #[test] @@ -356,9 +358,9 @@ fn verbosity() { } } - assert_eq!(Settings::default().parse(["test", "-v"]).verbosity, 1); - assert_eq!(Settings::default().parse(["test", "-vv"]).verbosity, 2); - assert_eq!(Settings::default().parse(["test", "-vvv"]).verbosity, 3); + assert_eq!(Settings::default().parse(["test", "-v"]).0.verbosity, 1); + assert_eq!(Settings::default().parse(["test", "-vv"]).0.verbosity, 2); + assert_eq!(Settings::default().parse(["test", "-vvv"]).0.verbosity, 3); } #[test] @@ -390,9 +392,9 @@ fn infer_long_args() { } } - assert!(Settings::default().parse(["test", "--all"]).all); - assert!(Settings::default().parse(["test", "--alm"]).almost_all); - assert!(Settings::default().parse(["test", "--au"]).author); + assert!(Settings::default().parse(["test", "--all"]).0.all); + assert!(Settings::default().parse(["test", "--alm"]).0.almost_all); + assert!(Settings::default().parse(["test", "--au"]).0.author); assert!(Settings::default().try_parse(["test", "--a"]).is_err()); } @@ -431,13 +433,13 @@ fn enum_flag() { } } - assert_eq!(Settings::default().parse(["test"]).foo, SomeEnum::Foo); + assert_eq!(Settings::default().parse(["test"]).0.foo, SomeEnum::Foo); assert_eq!( - Settings::default().parse(["test", "--bar"]).foo, + Settings::default().parse(["test", "--bar"]).0.foo, SomeEnum::Bar ); assert_eq!( - Settings::default().parse(["test", "--baz"]).foo, + Settings::default().parse(["test", "--baz"]).0.foo, SomeEnum::Baz, ); } diff --git a/tests/options.rs b/tests/options.rs index d52dfe7..4a328d5 100644 --- a/tests/options.rs +++ b/tests/options.rs @@ -24,6 +24,7 @@ fn string_option() { assert_eq!( Settings::default() .parse(["test", "--message=hello"]) + .0 .message, "hello" ); @@ -60,13 +61,14 @@ fn enum_option() { } assert_eq!( - Settings::default().parse(["test", "--format=bar"]).format, + Settings::default().parse(["test", "--format=bar"]).0.format, Format::Bar ); assert_eq!( Settings::default() .parse(["test", "--format", "baz"]) + .0 .format, Format::Baz ); @@ -101,11 +103,11 @@ fn enum_option_with_fields() { } assert_eq!( - Settings::default().parse(["test", "-i=thin"]).indent, + Settings::default().parse(["test", "-i=thin"]).0.indent, Indent::Spaces(4) ); assert_eq!( - Settings::default().parse(["test", "-i=wide"]).indent, + Settings::default().parse(["test", "-i=wide"]).0.indent, Indent::Spaces(8) ); } @@ -150,11 +152,11 @@ fn enum_with_complex_from_value() { } assert_eq!( - Settings::default().parse(["test", "-i=tabs"]).indent, + Settings::default().parse(["test", "-i=tabs"]).0.indent, Indent::Tabs ); assert_eq!( - Settings::default().parse(["test", "-i=4"]).indent, + Settings::default().parse(["test", "-i=4"]).0.indent, Indent::Spaces(4) ); } @@ -190,27 +192,30 @@ fn color() { } assert_eq!( - Settings::default().parse(["test", "--color=yes"]).color, + Settings::default().parse(["test", "--color=yes"]).0.color, Color::Always ); assert_eq!( - Settings::default().parse(["test", "--color=always"]).color, + Settings::default() + .parse(["test", "--color=always"]) + .0 + .color, Color::Always ); assert_eq!( - Settings::default().parse(["test", "--color=no"]).color, + Settings::default().parse(["test", "--color=no"]).0.color, Color::Never ); assert_eq!( - Settings::default().parse(["test", "--color=never"]).color, + Settings::default().parse(["test", "--color=never"]).0.color, Color::Never ); assert_eq!( - Settings::default().parse(["test", "--color=auto"]).color, + Settings::default().parse(["test", "--color=auto"]).0.color, Color::Auto ); assert_eq!( - Settings::default().parse(["test", "--color"]).color, + Settings::default().parse(["test", "--color"]).0.color, Color::Always ) } @@ -247,7 +252,8 @@ fn actions() { } } - let settings = Settings::default().parse(["test", "-m=Hello", "-m=World", "--send"]); + let (settings, _operands) = + Settings::default().parse(["test", "-m=Hello", "-m=World", "--send"]); assert_eq!(settings.messages, vec!["Hello", "World"]); assert_eq!(settings.last_message, "World"); assert!(settings.send); @@ -275,8 +281,8 @@ fn width() { } } - assert_eq!(Settings::default().parse(["test", "-w=0"]).width, None); - assert_eq!(Settings::default().parse(["test", "-w=1"]).width, Some(1)); + assert_eq!(Settings::default().parse(["test", "-w=0"]).0.width, None); + assert_eq!(Settings::default().parse(["test", "-w=1"]).0.width, Some(1)); } #[test] @@ -327,17 +333,17 @@ fn integers() { } } - assert_eq!(Settings::default().parse(["test", "--u8=5"]).n, 5); - assert_eq!(Settings::default().parse(["test", "--u16=5"]).n, 5); - assert_eq!(Settings::default().parse(["test", "--u32=5"]).n, 5); - assert_eq!(Settings::default().parse(["test", "--u64=5"]).n, 5); - assert_eq!(Settings::default().parse(["test", "--u128=5"]).n, 5); + assert_eq!(Settings::default().parse(["test", "--u8=5"]).0.n, 5); + assert_eq!(Settings::default().parse(["test", "--u16=5"]).0.n, 5); + assert_eq!(Settings::default().parse(["test", "--u32=5"]).0.n, 5); + assert_eq!(Settings::default().parse(["test", "--u64=5"]).0.n, 5); + assert_eq!(Settings::default().parse(["test", "--u128=5"]).0.n, 5); - assert_eq!(Settings::default().parse(["test", "--i8=5"]).n, 5); - assert_eq!(Settings::default().parse(["test", "--i16=5"]).n, 5); - assert_eq!(Settings::default().parse(["test", "--i32=5"]).n, 5); - assert_eq!(Settings::default().parse(["test", "--i64=5"]).n, 5); - assert_eq!(Settings::default().parse(["test", "--i128=5"]).n, 5); + assert_eq!(Settings::default().parse(["test", "--i8=5"]).0.n, 5); + assert_eq!(Settings::default().parse(["test", "--i16=5"]).0.n, 5); + assert_eq!(Settings::default().parse(["test", "--i32=5"]).0.n, 5); + assert_eq!(Settings::default().parse(["test", "--i64=5"]).0.n, 5); + assert_eq!(Settings::default().parse(["test", "--i128=5"]).0.n, 5); } #[test] @@ -373,19 +379,20 @@ fn ls_classify() { } } - assert_eq!(Settings::default().parse(["test"]).classify, When::Auto); + assert_eq!(Settings::default().parse(["test"]).0.classify, When::Auto); assert_eq!( Settings::default() .parse(["test", "--classify=never"]) + .0 .classify, When::Never, ); assert_eq!( - Settings::default().parse(["test", "--classify"]).classify, + Settings::default().parse(["test", "--classify"]).0.classify, When::Always, ); assert_eq!( - Settings::default().parse(["test", "-F"]).classify, + Settings::default().parse(["test", "-F"]).0.classify, When::Always, ); assert!(Settings::default().try_parse(["test", "-Falways"]).is_err()); @@ -413,13 +420,13 @@ fn mktemp_tmpdir() { } } - let settings = Settings::default().parse(["test", "-p", "X"]); + let (settings, _operands) = Settings::default().parse(["test", "-p", "X"]); assert_eq!(settings.tmpdir.unwrap(), "X"); - let settings = Settings::default().parse(["test", "--tmpdir=X"]); + let (settings, _operands) = Settings::default().parse(["test", "--tmpdir=X"]); assert_eq!(settings.tmpdir.unwrap(), "X"); - let settings = Settings::default().parse(["test", "--tmpdir"]); + let (settings, _operands) = Settings::default().parse(["test", "--tmpdir"]); assert_eq!(settings.tmpdir.unwrap(), "/tmp"); assert!(Settings::default().try_parse(["test", "-p"]).is_err()); @@ -490,8 +497,8 @@ fn deprecated() { } } - 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); } diff --git a/tests/positionals.rs b/tests/positionals.rs deleted file mode 100644 index 02eb1d4..0000000 --- a/tests/positionals.rs +++ /dev/null @@ -1,163 +0,0 @@ -use uutils_args::{Arguments, Options}; - -#[test] -fn one_positional() { - #[derive(Arguments, Clone)] - enum Arg { - #[arg("FILE", 1)] - File1(String), - } - - #[derive(Default)] - struct Settings { - file1: String, - } - - impl Options for Settings { - fn apply(&mut self, Arg::File1(f): Arg) { - self.file1 = f; - } - } - - let settings = Settings::default().parse(["test", "foo"]); - assert_eq!(settings.file1, "foo"); - - assert!(Settings::default().try_parse(["test"]).is_err()); -} - -#[test] -fn two_positionals() { - #[derive(Arguments)] - enum Arg { - #[arg("FOO", 1)] - Foo(String), - #[arg("BAR", 1)] - Bar(String), - } - - #[derive(Default)] - struct Settings { - foo: String, - bar: String, - } - - impl Options for Settings { - fn apply(&mut self, arg: Arg) { - match arg { - Arg::Foo(x) => self.foo = x, - Arg::Bar(x) => self.bar = x, - } - } - } - - let settings = Settings::default().parse(["test", "a", "b"]); - assert_eq!(settings.foo, "a"); - assert_eq!(settings.bar, "b"); - - assert!(Settings::default().try_parse(["test"]).is_err()); -} - -#[test] -fn optional_positional() { - #[derive(Arguments)] - enum Arg { - #[arg("FOO", 0..=1)] - Foo(String), - } - - #[derive(Default)] - struct Settings { - foo: Option, - } - - impl Options for Settings { - fn apply(&mut self, Arg::Foo(x): Arg) { - self.foo = Some(x); - } - } - - let settings = Settings::default().parse(["test"]); - assert_eq!(settings.foo, None); - let settings = Settings::default().parse(["test", "bar"]); - assert_eq!(settings.foo.unwrap(), "bar"); -} - -#[test] -fn collect_positional() { - #[derive(Arguments, Clone)] - enum Arg { - #[arg("FOO", ..)] - Foo(String), - } - - #[derive(Default)] - struct Settings { - foo: Vec, - } - - impl Options for Settings { - fn apply(&mut self, Arg::Foo(x): Arg) { - self.foo.push(x); - } - } - - let settings = Settings::default().parse(["test", "a", "b", "c"]); - assert_eq!(settings.foo, vec!["a", "b", "c"]); - let settings = Settings::default().parse(["test"]); - assert_eq!(settings.foo, Vec::::new()); -} - -#[test] -fn last1() { - #[derive(Arguments)] - enum Arg { - #[arg("FOO", last, ..)] - Foo(Vec), - } - - #[derive(Default)] - struct Settings { - foo: Vec, - } - - impl Options for Settings { - fn apply(&mut self, Arg::Foo(x): Arg) { - self.foo = x; - } - } - - let settings = Settings::default().parse(["test", "a", "-b", "c"]); - assert_eq!(settings.foo, vec!["a", "-b", "c"]); -} - -#[test] -fn last2() { - #[derive(Arguments, Clone)] - enum Arg { - #[arg("-a")] - A, - - #[arg("FOO", last, ..)] - Foo(Vec), - } - - #[derive(Default)] - struct Settings { - foo: Vec, - } - - impl Options for Settings { - fn apply(&mut self, arg: Arg) { - match arg { - Arg::A => {} - Arg::Foo(x) => self.foo = x, - } - } - } - - let settings = Settings::default().parse(["test", "-a"]); - assert_eq!(settings.foo, Vec::::new()); - - let settings = Settings::default().parse(["test", "--", "-a"]); - assert_eq!(settings.foo, vec!["-a"]); -}