23 releases
0.1.1 | Mar 30, 2023 |
---|---|
0.1.0 | Dec 22, 2022 |
0.0.20 | Sep 15, 2021 |
0.0.17 | Jun 30, 2021 |
0.0.1 | Apr 30, 2019 |
#468 in Command-line interface
Used in 7 crates
44KB
662 lines
"Batteries included" prelude with crates, types and functions useful for writing command-line interface tools and quick scripts.
This prelude aims to be useful in generic context of CLI tools and will try to minimise dependencies.
Things that fit this prelude:
- argument parsing,
- logging,
- I/O including reading from stdin,
- common file operations and directory structure,
- executing commands,
- extensions to stdlib and language functionality,
- digests and checksums,
- time and duration
- commonly used stdlib imports.
Things that will not be included:
- JSON parser or other formats,
- HTTP client or specific API clients
- TLS or other encryption libraries.
lib.rs
:
"Batteries included" prelude with crates, types and functions useful for writing command-line interface tools and quick scripts.
This prelude aims to be useful in generic context of CLI tools and will try to minimise dependencies.
Basic command-line interface program template
Example starting point for your program that features command line argument parsing with help message, logger setup and human-friendly error and panic messages.
use cotton::prelude::*;
/// Example script description
#[derive(Parser)]
struct Cli {
#[command(flatten)]
logging: ArgsLogger,
#[command(flatten)]
dry_run: ArgsDryRun,
}
fn main() -> FinalResult {
let Cli {
logging,
dry_run,
} = Cli::parse();
setup_logger(logging, vec![module_path!()]);
if !dry_run.enabled {
warn!("Hello world!");
}
Ok(())
}
Features
A small list of crates is always included in cotton. These are adding some common data types, language usability aids and common standard library imports:
- itertools - extends standard iterators
- linked-hash-map and linked-hash_set - ordered maps and sets
- maybe-string - handle probably UTF-8 encoded binary data
- boolinator - convert [Option] to [bool]
- tap - avoid need for
let
bindings
Cotton will also always import large number of commonly used standard library items.
All other dependencies are optional and can be opted-out by disabling default features and opting-in to only selected crates.
For convenience there are features defined that group several crates together:
regex
- regular expressions- regex - An implementation of regular expressions for Rust
args
- parsing of command line arguments- clap - A simple to use, efficient, and full-featured Command Line Argument Parser
logging
- logging macros and logger- log - A lightweight logging facade for Rust
- stderrlog - Logger that logs to stderr based on verbosity specified
time
- time and date- chrono - Date and time library for Rust
term
- working with terminal emulators- ansi_term - Library for ANSI terminal colours and styles (bold, underline)
- atty - A simple interface for querying atty
- zzz - Fast progress bar with sane defaults
- term_size - functions for determining terminal sizes and dimensions
hashing
- digest calculations and hex encoding- hex - Encoding and decoding data into/from hexadecimal representation
- sha2 - Pure Rust implementation of the SHA-2 hash function family
- digest - Traits for cryptographic hash functions and message authentication codes
files
- file metadata and temporary files- tempfile - A library for managing temporary files and directories
- filetime - Platform-agnostic accessors of timestamps in File metadata
- file-mode - Decode Unix file mode bits, change them and apply them to files
- file-owner - Set and get Unix file owner and group
signals
- UNIX signal handling- signal-hook - Unix signal handling
- uninterruptible - Guard type that keeps selected Unix signals suppressed
errors
- flexible error handling and error context- problem - Error handling for command line applications or prototypes
- error-context - Methods and types that help with adding additional context information to error types
- scopeguard - A RAII scope guard that will run a given closure when it goes out of scope
- assert_matches - Asserts that a value matches a pattern
app
- application environment- directories - A tiny mid-level library that provides platform-specific standard locations of directories
process
- running programs and handling input/output- shellwords - Manipulate strings according to the word parsing rules of the UNIX Bourne shell
- exec - Use the POSIX exec function to replace the running program with another
- mkargs - Build command arguments
- cradle - Execute child processes with ease
Non-default features:
backtrace
- enable backtraces for problem::Problem errors (also run your program withRUST_BACKTRACE=1
)
For example you my include cotton
like this in Cargo.toml
:
cotton = { version = "0.1.0", default-features = false, features = ["errors", "args", "logging", "app", "hashing", "process"] }
Error context
Generally libraries should not add context to the errors as it may be considered sensitive for some uses. In this library context (like file paths) will be provided by default.
Static error types
When you need proper error handling (e.g. on the internal modules or when you need to act on the errors specifically) use standard way of doing this.
Use enums with Debug
, Display
and Error
trait implementations.
Add additional From
implementations to make ?
operator to work.
If you need to add context to an error you can use error_context crate that is included in the prelude.
Example custom static error type implementation
use cotton::prelude::*;
#[derive(Debug)]
enum FileResourceError {
FileDigestError(PathBuf, FileDigestError),
NotAFileError(PathBuf),
}
impl Display for FileResourceError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
// Do not include chained error message in the message; let the client handle this (e.g. with Problem type)
FileResourceError::FileDigestError(path, _) => write!(f, "digest of a file {:?} could not be calculated", path),
FileResourceError::NotAFileError(path) => write!(f, "path {:?} is not a file", path),
}
}
}
impl Error for FileResourceError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
match self {
// Chain the internal error
FileResourceError::FileDigestError(_, err) => Some(err),
FileResourceError::NotAFileError(_) => None,
}
}
}
// This allows for calls like `foo().wrap_error_while_with(|| self.path.clone())?` to add extra `PathBuf` context to the error
impl From<ErrorContext<FileDigestError, PathBuf>> for FileResourceError {
fn from(err: ErrorContext<FileDigestError, PathBuf>) -> FileResourceError {
FileResourceError::FileDigestError(err.context, err.error)
}
}
Dependencies
~3–14MB
~175K SLoC