Dans l’article précédent, nous nous étions arrêté à la récupération d’un module au format zip, grâce à la sous-commande de bsc : bsc add –zip [nom_module].

Aujourd’hui, on va voir comment procéder à la décompression de ce fichier zip, afin de récupérer les dossiers et fichiers sources du module. 

Cet article est donc volontairement assez court. Je pense que c’est mieux comme cela, puisque les lecteurs voulant simplement voir comment extraire des fichiers zip en Rust, auront donc toute la procédure dédiée à ce sujet ici, et ne se perdrons pas avec le reste qui les intéresse sans doute moins.

 

 

La décompression d’un module téléchargé au format zip

 

Nous nous étions donc arrêtés sur cette ligne précise, dans l’article précédent :

common::set_content_file(&format!(“{}bsc_modules/tmp/test.zip”, &path), &dst);

 

Pour rappel, l’appel de la fonction set_content_file, dans mon fichier common.rs, a pour but de créer le fichier test.zip, dans le dossier tmp (temporaire) de bsc_modules/.

Elle permet de plus de remplir ce fichier zip d’archive avec les données présentes dans le vecteur dst (il s’agit simplement d’un copié/collé du contenu du fichier zip distant, au final).

Voyons maintenant comment extraire ces données.

L’option la plus simple consiste à utiliser un crate (puisqu’il s’agit d’une opération assez courante, il existe sans aucun doute un crate permettant de décompresser un fichier au format zip).

Après quelques minutes de recherche sur crates.io, il s’avère que le crate “zip” (tout simplement !) possède exactement les fonctionnalités dont on a besoin.

Comme d’habitude lorsqu’on ajoute un nouveau crate, on commence donc par le renseigner dans le fichier Cargo.toml, situé à la racine du projet :

[package]
name = “bsc”
“version = “0.1.0”
authors = [“Victor Gallet <victor.gallet@developing-stuff.com”>]

[dependencies]
indicatif = “0.9.0”
console = “0.6.1”
clap = “2.32.0”
git2 = “0.7.5”
fs_extra = “1.1.0”
curl = “0.4.15”
zip = “0.4”

 

Voyons maintenant comment l’utiliser.

