Command line interface as a function.

Overview


Command line interface as a function.

fncmd

Crates.io Crates.io



fncmd is an opinionated command line parser frontend that wraps around clap. The functionality is mostly identical to clap, but provides much more automated and integrated experience.

Motivation

Imagine a command line program you want to create. Essentially, it can be abstracted as a simple function that takes command line options as arguments. Then there should be nothing to stop you from being able to write it literally as a function, without using structs or builders like today's Rustaceans do.

This concept is tremendously inspired by argopt, I really appreciate the work. However, it still requires a bit of cumbersome code, especially for handling subcommands. fncmd has been rewritten from scratch to get rid of all the complexities. Let's dig into Subcommands section to see how we can handle it.

Installation

This crate is nightly-only. Make sure you have set up your toolchain as nightly before using (e.g. having rust-toolchain file).

To install, if you use cargo-edit:

cargo add fncmd

Or you can manually edit Cargo.toml:

[dependencies]
fncmd = "1.0.0"

Basics

This crate exposes just a single attribute macro, [fncmd], which can only be attached to the main function:

// main.rs
use fncmd::fncmd;

/// Description of the command line tool
#[fncmd]
pub fn main(
  /// Argument foo
  #[opt(short, long)]
  foo: String,
  /// Argument bar
  #[opt(short, long)]
  bar: Option<String>,
) {
  println!("{:?} {:?}", foo, bar);
}

That's all, and now you got a command line program with options handled by clap. With above code, the help message will be like below:

crate-name 0.1.0

Description of the command line tool

USAGE:
    crate-name [OPTIONS] --foo <FOO>

OPTIONS:
    -b, --bar <BAR>    Argument bar
    -f, --foo <FOO>    Argument foo
    -h, --help         Print help information
    -V, --version      Print version information

The name and the version of your crate are automatically inferred from Cargo metadata.

The usage of the opt attribute is exactly the same as the underlying clap attribute on arguments, they're just passed as is, except that it appends (long) if no configuration was provided, i.e. #[opt] means #[opt(long)]. If you want to take the argument foo without --foo, just omit #[opt].

Subcommands

As you may know, in Cargo project you can put entrypoints for additional binaries into src/bin. If 1) their names are prefixed by crate-name and 2) their main functions are decorated with the #[fncmd] attribute and 3) exposed as pub, then those are automatically wrapped up as subcommands of the default binary target crate-name. Say you have the following directory structure:

src
├── main.rs
└── bin
    ├── crate-name-subcommand1.rs
    └── crate-name-subcommand2.rs

You'll get the following subcommand structure:

crate-name
└── crate-name subcommand1
└── crate-name subcommand2

Specifying entrypoint paths manually

Configuring binary targets in your Cargo.toml should work as usual, for example:

[[bin]]
name = "crate-name"
path = "src/clis/crate-name.rs"

[[bin]]
name = "crate-name-subcommand1"
path = "src/clis/crate-name-subcommand1.rs"

[[bin]]
name = "crate-name-subcommand2"
path = "src/clis/crate-name-subcommand2.rs"

The resulting subcommand structure of this configuration is equivalent to what you get in the Subcommands section above. If you want a binary target to be handled by fncmd but not to be a subcommand regardless of its target name, just omit pub. So for example, when crate-name-subcommand2 is not exposed as pub, it won't contained within crate-name.

Nested subcommands

