Skip to content

Commit

Permalink
Merge pull request #44 from tertsdiepraam/maintenance
Browse files Browse the repository at this point in the history
Maintenance
  • Loading branch information
cakebaker authored Dec 2, 2023
2 parents c5680c2 + 2e814ac commit c965b3c
Show file tree
Hide file tree
Showing 16 changed files with 175 additions and 152 deletions.
6 changes: 3 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@ edition = "2021"
authors = ["Terts Diepraam"]
license = "MIT"

homepage = "https://github.com/tertsdiepraam/uutils-args"
repository = "https://github.com/tertsdiepraam/uutils-args"
homepage = "https://github.com/uutils/uutils-args"
repository = "https://github.com/uutils/uutils-args"
readme = "README.md"

[dependencies]
derive = { version = "0.1.0", path = "derive" }
uutils-args-derive = { version = "0.1.0", path = "derive" }
strsim = "0.10.0"
lexopt = "0.3.0"

Expand Down
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Copyright (c) Terts Diepraam
Copyright (c) uutils developers

Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
Expand Down
138 changes: 137 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,139 @@
# uutils-args

An experimental derive-based argument parser specifically for uutils
Argument parsing for the [uutils coreutils](https://www.github.com/uutils/coreutils) project.

It is designed to be flexible, while providing default
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.

## When you should not use this library

The goal of this library is to make it easy to build applications that
mimic the behaviour of the GNU coreutils. There are other applications
that have similar behaviour, which are C application that use `getopt`
and `getopt_long`. If you want to mimic that behaviour exactly, this
is the library for you. If you want to write basically anything else,
you should probably pick another argument parser.

## Getting Started

Parsing with this library consists of two "phases". In the first
phase, the arguments are mapped to an iterator of an `enum`
implementing [`Arguments`]. The second phase is mapping these
arguments onto a `struct` implementing [`Options`]. By defining
your arguments this way, there is a clear divide between the public
API and the internal representation of the settings of your app.

For more information on these traits, see their respective documentation:

- [`Arguments`]
- [`Options`]

Below is a minimal example of a full CLI application using this library.

```rust
use uutils_args::{Arguments, Initial, 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
#[option("-n", "--no-caps")]
NoCaps,

// This option takes a value:
/// Add exclamation marks to output
#[option("-e N", "--exclaim=N")]
ExclamationMarks(u8),

// This is a positional argument, the range specifies that
// at least one positional argument must be passed.
#[positional(1..)]
Text(String),
}

#[derive(Initial)]
struct Settings {
// We can change the default value with the field attribute.
#[initial(true)]
caps: bool,
exclamation_marks: u8,
text: String,
}

// To implement `Options`, we only need to provide the `apply` method.
// The `parse` method will be automatically generated.
impl Options<Arg> for Settings {
fn apply(&mut self, arg: Arg) {
match arg {
Arg::NoCaps => self.caps = false,
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: &'static [&'static str]) -> String {
let s = Settings::parse(args);
let mut output = if s.caps {
s.text.to_uppercase()
} else {
s.text
};
for i in 0..s.exclamation_marks {
output.push('!');
}
output
}

// 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!!!");
```

## Additional functionality

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.

## Examples

The following files contain examples of commands defined with
`uutils_args`:

- [hello world](https://github.com/uutils/uutils-args/blob/main/examples/hello_world.rs)
- [arch](https://github.com/uutils/uutils-args/blob/main/tests/coreutils/arch.rs)
- [b2sum](https://github.com/uutils/uutils-args/blob/main/tests/coreutils/b2sum.rs)
- [base32](https://github.com/uutils/uutils-args/blob/main/tests/coreutils/base32.rs)
- [basename](https://github.com/uutils/uutils-args/blob/main/tests/coreutils/basename.rs)
- [cat](https://github.com/uutils/uutils-args/blob/main/tests/coreutils/cat.rs)
- [echo](https://github.com/uutils/uutils-args/blob/main/tests/coreutils/echo.rs)
- [ls](https://github.com/uutils/uutils-args/blob/main/tests/coreutils/ls.rs)
- [mktemp](https://github.com/uutils/uutils-args/blob/main/tests/coreutils/mktemp.rs)
3 changes: 1 addition & 2 deletions derive/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[package]
name = "derive"
name = "uutils-args-derive"
version = "0.1.0"
edition = "2021"

Expand All @@ -10,6 +10,5 @@ proc_macro = true

[dependencies]
proc-macro2 = "1.0.47"
pulldown-cmark = "0.9.2"
quote = "1.0.21"
syn = { version = "2.0.18 ", features = ["full"] }
1 change: 1 addition & 0 deletions derive/LICENSE
3 changes: 3 additions & 0 deletions derive/src/argument.rs
Original file line number Diff line number Diff line change
@@ -1,3 +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;
Expand Down
3 changes: 3 additions & 0 deletions derive/src/attributes.rs
Original file line number Diff line number Diff line change
@@ -1,3 +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 syn::{
Expand Down
3 changes: 3 additions & 0 deletions derive/src/flags.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.

use proc_macro2::TokenStream;
use quote::quote;

Expand Down
3 changes: 3 additions & 0 deletions derive/src/help.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.

use std::{
io::Read,
path::{Path, PathBuf},
Expand Down
2 changes: 0 additions & 2 deletions derive/src/help_parser.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
// This file is part of the uutils coreutils package.
//
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.

Expand Down
3 changes: 3 additions & 0 deletions derive/src/initial.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.

use syn::{
parse::{Parse, ParseStream},
parse_macro_input, Data, DeriveInput, Fields, Token,
Expand Down
3 changes: 3 additions & 0 deletions derive/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.

mod argument;
mod attributes;
mod flags;
Expand Down
2 changes: 1 addition & 1 deletion design/problems_with_clap.md
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ libraries.
- Extremely flexible, even supports `dd`-style.
- A different configuration between short and long options requires a workaround.
- A many-to-many relation ship is possible, though not very ergonomic.
- For more information, see: https://github.com/tertsdiepraam/uutils-args/issues/17
- For more information, see: https://github.com/uutils/uutils-args/issues/17
- [`gumdrop`](https://github.com/murarth/gumdrop)
- Does not handle invalid UTF-8.
- Not configurable enough.
Expand Down
3 changes: 3 additions & 0 deletions src/error.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.

use std::{
error::Error as StdError,
ffi::OsString,
Expand Down
Loading

0 comments on commit c965b3c

Please sign in to comment.