Crate cotton

Source
Expand description

“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:

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
  • 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 with RUST_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)
 }
}

Re-exports§

pub use itertools;
pub use linked_hash_map;
pub use linked_hash_set;
pub use boolinator;
pub use tap;
pub use regex;
pub use tempfile;
pub use filetime;
pub use file_owner;
pub use file_mode;
pub use problem;
pub use error_context;
pub use scopeguard;
pub use assert_matches;
pub use chrono;
pub use ansi_term;
pub use atty;
pub use zzz;
pub use term_size;
pub use clap;
pub use log;
pub use stderrlog;
pub use sha2;
pub use digest;
pub use shellwords;
pub use exec;
pub use mkargs;
pub use cradle;
pub use hex;
pub use maybe_string;
pub use signal_hook;
pub use uninterruptible;
pub use directories;

Modules§

prelude

Macros§

init_app_info
Initializes application name and author with CARGO_PKG_NAME and CARGO_PKG_AUTHORS.