Following is how #[fncmd] macro determines which targets are subcommands (roughly explained):

  1. Get the name of the call-site target itself
  2. Enumerate all possible targets (#[fncmd]-annotated entrypoints)
  3. Filter out inappropriate items (ones not prefixed by the name of the call-site target)
  4. Filter out inappropriate items (ones prefixed by any other target name)
  5. Filter out inappropriate items (non-pub targets)

These steps are done for each macroexpansion. So for example:

[[bin]]
name = "crate-name"
path = "src/clis/crate-name.rs"

[[bin]]
name = "another"
path = "src/clis/another.rs"

[[bin]]
name = "another-sub" # `pub`
path = "src/clis/another-sub.rs"

[[bin]]
name = "another-sub-subsub" # `pub`
path = "src/clis/another-sub-subsub.rs"

[[bin]]
name = "another-orphan" # non-`pub`
path = "src/clis/another-orphan.rs"

[[bin]]
name = "another-orphan-sub" # `pub`
path = "src/clis/another-orphan-sub.rs"

This configuration yields up into these commands:

crate-name

another
└── another sub
    └── another sub subsub

another-orphan
└── another-orphan sub

Of course the same structure can be achieved without manually editing Cargo.toml, by placing files into the default location:

src
├── main.rs
└── bin
    ├── another.rs
    ├── another-sub.rs
    ├── another-sub-subsub.rs
    ├── another-orphan.rs
    └── another-orphan-sub.rs

Restrictions

fncmd won't support following features by design:

  • Show authors on the help message
  • Change the name and the version of the command to arbitrary values
  • Attach #[fncmd] to functions other than main

That's why fncmd states “opinionated”. Showing authors on the help will simply be a noise from general user's point of view, and changing metadata such as name and version to different values from the ones defined in Cargo.toml can easily undermine maintainability and consistency of them. Attaching #[fncmd] to arbitrary functions can lead to a bloated single file codebase, which should be avoided in general.

Releases(v1.0.1)
  • v1.0.1(Nov 28, 2021)

    What's Changed

    • refactor: remove unnecessary unstable feature flags by @yuhr in https://github.com/yuhr/fncmd/pull/1
    • refactor: remove unused dependency by @yuhr in https://github.com/yuhr/fncmd/pull/2
    • docs: update readme by @yuhr in https://github.com/yuhr/fncmd/pull/3
    • fix: assert attached function name to be main by @yuhr in https://github.com/yuhr/fncmd/pull/4
    • chore: configure release flow by @yuhr in https://github.com/yuhr/fncmd/pull/5

    New Contributors

    • @yuhr made their first contribution in https://github.com/yuhr/fncmd/pull/1

    Full Changelog: https://github.com/yuhr/fncmd/compare/v1.0.0...v1.0.1

    Source code(tar.gz)
    Source code(zip)
  • v1.0.0(Nov 23, 2021)

Owner
Yu Shimura
❤️ TypeScript, Rust, Haskell
Yu Shimura
Low-level Rust library for implementing terminal command line interface, like in embedded systems.

Terminal CLI Need to build an interactive command prompt, with commands, properties and with full autocomplete? This is for you. Example, output only

HashMismatch 43 Oct 23, 2021
Crunch is a command-line interface (CLI) to claim staking rewards every X hours for Substrate-based chains

crunch · crunch is a command-line interface (CLI) to claim staking rewards every X hours for Substrate-based chains. Why use crunch To automate payout

null 16 Nov 6, 2021
Command line interface for Solana Metaplex programs.

Metaplex Command Line Interface This is a command line interface for creating and managing non-fungible tokens on the Solana blockchain through the Me

Caleb Everett 7 Oct 31, 2021
Railway CLI - This is the command line interface for Railway.

Railway CLI This is the command line interface for Railway. Use it to connect your code to Railways infrastructure without needing to worry about envi

Nebula 2 Nov 21, 2021
A full featured, fast Command Line Argument Parser for Rust

clap Command Line Argument Parser for Rust It is a simple-to-use, efficient, and full-featured library for parsing command line arguments and subcomma

null 7k Nov 26, 2021
Docopt for Rust (command line argument parser).

THIS CRATE IS UNMAINTAINED This crate is unlikely to see significant future evolution. The primary reason to choose this crate for a new project is if

null 733 Nov 28, 2021
Parse command line arguments by defining a struct.

StructOpt Parse command line arguments by defining a struct. It combines clap with custom derive. Documentation Find it on Docs.rs. You can also check

Guillaume P. 2.3k Nov 24, 2021
A command line progress reporting library for Rust

indicatif Documentation A Rust library for indicating progress in command line applications to users. This currently primarily provides progress bars

Armin Ronacher 2.2k Nov 22, 2021
⚡️ Lightning-fast and minimal calendar command line. Written in Rust 🦀

⚡️ Lightning-fast and minimal calendar command line. It's similar to cal. Written in Rust ??

Arthur Henrique 21 Nov 9, 2021
Command-Line program that takes images and produces the copy of the image with a thin frame and palette made of the 10 most frequent colors.

paleatra v.0.0.1 Command-Line program that takes an image and produces the copy of the image with a thin frame and palette made of the 10 most frequen

Beka Modebadze 19 Oct 27, 2021
parse command-line arguments into a hashmap and vec of positional args

parse command-line arguments into a hashmap and vec of positional args This library doesn't populate custom structs, format help messages, or convert types.

James Halliday 16 Oct 7, 2021
A tactics trainer for the command line

Chess Tactics CLI Practice some chess tactics in your terminal while you wait for your code to compile. Fetches tactics from this tactics API. Built W

Marcus Buffett 16 Oct 27, 2021
Cork is a simple command-line calculator, mainly targeted towards people who deal with hex numbers

Cork is a simple command-line calculator, mainly targeted towards people who deal with hex numbers. It deals only with integer arithmetic. Expressions may involve mixed bases (limited to decimal, hexadecimal, octal and binary numbers). The global output format may be set to a particular radix - by default it is hex.

Deep Majumder 29 Nov 2, 2021
A small command-line application to view images from the terminal written in Rust.

A small command-line application to view images from the terminal written in Rust. It is basically the front-end of viuer

Atanas Yankov 1.3k Nov 30, 2021
belt is a command line app that can show your time from a list of selected time zones

A CLI app to show your time from a list of selected time zones, and a rust lib to parse dates in string formats that are commonly used.

Rollie Ma 6 Oct 10, 2021
rip is a command-line deletion tool focused on safety, ergonomics, and performance

rip (Rm ImProved) rip is a command-line deletion tool focused on safety, ergonomics, and performance. It favors a simple interface, and does not imple

Kevin Liu 473 Nov 17, 2021
A command-line benchmarking tool

hyperfine 中文 A command-line benchmarking tool. Demo: Benchmarking fd and find: Features Statistical analysis across multiple runs. Support for arbitra

David Peter 9.3k Nov 29, 2021
An interactive cheatsheet tool for the command-line

navi An interactive cheatsheet tool for the command-line. navi allows you to browse through cheatsheets (that you may write yourself or download from

Denis Isidoro 10k Nov 23, 2021
A command-line downloader for sites archived on the Wayback Machine

This is a small command-line utility I wrote to help with browsing archived websites from the Wayback Machine, which can sometimes be pretty slow.

Jonas Schievink 7 Aug 29, 2021