Writing Rust CLI Applications

Jean-Marcel Belmont

Senior Software Engineer

What is a cli anyway?

A command-line interface or command language interpreter (CLI), also known as command-line user interface, console user interface and character user interface (CUI), is a means of interacting with a computer program where the user (or client) issues commands to the program in the form of successive lines of text (command lines). A program which handles the interface is called a command language interpreter or shell (computing).

According to wikipedia:

clis are for hackers right

Who needs clis I have my IDE

I press F5 and boom I am golden!!!

Let the CLI Cookoff begin!!

CLI program that computes a sum in Rust.

Rust has several different ways to iterate through collections!

We can use a for in loop with Rust.

Notice that we didn't put a return statement here!

sum is an expression and will return.

We can use iterator methods to do the same action

fold is an iterator method that applies a function, producing a single, final value.

Notice here that we used the explicit return statement here!

Using the return statement like this is not considered idiomatic.

Demo!

Making an HTTP Request

Let us write a Rust Script that makes an HTTP Request

Here we use the reqwest library to make an HTTP request

Here is the main program

Here I put a sleep so that I could quickly copy the standard output of this command!

The tee command will spit out standard output and create a file for us in one go.

Demo this script!!

Lets Test our Rust CLI programs!!

Changing the structure of the summation program for testing

Here we supply an option of --lib to cargo and it generates a lib.rs

Notice that cargo generated a test for us in a file called lib.rs

We will break apart the 2 summation functions from the main.rs into the lib.rs

Notice here that we add pub for public visibility

Notice here that we now use our lib as an internal crate!

We call our public method with the crate name now.

We add the following annotation to create our unit test cases.

Now we can unit test our cli program by issuing the cargo test command

Notice here that both of our unit test cases run when we run cargo test!

Demo Building average cli program!

Hacking with the Rust clap library

Using Clap to build Robust Clis in Rust

Clap has many features to build robust features

Using Clap to build Robust Clis in Rust

let matches = App::new("Calculator")
        .subcommand(SubCommand::with_name("add") 
            .about("Sum some numbers")
            .version("0.1") 
            .author("Jean-Marcel Belmont")
            .arg(Arg::with_name("numbers")
                .multiple(true)
                .help("the numbers to add")
                .required(true)))
        .subcommand(SubCommand::with_name("subtract") 
            .about("Subtract some numbers")
            .version("0.1") 
            .author("Jean-Marcel Belmont")
            .arg(Arg::with_name("numbers")
                .multiple(true)
                .help("the numbers to subtract")
                .index(1)
                .required(true)))

Here we use subcommands to build a calculator cli application

Here we run the binary executable to run our simple calculator cli application

Notice that each subcommand performs a different calculation.

Also notice that we are using the binary executable here.

 

It is created upon successful `cargo run` and/or `cargo build`.

Demo!!!

Let's Check off parsing, serialization, and deserialization of our Rust Bucket List Next

Data Serialization, Deserialization and Parsing in Rust CLIs

The serde crate

The serde book

We will write a command line application in Rust.

 

It will base64 decode a json web token to standard output

Use statements

Library Dependencies

Here are 3 Structs and notice that the Token struct has both the Header and Claims struct embedded in it.

The new methods for the Claims and Headers structs instantiate a new Claims and Header instance.

Here is the main program.

Demo!!!

Having some ascii fun now in the command line

Having some ascii fun now in the command line

This C program generates ascii characters to standard output.

This shell command replaces the output with single quotes and adds a comma to the end except last line.

Let us take out the portion of the main ascii_table.c that print out to ascii and put it into its own c source file

Let us use the clang compiler to generate an object file and then help the rust compiler find the symbol to use in C.

We can find the generated symbol in the print_ascii.o object file using objdump utility.

Notice here that the printAscii exists in the object file and so we can use this symbol using a concept called foreign function interface

We can use the unsafe keyword that is talked about in the Rust Book Chapter 19 to call our printAscii function written in C

Notice that here we wrap the printAscii function with the extern keyword

We then call it using an unsafe block in Rust

Remember that we then compiled our rust main file and used a couple of options helping the rust compiler locate the symbol that we will use in C.

Here notice that the rust compiler has several options that we need to use


rustc src/main.rs -l print_ascii.o -L .

This will generate a binary executable called main that we can run now.

Demo Time!!

Time to shred some web scraping waves!!

Here are our dependencies for the Web Scraping project:

Here is our use and crate statements:

Here is the main logic for the Web Scraping Project

Here is the main function:

Notice that we ran the run() function and wrap it in a `if let` statement which will print error and exit if there is an error.

Demo!!

Debugging Rust Applications

Printing values with println! macro in Rust

Printing with named value

Pretty Print with println("{:#?}", something);

Debugging Rust applications with rust-lldb.

There are 2 debuggers that come prebundled with cargo:

If you are working on Mac OS X it is easier to use rust-lldb

You can use gdb in Mac OS X but it requires extra steps:

First install gdb, easiest way is via homebrew package manager

Next you will need to codesign gdb, if you are interested follow the steps in this CODESIGN-GIST

For the purposes of this demo we will use rust-lldb

Here we start rust-lldb using the compiled binary executable

Notice here that we run the main.rs file by issuing the command run

Let us set a breakpoint in lldb

This is the short form version of setting a breakpoint

This is the longer but more powerful way to set breakpoint

Let us run our program with breakpoint set

Notice here that lldb stopped in line 6 which is our breakpoint!

We issued command "frame variable args" to see variable

Note that the commands for printing: p (simple types), po (objects), and pa (arrays) don't work as well with Rust.

The frame command is used to examine current stack frame and has various subcommands such as variable which work better!

Step over with lldb

Resume/Continue execution of program

Notice here that we continued until the next breakpoint!

List breakpoints

Delete all breakpoints

Delete specific breakpoint(s)

First do Instruction level single step, stepping over calls

Next actually step into function

Next let us step over and then print value of variable

Step out of the function

Get stack frame information and continue execution

Use expression to set values in running program

Print back trace information

Kill lldb session

Useful Rust Libraries for Command Line Applications:

Parse command line argument by defining a struct. It combines clap with custom derive.

It is a simple-to-use, efficient, and full-featured library for parsing command line arguments and subcommands when writing console/terminal applications.

 

Useful Rust Libraries for Command Line Applications:

log

A Rust library providing a lightweight logging facade. A logging facade provides a single logging API that abstracts over the actual logging implementation. Libraries can use the logging API provided by this crate, and the consumer of those libraries can choose the logging implementation that is most suitable for its use case.

A rust library for indicating progress in command line applications to users.

Useful Rust Libraries for Command Line Applications:

A tool to help invoke fuzzers in Rustlang

Easy command initialization and assertions.

Proptest is a property testing framework inspired by the Hypothesis framework for Python. It allows to test that certain properties of your code hold for arbitrary inputs, and if a failure is found, automatically finds the minimal test case to reproduce the problem.

Useful Rust Libraries for Command Line Applications:

Library for safe and correct Unix signal handling in Rust.

The asynchronous run-time for the Rust programming language.

Please read the most excellent Rustlang Nursery online book on writing command line applications in Rust: https://rust-lang-nursery.github.io/cli-wg/index.html

Questions?

How to find me:

jbelmont @ Github

jbelmont80 @ Twitter

Personal website @ marcelbelmont.com

Jean-Marcel Belmont @ LinkedIn

Writing Rust Command Line Applications

By Jean-Marcel Belmont

Writing Rust Command Line Applications

A talk focusing on building command line applications with Rust and Golang.

  • 1,465