Gestion de la ligne de commande

Jusqu'ici nous avons géré les arguments donnés sur la ligne de commande à la main grâce à l'itérateur std::env::args(). Il existe des crates permettant de gérer de manière plus complète la ligne de commande. Le plus connu d'entre eux est clap.

clap est capable de parser les arguments, sous-commandes et les options fournis sur la ligne de commande et de vérifier qu'ils correspondent à la spécification.

Nous allons configurer clap pour ajouter une sous-commande group qui affichera les logins ayant le même mot de passe comme nous faisions précédemment.

  1. Ajoutez le crate clap (version 3.x) à votre projet grâce à cargo add clap@3 --features derive. Cette sous-commande de cargo, venant du crate cargo-edit que vous avez installé précedemment, ajoute la dernière version du crate à votre projet avec les options derive de ce crate qui permet d'utiliser Parser.

  2. Après avoir importé les entités nécessaires du crate clap, créez la description de votre application :

use clap::{Args, Parser, Subcommand};

#[derive(Parser)]
#[clap(version, author, about)]
struct AppArgs {
    #[clap(subcommand)]
    command: Command,
}

#[derive(Subcommand)]
enum Command {
    /// Check duplicate passwords from command line
    Group(GroupArgs),
}

#[derive(Args)]
struct GroupArgs {
    #[clap(required = true)]
    /// Account to check
    account: Vec<Account>,
}

fn main() {
  // Check command line
  let args = AppArgs::parse();
  … 
}

Décortiquons ce qu'on a décrit :

  • Nous avons une application dont le nom, la version, les auteurs et la description proviennent respectivement des champs name, version, authors et description de Cargo.toml lorsqu'ils sont définis.
  • Une sous-commande existe qui a comme nom group. Les arguments de cette sous-commande sont décrits dans la structure GroupArgs.
  • Cette sous-commande a un argument ACCOUNT dont l'aide est "Account to check", dont la présence est obligatoire, et qui peut être répété puisque nous utilisons Vec<Account>.
  • Étant donné que nous avons implémenté le trait FromStr pour Account, le parseur nous générera automatiquement un vecteur du bon type.
  • Une sous-commande doit être obligatoirement précisée, sinon le message d'aide sera produit et le programme s'arrêtera. Nous n'avons qu'une seule sous-commande, il faudra donc systématiquement préciser "group" sur la ligne de commande.

Exploration des messages d'aide

Rien qu'en insérant cela dans main(), nous pouvons vérifier les messages d'aide générés avant même de modifier notre programme pour en tenir compte :

$ cargo run
pwdchk 0.1.0

USAGE:
    pwdchk <SUBCOMMAND>

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

SUBCOMMANDS:
    group     Check duplicate passwords from command line
    help      Print this message or the help of the given subcommand(s)

Une sous-commande help a été insérée automatiquement. Utilisons la pour voir la documentation de notre sous-commande group :

$ cargo run -- help group
pwdchk-group
Check duplicate passwords from command line

USAGE:
    pwdchk group [OPTIONS] <ACCOUNT>...

ARGS:
    <ACCOUNT>...    Account to check

OPTIONS:
    -h, --help      Print help information

On remarquera que ces messages d'aide ont été générés à partir de nos commentaires sur les arguments. Comme on l'a expliqué précédemment, le séparateur -- permet à cargo run de comprendre que les arguments sont destinés à l'application lancée et pas à cargo run lui-même. Ici ce n'est pas obligatoire, mais si on avait passé des arguments comme -h cela aurait été nécessaire :

$ cargo run -h
cargo-run
Run a binary or example of the local package

USAGE:
    cargo run [OPTIONS] [--] [args]...

OPTIONS:
    -q, --quiet                      Do not print cargo log messages
        --bin <NAME>...              Name of the bin target to run
        --example <NAME>...          Name of the example target to run
[…]

$ cargo run -- -h
pwdchk 0.1.0

USAGE:
    pwdchk <SUBCOMMAND>

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

SUBCOMMANDS:
    group     Check duplicate passwords from command line
    help      Print this message or the help of the given subcommand(s)

Adaptation de notre code

En cas d'utilisation correcte de la ligne de commande, la variable args contient une structure AppArgs. En cas d'erreur, le programme termine avec un message d'aide.

Nous pouvons extraire la sous-commande utilisée ainsi que ses arguments.

fn main() {
    let args = AppArgs::parse();
    match args.command {
        Command::Group(args) => {   // args is of type GroupArgs here
           // Grouper les comptes collectés dans args.account, les filtrer, afficher
           // ceux ayant plus d'un login en réutilisant le code écrit précédemment.
           todo!()
        }
    }
  }
  1. En utilisant le modèle ci-dessus comme exemple, modifiez votre application pour qu'elle fasse ce qui est demandé.

Maintenant qu'on dispose d'un traitement de la ligne de commande adéquat, il sera facile d'ajouter de nouvelles fonctionnalités tout en conservant les précédentes. N'hésitez pas à parcourir la documentation de clap pour en découvrir toutes les possibilités.