Comment un programme Rust lit ses métadonnées depuis Cargo.toml
\n\nAvez-vous déjà écrit un utilitaire CLI en Rust en souhaitant qu'à l'exécution avec le flag --version, il affiche la version exacte indiquée dans Cargo.toml plutôt qu'un simple "0.1.0" ? Ou peut-être aviez-vous besoin de journaliser le nom de votre crate et sa description — mais extraire ces chaînes depuis le code était peu pratique et source d'erreurs ?
Cette question est l'une des plus populaires sur Stack Overflow (des milliers de vues !). Et pour cause : quoi de plus simple que de lire son propre fichier de configuration ? Mais Rust a ses subtilités, et surtout, plusieurs solutions élégantes existent.
\n\nDans cet article, nous allons explorer trois approches principales pour récupérer les métadonnées d'un package Cargo directement depuis le code : du simple macro env!() au flexible build.rs. Vous apprendrez à lire correctement la version, le nom du crate, la description, et à éviter les erreurs courantes.
Pourquoi en avez-vous besoin ? Trois scénarios typiques
\n\nAvant de plonger dans le code, comprenons pourquoi accéder aux métadonnées du package est utile. Voici trois cas concrets :
\n\n- \n
- Utilitaires CLI : la commande
monapp --versiondoit afficher la version depuisCargo.toml, sans valeur codée en dur. \n - Journaux et télémétrie : au démarrage de l'application, il est utile d'enregistrer le nom du crate et sa version dans les logs — cela simplifie le débogage. \n
- Génération automatique de documentation : vous pouvez insérer la description du crate dans un message d'aide ou un README généré à la volée. \n
Maintenant que nous savons "pourquoi", passons au "comment".
\n\nMéthode n°1 : Le macro env!() — la plus simple et la plus populaire
\n\nLa voie la plus directe est d'utiliser le macro intégré env!(). Il permet de lire les variables d'environnement que Cargo définit automatiquement lors de la compilation. Voici les plus utiles :
- \n
CARGO_PKG_NAME— le nom du package (ex : "mon_app_super") \n CARGO_PKG_VERSION— la version (ex : "0.2.1") \n CARGO_PKG_DESCRIPTION— la description depuis le champdescriptiondansCargo.toml\n CARGO_PKG_AUTHORS— la liste des auteurs, séparée par des deux-points \n CARGO_PKG_LICENSE— la licence \n CARGO_PKG_REPOSITORY— l'URL du dépôt \n
Exemple d'utilisation :
\n\nfn main() {\n let nom = env!("CARGO_PKG_NAME");\n let version = env!("CARGO_PKG_VERSION");\n\n println!("{} v{}", nom, version);\n // Affiche : mon_app_super v0.2.1\n}\n\n\nImportant : env!() est un macro de compilation. Si la variable d'environnement n'est pas définie, le programme ne compilera même pas ! C'est un avantage (sécurité des types) et un inconvénient (pas de valeur de repli sans astuces supplémentaires).
Si vous n'avez besoin de la version qu'à des fins de débogage, utilisez option_env!(), qui retourne un Option<&str> :
let version = option_env!("CARGO_PKG_VERSION").unwrap_or("inconnue");\nprintln!("Version : {}", version);\n\n\nCette méthode est idéale dans 90 % des cas. Elle ne nécessite aucune dépendance supplémentaire et fonctionne "prête à l'emploi".
\n\nMéthode n°2 : include_str!() + analyse manuelle — pour un contrôle maximal
\n\nParfois, vous ne voulez pas seulement lire un champ, mais obtenir des données structurées depuis Cargo.toml. Par exemple, toutes les dépendances ou les mots-clés. Dans ce cas, lisez le fichier lui-même sous forme de chaîne et analysez-le.
fn main() {\n let cargo_toml = include_str!("../Cargo.toml");\n // Vous pouvez utiliser un analyseur toml, mais pour l'exemple — une recherche simple\n if let Some(ligne) = cargo_toml.lines().find(|l| l.starts_with("version =")) {\n let version = ligne.trim_start_matches("version = \\"").trim_end_matches('"');\n println!("Version depuis le fichier : {}", version);