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:
- Building a
Command
, and - 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§
- Exposed dependency crates.
- Typical imports.