Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove the Initial trait #51

Merged
merged 1 commit into from
Dec 8, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 19 additions & 25 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,16 +41,16 @@ For more information on these traits, see their respective documentation:
Below is a minimal example of a full CLI application using this library.

```rust
use uutils_args::{Arguments, Initial, Options};
use uutils_args::{Arguments, Options};

#[derive(Arguments)]
enum Arg {
// The doc strings below will be part of the `--help` text
// First we define a simple flag:
/// Do not transform input text to uppercase
#[arg("-n", "--no-caps")]
NoCaps,
/// Transform input text to uppercase
#[arg("-c", "--caps")]
Caps,

// This option takes a value:
/// Add exclamation marks to output
#[arg("-e N", "--exclaim=N")]
Expand All @@ -62,10 +62,8 @@ enum Arg {
Text(String),
}

#[derive(Initial)]
#[derive(Default)]
struct Settings {
// We can change the default value with the field attribute.
#[initial(true)]
caps: bool,
exclamation_marks: u8,
text: String,
Expand All @@ -76,7 +74,7 @@ struct Settings {
impl Options<Arg> for Settings {
fn apply(&mut self, arg: Arg) {
match arg {
Arg::NoCaps => self.caps = false,
Arg::Caps => self.caps = true,
Arg::ExclamationMarks(n) => self.exclamation_marks += n,
Arg::Text(s) => {
if self.text.is_empty() {
Expand All @@ -91,7 +89,7 @@ impl Options<Arg> for Settings {
}

fn run(args: &'static [&'static str]) -> String {
let s = Settings::parse(args);
let s = Settings::default().parse(args);
let mut output = if s.caps {
s.text.to_uppercase()
} else {
Expand All @@ -104,24 +102,20 @@ fn run(args: &'static [&'static str]) -> String {
}

// The first argument is the binary name. In this example it's ignored.
assert_eq!(run(&["shout", "hello"]), "HELLO");
assert_eq!(run(&["shout", "-e3", "hello"]), "HELLO!!!");
assert_eq!(run(&["shout", "-e", "3", "hello"]), "HELLO!!!");
assert_eq!(run(&["shout", "--no-caps", "hello"]), "hello");
assert_eq!(run(&["shout", "-e3", "-n", "hello"]), "hello!!!");
assert_eq!(run(&["shout", "-e3", "hello", "world"]), "HELLO WORLD!!!");
assert_eq!(run(&["shout", "hello"]), "hello");
assert_eq!(run(&["shout", "-e3", "hello"]), "hello!!!");
assert_eq!(run(&["shout", "-e", "3", "hello"]), "hello!!!");
assert_eq!(run(&["shout", "--caps", "hello"]), "HELLO");
assert_eq!(run(&["shout", "-e3", "-c", "hello"]), "HELLO!!!");
assert_eq!(run(&["shout", "-e3", "-c", "hello", "world"]), "HELLO WORLD!!!");
```

## Additional functionality
## Value parsing

To make it easier to implement [`Arguments`] and [`Options`], there are
two additional traits:

- [`Initial`] is an alternative to the [`Default`] trait from the standard
library, with a richer derive macro.
- [`Value`] allows for easy parsing from `OsStr` to any type
implementing [`Value`]. This crate also provides a derive macro for
this trait.
To make it easier to implement [`Arguments`] and [`Options`], there is the
[`Value`] trait, which allows for easy parsing from `OsStr` to any type
implementing [`Value`]. This crate also provides a derive macro for
this trait.

## Examples

Expand Down
136 changes: 0 additions & 136 deletions derive/src/initial.rs

This file was deleted.

6 changes: 0 additions & 6 deletions derive/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ mod attributes;
mod flags;
mod help;
mod help_parser;
mod initial;

use argument::{
free_handling, long_handling, parse_argument, parse_arguments_attr, positional_handling,
Expand All @@ -19,11 +18,6 @@ use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, Data::Enum, DeriveInput};

#[proc_macro_derive(Initial, attributes(initial))]
pub fn initial(input: TokenStream) -> TokenStream {
initial::initial(input)
}

