Chargement depuis un fichier
Nous souhaitons pouvoir charger nos listes de comptes depuis un fichier plutôt que les passer sur la ligne de commande. Cela permettra de traiter un grand nombre de comptes sans être limité par la taille maximale de la ligne de commande.
Au préalable, nous devons réaliser que nous allons avoir deux types d'erreur à propager :
- les erreurs liées aux entrées-sorties, de type
std::io::Error
; - les erreurs liées à l'analyse de la ligne "login:password", de type
account::NoColon
.
Étant donné que les structures Result<T, E>
ne contiennent qu'un seul type d'erreur, de type E
, nous avons deux choix d'implémentation possibles :
- définir un nouveau type d'erreur qui nous est propre et qui peut encapsuler l'une ou l'autre de nos erreurs ;
- implémenter le trait
std::error::Error
suraccount::NoColon
(qui est déjà implémenté pourstd::io::Error
) et utiliser le type dynamiqueBox<dyn std::error::Error>
pour représenter une erreur générique.
Nous allons choisir la première méthode et définir un enum Error
dans un module error
de notre programme qui encapsulera les deux types d'erreurs possibles.
Création d'un type error::Error
- Créez un nouveau module
error
dans le projet. - Définissez une énumération
error::Error
permettant d'encapsuler les deux formes d'erreur
#[derive(Debug)]
pub enum Error {
IoError(std::io::Error),
NoColon,
}
-
Supprimez la définition du type vide
account::NoColon
, et remplacez son utilisation parerror::Error
dans les signatures de fonctions et parerror::Error::NoColon
pour signaler l'occurrence de l'erreur. -
Implémentez le trait
From<std::io::Error>
pourerror::Error
. Cela permettra à l'opérateur?
de convertir grâce à l'appel implicite àinto()
une erreur de typestd::io::Error
enerror::Error
.
Si vous le souhaitez, vous pouvez utiliser le crate thiserror
pour implémenter plus facilement vos erreurs et des conversions automatiques. Cela nécessite d'en lire la documentation.
Chargement depuis un fichier
- Écrivez une méthode
Account::from_file()
permettant de charger un compte par ligne depuis un fichier, avec la signature suivante :
impl Account {
…
fn from_file(filename: &Path) -> Result<Vec<Account>, error::Error> {
todo!()
}
}
On pourra utilement regarder les fonctions Path::new()
, File::open()
, BufReader::new()
qui permet d'encapsuler un descripteur de fichier dans un lecteur intelligent, le trait BufRead
et notamment sa méthode lines()
qui renvoie un itérateur sur les lignes d'un lecteur intelligent, ainsi que la méthode Result::map_err()
qui permet de transformer une erreur en une autre.
On rappelera également que pour convertir une entité t
de type T
en type U
, on pourra utiliser :
t.into()
si le contexte indique qu'un typeU
est requis et queT
implémenteInto<U>
ou queU
implémenteFrom<T>
U::from(t)
siU
implémenteFrom<T>
Dit autrement, il sera possible au besoin de convertir une entité r
contenant un Result<T, std::io::Error>
en Result<T, error:Error>
en appelant x.map_err(|e| error::Error::from(e))
ou de manière plus concise x.map_err(error::Error::from)
.
- Ajoutez une option
--file=FILE
(avec la version courte-f FILE
) à la sous-commandegroup
de votre programme pour charger la liste des comptes depuis le fichierFILE
. Ce fichier sera optionnel:
struct AppArgs {
…
#[clap(short, long)]
/// Load passwords from a file
file: Option<PathBuf>,
}
Pour que notre programme fonctionne sur des systèmes de fichiers dont les noms ne sont pas codés en UTF-8 il faut prendre soin de ne pas mettre la valeur de l'argument dans une String
mais dans un PathBuf
. Ce type manipule des noms et chemins de fichiers qui ne sont pas nécessairement des chaînes UTF-8 valides.
-
Indiquez que les comptes données sur la ligne de commande ne sont plus obligatoires en modifiant l'attribut
#[clap]
sur ce champ. -
Indiquez qu'un des deux paramètres
file
ouaccount
sont obligatoires en ajoutant l'attribut idoine sur la structureAppArgs
:
#[clap(group(
ArgGroup::new("input")
.required(true)
.args(&["account", "file"]),
))]
struct AppArgs {
…
}
- Vérifiez que votre programme se comporte bien quand vous précisez un nom de fichier, qu'il signale bien une erreur quand un mauvais nom est indiqué, qu'il rejette correctement une liste de comptes vide passée sur la ligne de commande.
Vous pouvez télécharger un fichier avec un grand nombre de comptes.