La première chose à faire, est d’ouvrir le fichier zip fraîchement créé (dans bsc_modules/tmp/test.zip), et de stocker son contenu dans une variable « file », grâce à la fonction fs::open de la librairie standard (pour rappel, il faut ajouter la ligne « use std::fs; » en haut du fichier pour pouvoir l’utiliser :

let file = fs::File::open(format!(“{}bsc_modules/tmp/test.zip”, &path)).unwrap();

 

Maintenant, en suivant la documentation du crate zip, on peut alors créer une instance de la structure ZipArchive, dont le constructeur prend en paramètre un fichier (dans notre cas, notre variable file).

Cette structure va nous permettre de récupérer tout le contenu compressé dans le fichier zip (dossiers et fichiers) :

let mut archive = zip::ZipArchive::new(file).unwrap();

 

On va également créer une variable de type String afin de stocker le chemin du dossier parent de cette archive.

let mut folder_path = String::new();

 

Il ne nous reste plus qu’à suivre à la lettre la documentation officielle du crate zip, en itérant pour chaque fichier/dossier trouvé dans l’archive, et en procédant à son extraction vers notre dossier temporaire bsc_modules/tmp/ :

// Pour chaque dossier/fichier trouvé dans l’archive
for i in 0..archive.len(){
  // on récupère le fichier/dossier
  let mut file = archive.by_index(i).unwrap();
  if i == 0 {
    // Dans le cas où il s’agit du premier dossier, alors on stock son nom dans la variable “folder_path”.
    // Toutes les fonctions appelées à la suite (“sanitized_name, into_os_string…) permettent simplement de récupérer le nom du dossier seul (sans le chemin complet) au format string
    folder_path = file.sanitized_name().into_os_string().into_string().unwrap();
  }
  // On créé un buffer vers le chemin final du dossier après extraction
  let mut outpath = PathBuf::from(format!”{}{}”, &path, “bsc_modules/tmp/”);
  outpath.push(file.sanitized_name());
  {
    // On affiche les commentaires liés au dossier/fichier en cours d’extraction,
    // s’il y en a de renseignés dans l’archive (ceci est complètement optionnel)
    let comment = file.comment();
    if !comment.is_empty(){
      printfln!(“File {} comment: {}”, i, comment);
    }
  }
  
  // Test s’il s’agit d’un dossier.
  // Si oui, alors on créé simplement un dossier du même nom dans le dossier temporaire parent bsc_modules/tmp/
  if(&*file.name()).ends_with(‘/’){
    fs::create_dir_all(&outpath).unwrap();
  }else{
    // Sinon, s’il s’agit d’un fichier, on l’extrait dans son dossier parent s’il existe.
    // Si jamais il n’existe pas (juste au cas où), alors on récupère le dossier parent et on le créé dans bsc_modules/tmp/
    if let Some(p) = outpath.paren(){
      if !p.exists(){
        fs::create_dir_all(&p).unwrap();
      }
    }
    let mut outfile = fs::File::create(&outpath).unwrap();
    io::copy(&mut file, &mut outfile).unwrap();
  }
}

 

Maintenant que le dossier zip est correctement extrait dans notre dossier temporaire bsc_modules/tmp/, on peut alors supprimer le fichier zip téléchargé plus tôt, grâce à la fonction « delete_file » créée précédemment dans mon fichier « common.rs » :

Pour rappel, delete_file consiste simplement à appeler la fonction fs::remove_file de la librairie standard, et à gérer les éventuelles erreurs.

common::delete_files(&format!(“{}bsc_modules/tmp/test.zip”, &path));

 

On peut également déplacer le dossier du module extrait dans bsc_modules/tmp/, vers le dossier parent des modules bsc (bsc_modules), en appelant la fonction move_folder_content_to_parent_folder présente également dans le fichier common.rs.

Pour information, cette fonction permet, comme son nom l’indique, de déplacer un dossier (dont le chemin est donné en paramètre) vers le dossier parent, et ressemble tout simplement à ceci :

pub fn move_folder_content_to_parent_folder(folder_path: &str){
    let dir = path::Path::new(&folder_path);
    if dir.is_dir(){
        for entry in fs::read_dir(dir).unwrap(){
            let entry = entry.unwrap();
            let path = entry.path;
            let mut parent_path = path.parent().unwrap();
            if path.is_dir(){
                // S’il s’agit d’un dossier, alors on le déplace vers le dossier parent de ce dossier
                move_folder(&path.to_str().unwrap(), &parent_path.parent.unwrap().to_str().unwrap());
            }else{
                // Pareil s’il s’agit d’un fichier, mais on appelle la fonction move_file, définie également dans common.rs (qui est au final un appel de la fonction move_items du crate fs_extra, car la librairie standard ne permet pas de directement gérer le déplacement de dossier/fichier. 
                move_file(&path.to_str().unwrap(), &parent_path.parent().unwrap().to_str().unwrap());
            }
        }
    }
}

 

Vous aurez remarqué, via les commentaires ci-dessus, que j’utilise le crate fs_extra pour déplacer les fichiers et les dossiers.

En effet, il n’y a pas de fonctions disponibles pour cela dans la librairie standard. Il faut donc itérer pour chaque fichiers/dossiers, les copier vers la destination, puis les supprimer de la source.

Cette étape est un peu longue, et j’ai préféré utiliser le crate fs_extra qui le fait à ma place – grâce à ses fonctions move_dir, et move_items – par simplicité (ou par flemme dira-t-on 🙂 ).

Bref, dans tous les cas, revenons à nos moutons, voilà simplement l’appel à la fonction décrite ci-dessus, pour terminer l’opération d’extraction du module :

common::move_folder_content_to_parent_folder(&format!(“{}bsc_modules/tmp/{}”, &path, folder_path));

 

Au final, si notre module s’appelait test_bsc0.1.1 par exemple, on a donc téléchargé une archive au format zip nommée test_bsc0.1.1.zip qu’on a extrait dans bsc_modules/tmp/test_bsc0.1.1

Puis, on l’a déplacé dans bsc_modules/test_bsc0.1.1.

 

 

Pour conclure :

 

Toute la partie de récupération des modules, quelque soit l’option choisie (via git, via la récupération d’un module local, ou, comme montré dans cet article, via la récupération d’un module distant au format zip), est dorénavant terminée !

Il ne nous reste plus qu’à vérifier l’intégrité du module téléchargé, et à ajouter ses fichiers sources dans la configuration de nos fichiers CMake (et effectuer un petit twist au niveau des fichiers headers, pour faciliter leur inclusion dans le projet), et nous aurons enfin terminé la partie dédiée à l’ajout d’un module !

J’espère que cette série continue de vous intéresser.

Pour finir, comme d’habitude, si jamais vous avez la moindre remarque ou question, que ce soit sur le fond, comme sur la forme de cet article (ou des précédents), n’hésitez surtout pas à m’en faire part via les commentaires ci-dessous !

Victor Gallet

Victor Gallet

Étudiant programmeur jeu vidéo. J'aime par dessus tout apprendre, et je suis un éternel curieux de tout. Mon principal but dans la vie est d’être une meilleure personne, et de partager mes (faibles) connaissances avec les autres.

Leave a Reply

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.