#[proc_macro_derive(Arguments, attributes(arg, arguments))]
pub fn arguments(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
Expand Down
12 changes: 6 additions & 6 deletions examples/deprecated.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use uutils_args::{Arguments, Initial, Options};
use uutils_args::{Arguments, Options};

fn parse_minus(s: &str) -> Option<&str> {
let num = s.strip_prefix('-')?;
Expand Down Expand Up @@ -27,7 +27,7 @@ enum Arg {
Plus(isize),
}

#[derive(Initial)]
#[derive(Default)]
struct Settings {
n1: usize,
n2: isize,
Expand All @@ -43,8 +43,8 @@ impl Options<Arg> for Settings {
}

fn main() {
assert_eq!(Settings::parse(["test", "-10"]).n1, 10usize);
assert!(Settings::try_parse(["test", "--10"]).is_err());
assert_eq!(Settings::parse(["test", "+10"]).n2, 10isize);
assert_eq!(Settings::parse(["test", "+-10"]).n2, -10isize);
assert_eq!(Settings::default().parse(["test", "-10"]).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);
}
11 changes: 7 additions & 4 deletions examples/hello_world.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use uutils_args::{Arguments, Initial, Options};
use uutils_args::{Arguments, Options};

#[derive(Arguments)]
#[arguments(file = "examples/hello_world_help.md")]
Expand All @@ -20,10 +20,8 @@ enum Arg {
Hidden,
}

#[derive(Initial)]
struct Settings {
name: String,
#[initial(1)]
count: u8,
}

Expand All @@ -38,7 +36,12 @@ impl Options<Arg> for Settings {
}

fn main() -> Result<(), uutils_args::Error> {
let settings = Settings::parse(std::env::args_os());
let settings = Settings {
name: String::new(),
count: 1,
}
.parse(std::env::args_os());

for _ in 0..settings.count {
println!("Hello, {}!", settings.name);
}
Expand Down
36 changes: 10 additions & 26 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -160,21 +160,6 @@ impl<T: Arguments> ArgumentIter<T> {
}
}

/// An alternative for the [`Default`] trait, with a more feature
/// packed derive macro.
///
/// The `Initial` trait is used by `Options` to construct the initial
/// state of the options before any arguments are parsed.
///
/// The [derive macro](derive@Initial) supports setting the initial
/// value per field and parsing the initial values from environment
/// variables. Otherwise, it will be equivalent to the derive macro
/// for the [`Default`] trait.
pub trait Initial: Sized {
/// Create the initial state of `Self`
fn initial() -> Self;
}

/// Defines the app settings by consuming [`Arguments`].
///
/// When implementing this trait, only two things need to be provided:
Expand All @@ -184,35 +169,34 @@ pub trait Initial: Sized {
/// type onto the options.
///
/// By default, the [`Options::parse`] method will
/// 1. create a new instance of `Self` using [`Initial::initial`],
/// 2. repeatedly call [`ArgumentIter::next_arg`] and call [`Options::apply`]
/// 1. repeatedly call [`ArgumentIter::next_arg`] and call [`Options::apply`]
/// on the result until the arguments are exhausted,
/// 3. and finally call [`Arguments::check_missing`].
pub trait Options<Arg: Arguments>: Sized + Initial {
/// 2. and finally call [`Arguments::check_missing`] to check whether all
/// required arguments were given.
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
fn parse<I>(args: I) -> Self
/// Parse an iterator of arguments into the options
fn parse<I>(self, args: I) -> Self
where
I: IntoIterator + 'static,
I::Item: Into<OsString>,
{
exit_if_err(Self::try_parse(args), Arg::EXIT_CODE)
exit_if_err(self.try_parse(args), Arg::EXIT_CODE)
}

fn try_parse<I>(args: I) -> Result<Self, Error>
fn try_parse<I>(mut self, args: I) -> Result<Self, Error>
where
I: IntoIterator + 'static,
I::Item: Into<OsString>,
{
let mut _self = Self::initial();
let mut iter = Arg::parse(args);
while let Some(arg) = iter.next_arg()? {
_self.apply(arg);
self.apply(arg);
}
Arg::check_missing(iter.positional_idx)?;
Ok(_self)
Ok(self)
}
}

Expand Down
Loading
Loading