Crate rust_script_ext

source ·
Expand description

Opinionated set of extensions for use with rust-script. Using rust-script to run Rust like a shell script is great! This crate provides an opinionated set of extensions tailored towards common patterns in scripts. These patterns include file reading, argument parsing, error handling.

§Argument Parsing

A rudimentary argument parser is provided, simply call args.

The parsing is meant to be simple, tailored to script usage. For fully featured CLI apps, consider importing clap.

§Error Handling

Error handling uses the miette crate. A Result type alias is exposed, and IntoDiagnostic can be used to convert errors.

fn foo() -> Result<String> {
   std::fs::read_to_string("foo.txt")
       .into_diagnostic()
       .wrap_err("failed to open 'foo.txt'")
}

§Invoking Commands

Running commands is done through std::process::Command. There are a few helper traits and macros to assist in:

  1. Building a Command, and
  2. Executing a command.

Building commands can leverage the cmd! macro. This can be used to succintly build a command with arguments.

let x = 1.0;
let cmd = cmd!(./my-script.sh: foo/bar, --verbose, {x + 2.14});
assert_eq!(&cmd.cmd_str(), "./my-script.sh foo/bar --verbose 3.14");

The CommandExecute trait provides some methods which can execute a command and automatically collect the output, along with providing verbose error messages if something fails.

// Verbose means also print stdout/stderr to terminal as execution occurs
cmd!(ls: src).execute_str(Verbose).unwrap();

§Serialisation

Serialize, Deserialize, and DeserializeOwned are all exposed. Because of some path quirks with re-exported proc-macros, all derived values need to be tagged with the path to the serde crate, as shown below.

#[derive(Deserialize)]
#[serde(crate = "deps::serde")]
struct PizzaOrder {
   ham: bool,
   cheese: bool,
   pineapple: bool,
}

§Structured IO

A common pattern is to read/write with a particular serialisation format. Examples include reading a CSV file from disk, or writing JSON to stdout. An abstraction is provided (structured IO) which produces read_as and write_as functions on typical targets so working with structured data is ergonomic.

#[derive(Serialize, Deserialize, Debug, PartialEq)]
struct City {
    city: String,
    pop: u32,
}

let csv = "city,pop\nBrisbane,100000\nSydney,200000\n";

// read_as on anything that is Read
let x = csv.as_bytes().read_as::<CSV, City>().unwrap();

assert_eq!(
    x,
    vec![
        City {
            city: "Brisbane".to_string(),
            pop: 100_000,
        },
        City {
            city: "Sydney".to_string(),
            pop: 200_000,
        }
    ]
);

let mut buf = Vec::new();

// write &[T] (T: Serialize) as a CSV into a Writer
x.write_as(CSV, &mut buf).unwrap();
assert_eq!(buf, csv.as_bytes());

§Date and Time

Date and time is handled by exposing the time crate. For duration, humantime is used, exposing its Duration directly. This is done for duration parsing similar to what is experienced in unix tools.

§Number formatting

numfmt::Formatter is exposed (as NumFmt) which can be used to format numbers in a nice way. The numfmt module documentation describes ways to build a formatter, along with the syntax for parsing a format string.

§Progress reporting

how-u-doin can be used to show progress of a longer running script.

§Tabular printing

Tables can be printed neatly with TablePrinter, which is just exposing comfy-table.

Modules§