Créer une application en ligne de commande avec Rust
Tags: french, rust, learning
Published: 2024-06-06 17:52:04


## Introduction

Dans cet article, nous allons construire ensemble une application en ligne de commande toute simple avec Rust. Le but du programme sera très basique. L'utilisateur nous donnera un nom de dossier et nous créerons un nouveau dossier avec ce nom. Plutôt facile non? Allons-y!


## Setup

Si vous n'avez pas encore installé Rust, vous pouvez consulter la <a href="https://www.rust-lang.org/fr/learn/get-started" target="_blank">documentation officielle</a>, il ya beaucoup d'infos utile!

Commençons par créer notre nouveau projet Rust. Pour ça, on peut lancer la commande suivante:

`cargo new new_dir_cmd`

Ceci va créer un nouveau dossier appelé *new_dir_cmd*. Bien sûr, vous pouvez remplacer ce nom par ce que vous voulez.

Si on jette un oeil à l'intérieur de notre nouveau dossier, on peut voir ceci:

![Nouveau dossier Rust avec son contenu](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/va89i2c40tval2lys466.png)

Si on lance direct `cargo run` à l'intérieur de ce dossier, on va se retrouver avec notre bon vieux "Hello World!".

![Cargo run Hello World](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vydpdsyea5bnp7iksq5t.png)

Génial, on est prêt à coder!

## Créer un nouveau dossier

Première étape, on va créer un nouveau dossier. On améliorera notre programme petit à petit.

Mettons le code suivant dans notre *main.rs*:

```rust
use std::fs;

fn main() -> std::io::Result<()> {
    fs::create_dir_all("/Users/damiencosset/Desktop/rust/new_dir_cmd/awesome_new_dir")?;
    Ok(())
}
```
On utilise le module *fs* qui vient du crate *std*. Crates ( caisses en Français ), dans le contexte de Rust, sont similaires aux packages dans d'autres languages. Si vous voulez plus d'informations sur les crates, vous pouvez consulter la <a href="https://doc.rust-lang.org/book/ch07-01-packages-and-crates.html" target="_blank">documentation</a>

On utilise la fonction *create_dir_all* exportée par le module. Elle prend un chemin (path) comme argument. Pour l'instant, je lui donne un nom de chemin en dur.

Enfin, cette fonction retourne un type *std::io::Result*. Donc, notre fonction main doit retourner ce type. On ajoute donc *-> std::io::Result<()>*. Après avoir créer un nouveau dossier, on renvoie simplement le *Ok* variant qui indique que tout est ok et qu'aucune erreur n'a été rencontré.

*Note*: Le caractère *?* est utilisé dans Rust pour la propagation d'erreur. Si nous rencontrons une erreur pendant la création de notre dossier, nous retournerons un *Err(error)*, *Err* étant le deuxième variant que peut prendre *Result* après *Ok*.

Lançons ce code et voyons ce que ça donne:


![Créer un nouveau dossier en dur](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/todd821csd94tadrx8ur.png)

Ca marche! On vient de créer notre premier nouveau dossier avec Rust!

## Ajouter des arguments à notre ligne de commande

Eviddemment, ce programme n'est pas très utile. Le but est que l'utilisateur puisse donner un nom de dossier via la ligne de commande. Mettons cela en place.

### Ajout du crate clap

Pour lire des arguments de notre ligne de commande, on va utiliser le crate *clap*. Pour consulter les crates qui ont été ajoutés à notre projet, on peut ouvrir le fichier *Cargo.toml* et voir ce qui se trouve après:

```
[dependencies]
```
Tout les crates qui auront été rajoutés se trouveront ici.

Ajoutons *clap* avec la commande suivante:

`cargo add clap --features derive`

Le flag *features* nous permet d'utiliser des fonctionnalités optionnel du crate. Ici, on veut utilise la feature appelé *derive*. Pour avoir plus d'informations sur ce crate, vous pouvez consulter sa <a href="https://docs.rs/clap/latest/clap/" target="_blank">documentation</a>. Et si nous jettons un oeil à notre *Cargo.toml*, nous avons à présent une nouvelle ligne:

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

### Définir nos arguments

Remplaçons notre code existant avec ceci:

```rust
use clap::Parser;
//use std::fs;

#[derive(Parser)]
struct Cli {
    directory_name: String,
}

fn main() -> std::io::Result<()> {
    let args = Cli::parse();
    println!("{}", args.directory_name);
    // fs::create_dir_all("/Users/damiencosset/Desktop/rust/new_dir_cmd/awesome_new_dir")?;
    Ok(())
}
```

J'ai mis en commentaire la création de dossier pour le moment
`#[derive(Parser)]` indique que nous utilisons une macro personnalisée qui implémente le *trait* *Parser*. Plus simplement, un *trait* définit un ensemble de méthodes (ou fonctions). Ici, nous implémentons un ensemble de méthodes (ou fonctions) définit dans *Parser* dans notre *Cli* structure. Donc, notre structure *Cli* est capable d'appeler les méthodes implémentées, comme *parse()*.

Avec ce code, et le pouvoir de *clap*, nous pouvons lire les arguments de notre ligne de commande. Lançons à nouveau notre application et donnons lui un argument:

![Lancer l'application avec un argument dans notre ligne de commande](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5cce3arpy814egjhpd1h.png)

Parfait! Nous avons réussi à afficher notre argument. On peut à présent lire notre nom de dossier depuis la ligne de commande.

### Récupérer le dossier actuel

Maintenant, nous avons un léger soucis. On ne va pas donner le chemin entier du dossier dans notre ligne de commande. On ne va donner que le nom du dossier que l'on souhaite créer.Donc, notre programme a besoin de savoir ou créer notre dossier. Pour garder les choses simples, nous allons prendre le dossier dans lequel nous sommes actuellement, puis y ajouter le nom du dossier que l'on vient de récupérer de la ligne de commande.

On a besoin de: 
* récupérer le chemin du dossier actuel
* ajouter le nom du dossier à ce chemin

Pour récupérer le chemin du dossoer actuel, on peut ajouter ces 2 lignes de code:

```rust
// Import 
use std::env;

// Inside the main function
let mut path = env::current_dir()?;
```

Notez que l'on utilise le mot clé *mut*. Cela signifie que la variable que nous venons de définir est mutable. Les variables de Rust sont par défaut immuable. En ajoutant le mot clé *mut*, on peut modifier notre variable pour y ajouter le nom du dossier que l'on veut créer.

En combinant tout cela, notre programme complet ressemble à ça:

```rust
use clap::Parser;
use std::env;
use std::fs;

#[derive(Parser)]
struct Cli {
    directory_name: String,
}

fn main() -> std::io::Result<()> {
    let args = Cli::parse();
    let mut path = env::current_dir()?;
    path.push(args.directory_name);
    fs::create_dir_all(path)?;
    Ok(())
}
```

En utilisant *push()*, on peut ajouter notre nom de dossier au chemin du dossir actuel. Voyons si tout fonctionne!


![Création de deux nouveaux dossiers avec notre application en ligne de commande](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bri6eda3pfy07b2bgtai.png)

Wooo! Ca marche!

Et just comme ça, nous avons créé un programme très simple avec Rust qui nous permet de créer des nouveaux dossiers.

En espérant que vous avez trouvé cela intéressant!

Have fun ❤