Command-line arguments via function arguments


Keywords
macro, parser, cli, main
Licenses
Apache-2.0/Apache-2.0/MIT

Documentation

nameless

Full-service command-line parsing

Github Actions CI Status crates.io page docs.rs docs zulip chat

This is currently an experimental project, and the API and command-line argument syntax are not currently stable.

Nameless provides full-service command-line parsing. This means you just write a main function with arguments with the types you want, add a conventional documentation comment, and it takes care of the rest:

Rust code:

use nameless::{InputByteStream, OutputByteStream};
use std::io::{self, Read, Write};

/// A simple program with input and output
///
/// # Arguments
///
/// * `input` - Input source
/// * `output` - Output sink
#[kommand::main]
fn main(mut input: InputByteStream, mut output: OutputByteStream) -> io::Result<()> {
    let mut s = String::new();
    input.read_to_string(&mut s)?;
    output.write_all(s.as_bytes())
}

Cargo.toml:

[dependencies]
kommand = "0"
nameless = "0"

Nameless completely handles "string to stream" translation. And in doing so, it doesn't just support files, but also gzipped files (*.gz), stdin/stdout (-), child processes ($(...)) (not yet on Windows tho), and URLs, including http:, https:, scp: (enable the "ssh2" feature), file:, and data:. And on output, nameless automatically takes care of piping data through bat for syntax highlighting and paging. So while your code is busy doing one thing and doing it well, nameless takes care of streaming the data in and out.

"Everything is a URL, and more", on Linux, macOS, Windows, and more.

kommand::main parses the documentation comment to extract the program description and the arguments. The command-line usage for the example above looks like this:

$ cargo run -- --help
simple-filter 0.0.0
A simple program with input and output

USAGE:
    simple-filter <input> <output>

FLAGS:
    -h, --help       Prints help information
    -V, --version    Prints version information

ARGS:
    <input>     Input source
    <output>    Output sink

More features

kommand is a wrapper around clap_derive, and supports the same attributes.

To add a flag, for example, #[kommand(short = 'n', long)] number: u32 means an argument with type i32 which can be specified with -n or --number on the command line. The grep example and basic example show examples of this.

The clap-v3 documentation for the full list of available features.

What's inside

This library provides:

Why "nameless"?

The name "nameless" refers to how, from the program's perspective, the string names of the inputs and outputs are hidden by the library.

Of course, sometimes you do want to know the name of an input, such as to display it in an error message. Nameless's pseudonym mechanism provides names for InputByteStream and other stream types, which allow the name to be displayed without exposing it to the application.

And sometimes you want to know an input file's extension, to determine what type of input it is. InputByteStream and other stream types have a media_type function which returns the media type (aka MIME type). If the input is a file, the type is inferred from the extension; if it's an HTTP stream, the type is inferred from the Content-Type header, and so on.

Why is it important to hide the name? On a theoretical level, most computations shouldn't care about where data is coming from or where it's going. This helps separate the concerns of what the program primarily does and how the program interacts with the local organization of resources. On a practical level, this is what makes it possible for nameless to transparently support URLs, child processes, and other things. And, it will support applications which are useful on conventional platforms, but which also work on platforms that lack filesystems, such as embedded systems or systems with new kinds of storage abstractions.

Hiding the names also helps programs avoid accidentally having behavior that depends on the names of files it accesses, which is a common source of trouble in deterministic-build environments.

Data URLs

data: URLs aren't as widely known, but are cool and deserve special mention. They carry a payload string in the URL itself which produced as the input stream. For example, opening data:,Hello%2C%20World! produces an input stream that reads the string "Hello, World!". Payloads can also be base64 encoded, like this: data:text/plain;base64,SGVsbG8sIFdvcmxkIQ==. So you can pass a literal string directly into a program's input stream instead of creating a temporary file.

Looking forward

Nameless is actively evolving! Watch this space for much more to come, and chat with us in Zulip, if you're interested in where we're going.

Literary reference

‘This must be the wood,’ she said thoughtfully to herself, ‘where things have no names.’

— "Through the Looking Glass", by Lewis Carroll