Skip to content

Latest commit

 

History

History
 
 

5_cli

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 

Add help and usage

What about features not in standard Rust Library ?
Our cli can manage many subcommands but usage is not documented
How to build a cli with nice usage help ?

🎯 Objectives

  • add external libraries from crates.io
  • use Clap as command line argument parser

Import Modules

Rust provides many libraries out of the box but at a time, we need specifics features

Crates.io is the defaut public repository where to search modules

💡 Notes

What about playing chifoumi with computer as opponent ?

Add a lib to generate random number

2 ways to add dependency

Option 1: Use cargo cli

cargo add rand

Updating crates.io index
      Adding rand v0.8.5 to dependencies.
             Features:
             + alloc
             + getrandom
             + libc
             + rand_chacha
             + std
             + std_rng
             - log
             - min_const_gen
             - nightly
             - packed_simd
             - serde
             - serde1
             - simd_support
             - small_rng

📚 Additional resources

Option 2: Add dependency manually

Add dependency in cargo.toml

[dependencies]
rand = "0.8.5"

Example of usage

use rand::Rng;

fn main() {
   let mut rng = rand::thread_rng();

   let n1: u8 = rng.gen();
   println!("Random u8: {}", n1);

   println!("Integer de 0 to 3 included: {}", rng.gen_range(0..=3));
}

📚 Additional resources

Use Clap to parse our cli arguements

Clap a commonly used crate for Cli application in Rust

With Derive feature, we can setup a Cli in few lines

// from clap doc.rs
use clap::Parser;

/// Simple program to greet a person
#[derive(Parser, Debug)]
#[clap(author, version, about, long_about = None)]
struct Args {
   /// Name of the person to greet
   #[clap(short, long, value_parser)]
   name: String,

   /// Number of times to greet
   #[clap(short, long, value_parser, default_value_t = 1)]
   count: u8,
}

fn main() {
   let args = Args::parse();

   for _ in 0..args.count {
       println!("Hello {}!", args.name)
   }
}
$ demo --help
clap [..]
A simple to use, efficient, and full-featured Command Line Argument Parser

USAGE:
    demo[EXE] [OPTIONS] --name <NAME>

OPTIONS:
    -c, --count <COUNT>    Number of times to greet [default: 1]
    -h, --help             Print help information
    -n, --name <NAME>      Name of the person to greet
    -V, --version          Print version information
$ demo --name Me
Hello Me!

💡 Notes

  • You can describe commands without annotation if you want

📚 Additional resources

Focus on crates features

Cargo "features" provide a mechanism to express conditional compilation and optional dependencies.

cargo add clap

Updating crates.io index
      Adding clap v3.2.22 to dependencies.
             Features:
             + atty
             + color
             + std
             + strsim
             + suggestions
             + termcolor
             - backtrace
             - cargo
             - clap_derive
             - debug
             - deprecated
             - derive
             - env
             - once_cell
             - regex
             - terminal_size
             - unicase
             - unicode
             - unstable-doc
             - unstable-grouped
             - unstable-replace
             - unstable-v4
             - wrap_help
             - yaml
             - yaml-rust

Note that Derive Feature is no enabled

📚 Additonal resource

Option 1: use cargo add

use cargo add option -F (or --features)

cargo add clap -F derive

Option 2: Edit Cargo.toml

Update dependencies

[dependencies]
clap = { version = "3.2.22", features = ["derive"] }

Clap provides subcommand

We want to have distinct options for each cli functionality (greets and chifoumi)
Clap provides functionality to create subcommand with struct and enum

use clap::{Parser, Subcommand};

#[derive(Parser)]
#[clap(
    author = "Julien Rollin",
    version = "1.0.0",
    about = "Crabby cli",
    long_about = None
)]
#[clap(propagate_version = true)]
struct Cli {
    #[clap(subcommand)]
    command: Commands,
}

#[derive(Subcommand)]
enum Commands {
    /// Adds files to myapp
    Add { name: Option<String> },
    /// Doc about other command
    Greets {
        /// Name of the person to greet
        #[clap(short, long, value_parser)]
        name: String,
    },
}

fn main() {
    let cli = Cli::parse();

    // You can check for the existence of subcommands, and if found use their
    // matches just as you would the top level cmd
    match &cli.command {
        Commands::Add { name } => {
            println!("'myapp add' was used, name is: {:?}", name)
        }
        Commands::Greets { name } => {
            println!("Name: {:?}", name)
        }
    }
}

Try this command by yourself

cargo run 

cargo run -- -V

cargo run -- greets --help

📝 Exercice 1: Refactor your cli code with Clap

Refactor your app code with Clap and Subcommand

you shoud see beautiful cli usage when running your app

$ crabby --help

crabby 0.1.0
I am the crabby help usage.

USAGE:
    crabby <SUBCOMMAND>

OPTIONS:
    -h, --help       Print help information
    -V, --version    Print version information

SUBCOMMANDS:
    chifoumi    chifoumi with players
    greets      Greets with name
    help        Print this message or the help of the given subcommand(s)

📝 Exercice 2: Play Chifoumi against computer

If no player two is provided use a random game value for player 2

cargo run -- chifoumi -a paper

p1: Paper vs p2: Scissors => Lost

👏 Congrats

you can imagine building any cli now !

Check a solution with unit tests here

📝 Summary

What you learned

  • add crate with features
  • parse and document usage of cli app with clap

Next Part

🤙 Go to next part: API