/cargo-script

Cargo script subcommand

Primary LanguageRustOtherNOASSERTION

cargo-script

cargo-script is a Cargo subcommand designed to let people quickly and easily run Rust "scripts" which can make use of Cargo's package ecosystem.

Or, to put it in other words, it lets you write useful, but small, Rust programs without having to create a new directory and faff about with Cargo.toml.

As such, cargo-script does two major things:

  1. Given a script, it extracts the embedded Cargo manifest and merges it with some sensible defaults. This manifest, along with the source code, is written to a fresh Cargo package on-disk.

  2. It caches the generated and compiled packages, regenerating them only if the script or its metadata have changed.

Installation

If you are using Rust 1.5 or higher, you can install cargo-script using Cargo's install subcommand:

cargo install cargo-script

Manually Compiling and Installing

cargo-script requires Rust 1.4.0 or higher to build.

Once built, you should place the resulting executable somewhere on your PATH. At that point, you should be able to invoke it by using cargo script.

Note that you can run the executable directly, but the first argument will need to be script.

If you want to run cargo script from a hashbang, you should also install the run-cargo-script program. We strongly recommend installing this program to the PATH and using #!/usr/bin/env run-cargo-script as the hashbang line.

Usage

The simplest way to use cargo-script is to simply pass it the name of the Rust script you want to execute:

$ echo 'fn main() { println!("Hello, World!"); }' > hello.rs
$ cargo script hello.rs
   Compiling hello v0.1.0 (file:///C:/Users/drk/AppData/Local/Cargo/script-cache/file-hello-25c8c198030c5d089740-3ace88497b98af47db6e)
Hello, World!
$ cargo script hello # you can omit the file extension
Hello, World!

Note that cargo-script does not currently do anything to suppress the regular output of Cargo. This is definitely on purpose and not simply out of abject laziness.

You may also embed a partial Cargo manifest at the start of your script, as shown below. cargo-script specifically supports the .crs extension to distinguish such "Cargoified" files from regular Rust source, but it will process regular .rs files in exactly the same manner.

Note that all of the following are equivalent:

now.rs (code block manifest and UNIX hashbang):

#!/usr/bin/env run-cargo-script
//! This is a regular crate doc comment, but it also contains a partial
//! Cargo manifest.  Note the use of a *fenced* code block, and the
//! `cargo` "language".
//!
//! ```cargo
//! [dependencies]
//! time = "0.1.25"
//! ```
extern crate time;
fn main() {
    println!("{}", time::now().rfc822z());
}

now.rs (dependency-only, short-hand manifest):

// cargo-deps: time="0.1.25"
// You can also leave off the version number, in which case, it's assumed
// to be "*".  Also, the `cargo-deps` comment *must* be a single-line
// comment, and it *must* be the first thing in the file, after the
// hashbang.
extern crate time;
fn main() {
    println!("{}", time::now().rfc822z());
}

Note: you can write multiple dependencies by separating them with commas. E.g. time="0.1.25", libc="0.2.5".

now.crs (prefix manifest; these might be removed in the future):

[dependencies]
time = "0.1.25"
---
extern crate time;
fn main() {
    println!("{}", time::now().rfc822z());
}
$ cargo script now
    Updating registry `https://github.com/rust-lang/crates.io-index`
   Compiling libc v0.1.8
   Compiling gcc v0.3.5
   Compiling time v0.1.25
   Compiling now v0.1.0 (file:///C:/Users/drk/AppData/Local/Cargo/script-cache/file-now-1410beff463a5c50726f-8dbf2bcf69d2d8208c4c)
Sat, 30 May 2015 19:26:57 +1000

If you are in a hurry, the above can also be accomplished by telling cargo-script that you wish to evaluate an expression, rather than an actual file:

$ cargo script --dep time --expr \
    "extern crate time; time::now().rfc822z().to_string()"
    Updating registry `https://github.com/rust-lang/crates.io-index`
   Compiling gcc v0.3.5
   Compiling libc v0.1.8
   Compiling time v0.1.25
   Compiling expr v0.1.0 (file:///C:/Users/drk/AppData/Local/Cargo/script-cache/expr-a7ffe37fbe6dccff132f)
"Sat, 30 May 2015 19:32:18 +1000"

Dependencies can also be specified with specific versions (e.g. --dep time=0.1.25); when omitted, cargo-script will simply use "*" for the manifest. The above can also be written variously as:

  • cargo script -d time -e "extern crate time; ..."
  • cargo script -d time -x time -e "..."
  • cargo script --dep-extern time --expr "..."
  • cargo script -D time -e "..."

The --dep-extern/-D option can be used to insert an automatic extern crate item into an expression (or loop, as shown below) script. This only works when the package name and compiled crate name match.

If you wish to use a dependency where the package and crate names do not match, you can specify the dependency with --dep/-d, and the extern crate name with --extern/-x.

Finally, you can also use cargo-script to write a quick stream filter, by specifying a closure to be called for each line read from stdin, like so:

$ cat now.crs | cargo script --loop \
    "let mut n=0; move |l| {n+=1; println!(\"{:>6}: {}\",n,l.trim_right())}"
   Compiling loop v0.1.0 (file:///C:/Users/drk/AppData/Local/Cargo/script-cache/loop-58079283761aab8433b1)
     1: [dependencies]
     2: time = "0.1.25"
     3: ---
     4: extern crate time;
     5: fn main() {
     6:     println!("{}", time::now().rfc822z());
     7: }

Note that you can achieve a similar effect to the above by using the --count flag, which causes the line number to be passed as a second argument to your closure:

$ cat now.crs | cargo script --count --loop \
    "|l,n| println!(\"{:>6}: {}\", n, l.trim_right())"
   Compiling loop v0.1.0 (file:///C:/Users/drk/AppData/Local/Cargo/script-cache/loop-58079283761aab8433b1)
     1: [dependencies]
     2: time = "0.1.25"
     3: ---
     4: extern crate time;
     5: fn main() {
     6:     println!("{}", time::now().rfc822z());
     7: }

Things That Should Probably Be Done

  • Suppress Cargo/rustc output unless there's actually a problem. Downside: scripts that require lots of network access and dependency compilation will appear to hang for a while with no feedback.

  • Somehow convince the Cargo devs to add aggressive caching of dependencies so that compiling anything that has dependencies doesn't take an age.

  • Gist support? I mean, if it's good enough for playpen...

License

Licensed under either of

at your option.

Contribution

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you shall be dual licensed as above, without any additional terms or conditions.