Publier une crate sur crates.io
Nous avons déjà utilisé des paquets provenant de crates.io comme dépendance de notre projet, mais vous pouvez aussi partager votre code avec d’autres personnes en publiant vos propres paquets. Le registre des crates disponible sur crates.io distribue le code source de vos paquets, donc il héberge principalement du code qui est open source.
Rust et cargo ont des fonctionnalités qui font en sorte que les paquets que vous publiez sont aisés à trouver et utiliser pour les autres développeurs. Nous allons voir certaines de ces fonctionnalités puis nous allons expliquer comment publier un paquet.
Créer des commentaires de documentation utiles
Documenter correctement vos paquets aidera les autres utilisateurs à savoir comment et quand les utiliser, donc ça vaut la peine de consacrer du temps à la rédaction de la documentation. Dans le chapitre 3, nous avons vu comment commenter du code Rust en utilisant deux barres obliques //. Rust a aussi un type particulier de commentaire pour la documentation, aussi connu sous le nom de commentaire de documentation, qui va générer de la documentation en HTML. Le HTML affiche le contenu des commentaires de documentation pour les éléments public de votre API à destination des développeurs qui s’intéressent à la manière d’utiliser votre crate et non pas à la manière dont elle est implémentée.
Les commentaires de documentation utilisent trois barres obliques /// au lieu de deux et prennent en charge la notation Markdown pour mettre en forme le texte. Placez les commentaires de documentation juste avant l’élément qu’ils documentent. L’encart 14-1 montre des commentaires de documentation pour une fonction ajouter_un dans une crate nommée ma_crate.
/// Ajoute 1 au nombre donné.
///
/// # Exemples
///
/// ```
/// let argument = 5;
/// let reponse = ma_crate::ajouter_un(argument);
///
/// assert_eq!(6, reponse);
/// ```
pub fn ajouter_un(x: i32) -> i32 {
x + 1
}
Ici nous avons écrit une description de ce que fait la fonction ajouter_un, débuté une section avec le titre Exemples puis fourni du code qui montre comment utiliser la fonction ajouter_un. Nous pouvons générer la documentation HTML à partir de ces commentaires de documentation en lançant cargo doc. Cette commande lance l’outil rustdoc qui est distribué avec Rust et place la documentation HTML générée dans le répertoire target/doc.
Pour plus de facilité, lancer cargo doc --open va générer le HTML pour la documentation de votre crate courante (ainsi que la documentation pour toutes les dépendances de la crate) et ouvrir le résultat dans un navigateur web. Rendez-vous à la fonction ajouter_one et vous découvrirez comment le texte dans les commentaires de la documentation a été interprété, ce qui devrait ressembler à l’illustration 14-1.
Illustration 14-1 : Documentation HTML pour la fonction ajouter_un
Les sections utilisées fréquemment
Nous avons utilisé le titre en Markdown # Exemples dans l’encart 14-1 afin de créer une section dans le HTML avec le titre “Exemples”. Voici d’autres sections que les auteurs de crate utilisent fréquemment dans leur documentation :
- Panics : il s’agit des scénarios dans lesquels la fonction qui est documentée peut paniquer. Ceux qui utilisent la fonction et qui ne veulent pas que leur programme panique doivent s’assurer qu’ils n’appellent pas la fonction dans ce genre de situation.
- Errors : si la fonction retourne un
Result, documenter les types d’erreurs qui peuvent survenir ainsi que les conditions qui mènent à ces erreurs sera très utile pour ceux qui utilisent votre API afin qu’ils puissent écrire du code pour gérer ces différents types d’erreurs de manière à ce que cela leur convienne. - Safety : si la fonction fait un appel à
unsafe(que nous verrons au chapitre 20), il devrait exister une section qui explique pourquoi la fonction fait appel à unsafe et quels sont les paramètres que la fonction s’attend à recevoir des utilisateurs de l’API.
La plupart des commentaires sur la documentation n’ont pas besoin de ces sections, mais c’est une bonne liste de vérifications à avoir pour vous rappeler les éléments importants à signaler aux utilisateurs de votre code.
Les commentaires de documentation pour faire des tests
L’ajout des blocs de code d’exemple dans vos commentaires de documentation peut vous aider à montrer comment utiliser votre bibliothèque, et ceci apporte un bonus supplémentaire : l’exécution de cargo test va lancer les codes d’exemples présents dans votre documentation comme étant des tests ! Il n’y a rien de mieux que de la documentation avec des exemples. Mais il n’y a rien de pire que des exemples qui ne fonctionnent plus car le code a changé depuis que la documentation a été écrite. Si nous lançons cargo test avec la documentation de la fonction ajouter_un de l’encart 14-1, nous verrons une section dans les résultats de tests comme celle-ci :
Doc-tests ma_crate
running 1 test
test src/lib.rs - ajouter_un (line 5) ... ok
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.27s
Maintenant, si nous changeons la fonction ou l’exemple de telle sorte que le assert_eq! de l’exemple panique et que nous lançons cargo test à nouveau, nous verrons que les tests de documentation vont découvrir que l’exemple et le code sont désynchronisés l’un de l’autre !
Commentaires de l’élément contenant l’élément courant
Le style de commentaire de documentation //! ajoute de la documentation à l’élément qui contient ce commentaire plutôt qu’aux éléments qui suivent ce commentaire. Nous utilisons habituellement ces commentaires de documentation dans le fichier de la crate racine (qui est src/lib.rs par convention) ou à l’intérieur d’un module afin de documenter la crate ou le module dans son ensemble.
Par exemple, pour ajouter de la documentation qui décrit le rôle de la crate ma_crate qui contient la fonction ajouter_un, nous ajoutons des commentaires de documentation qui commencent par //! au début du fichier src/lib.rs, comme dans l’encart 14-2.
//! # Ma crate
//!
//! `ma_crate` est un regroupement d'utilitaires pour rendre plus pratique
//! certains calculs.
/// Ajoute 1 au nombre donné.
// -- partie masquée ici --
///
/// # Exemples
///
/// ```
/// let argument = 5;
/// let reponse = ma_crate::ajouter_un(argument);
///
/// assert_eq!(6, reponse);
/// ```
pub fn ajouter_un(x: i32) -> i32 {
x + 1
}
my_crate crate as a wholeRemarquez qu’il n’y a pas de code après la dernière ligne qui commence par //!. Comme nous commençons les commentaires par //! au lieu de ///, nous documentons l’élément qui contient ce commentaire plutôt que l’élément qui suit ce commentaire. Dans notre cas, cet élément est le fichier src/lib.rs, qui est la racine de la crate. Ces commentaires vont décrire l’intégralité de la crate.
Lorsque nous lançons cargo doc --open, ces commentaires vont s’afficher sur la page d’accueil de la documentation de ma_crate, au-dessus de la liste des éléments publics de la crate, comme montré dans l’illustration 14-2.
Les commentaires de la documentation placés à l’intérieur des éléments sont particulièrement utiles pour décrire les crates et les modules. Utilisez-les pour expliquer globalement le rôle du conteneur pour aider vos utilisateurs à comprendre l’organisation de votre crate.
Illustration 14-2 : Documentation générée pour ma_crate, qui contient le commentaire qui décrit l’intégralité de la crate
Exporter une API publique conviviale
La structure de votre API publique est une question importante lorsque vous publiez une crate. Les personnes qui utilisent votre crate sont moins familiers avec la structure que vous l’êtes et pourraient avoir des difficultés à trouver les éléments qu’ils souhaitent utiliser si votre crate a une hiérarchie de module imposante.
Dans le chapitre 7, nous avons vu comment rendre publics des éléments en utilisant le mot-clé pub, et comment importer des éléments dans la portée en utilisant le mot-clé use. Cependant, la structure qui a un sens pour vous pendant que vous développez une crate peut ne pas être pratique pour vos utilisateurs. Vous pourriez vouloir organiser vos structures dans une hiérarchie qui a plusieurs niveaux, mais les personnes qui veulent utiliser un type que vous avez défini dans un niveau profond de la hiérarchie pourraient rencontrer des difficultés pour savoir que ce type existe. Ils peuvent aussi être agacés d’avoir à écrire use ma_crate::un_module::un_autre_module::TypeUtile; plutôt que use ma_crate::TypeUtile;.
La bonne nouvelle est que si la structure n’est pas pratique pour ceux qui l’utilisent dans une autre bibliothèque, vous n’avez pas à réorganiser votre organisation interne : à la place, vous pouvez ré-exporter les éléments pour créer une structure publique qui est différente de votre structure privée en utilisant pub use. Ré-exporter prend un élément public d’un endroit et le rend public dans un autre endroit, comme s’il était défini dans l’autre endroit.
Par exemple, disons que nous avons créé une bibliothèque art pour modéliser des concepts artistiques. À l’intérieur de cette bibliothèque nous avons deux modules : un module types qui contient deux énumérations CouleurPrimaire et CouleurSecondaire, et un module utilitaires qui contient une fonction mixer, comme dans l’encart 14-3.
//! # Art
//!
//! Une bibliothèque pour modéliser des concepts artistiques.
pub mod types {
/// Les couleurs primaires du modèle RJB.
pub enum CouleurPrimaire {
Rouge,
Jaune,
Bleu,
}
/// Les couleurs secondaires du modèle RJB.
pub enum CouleurSecondaire {
Orange,
Vert,
Violet,
}
}
pub mod utilitaires {
use crate::types::*;
/// Combine deux couleurs primaires dans les mêmes quantités pour
/// créer une couleur secondaire.
pub fn mixer(c1: CouleurPrimaire, c2: CouleurPrimaire) -> CouleurSecondaire {
// -- partie masquée ici --
unimplemented!();
}
}
art library with items organized into kinds and utils modulesL’illustration 14-3 montre ce à quoi devrait ressembler la page d’accueil de la documentation de cette crate générée par cargo doc.
Illustration 14-3 : La page d’accueil de la documentation de art qui liste les modules types et utilitaires
Notez que les types CouleurPrimaire et CouleurSecondaire ne sont pas listés sur la page d’accueil, pas plus que la fonction mixer. Nous devons cliquer sur types et utilitaires pour les voir.
Une autre crate qui dépend de cette bibliothèque va avoir besoin d’utiliser l’instruction use pour importer les éléments de art dans sa portée, en suivant la structure du module qui est actuellement définie. L’encart 14-4 montre un exemple d’une crate qui utilise les éléments CouleurPrimaire et mixer de la crate art.
use art::types::CouleurPrimaire;
use art::utilitaires::mixer;
fn main() {
let rouge = CouleurPrimaire::Rouge;
let jaune = CouleurPrimaire::Jaune;
mixer(rouge, jaune);
}
art crate’s items with its internal structure exportedL’auteur du code de l’encart 14-4, qui utilise la crate art, doit comprendre que CouleurPrimaire est dans le module types et que mixer est dans le module utilitaires. La structure du module de la crate art est bien plus pratique pour les développeurs qui travaillent sur la crate art que pour ceux qui l’utilisent. La structure interne ne contient aucune information utile à quelqu’un qui essaye de comprendre comment utiliser la crate art, mais elle est plutôt source de confusion car les développeurs qui l’utilisent doivent découvrir où trouver les éléments et doivent renseigner les noms des modules dans les instructions use.
Pour masquer l’organisation interne de l’API publique, nous pouvons modifier le code de la crate art de l’encart 14-3 pour ajouter l’instruction pub use pour ré-exporter les éléments au niveau supérieur, comme montré dans l’encart 14-5.
//! # Art
//!
//! Une bibliothèque pour modéliser des concepts artistiques.
pub use self::types::CouleurPrimaire;
pub use self::types::CouleurSecondaire;
pub use self::utilitaires::mixer;
pub mod types {
// -- partie masquée ici --
/// Les couleurs primaires du modèle RJB.
pub enum CouleurPrimaire {
Rouge,
Jaune,
Bleu,
}
/// Les couleurs secondaires du modèle RJB.
pub enum CouleurSecondaire {
Orange,
Vert,
Violet,
}
}
pub mod utilitaires {
// -- partie masquée ici --
use crate::types::*;
/// Combine deux couleurs primaires dans les mêmes quantités pour
/// créer une couleur secondaire.
pub fn mixer(c1: CouleurPrimaire, c2: CouleurPrimaire) -> CouleurSecondaire {
CouleurSecondaire::Orange
}
}
pub use statements to re-export itemsLa documentation de l’API que cargo doc a générée pour cette crate va maintenant lister et lier les ré-exportations sur la page d’accueil, comme dans l’illustration 14-4, ce qui rend les types CouleurPrimaire et CouleurSecondaire plus faciles à trouver.
Illustration 14-4 : La page d’accueil de la documentation pour art qui liste les ré-exports
Les utilisateurs de la crate art peuvent toujours voir et utiliser la structure interne de l’encart 14-3 comme ils l’utilisaient dans l’encart 14-4, mais ils peuvent maintenant utiliser la structure plus pratique de l’encart 14-5, comme montré dans l’encart 14-6.
use art::CouleurPrimaire;
use art::mixer;
fn main() {
// -- partie masquée ici --
let rouge = CouleurPrimaire::Rouge;
let jaune = CouleurPrimaire::Jaune;
mixer(rouge, jaune);
}
art crateDans les cas où il y a de nombreux modules imbriqués, ré-exporter les types au niveau le plus haut avec pub use peut faire une différence significative dans l’expérience utilisateur de ceux qui utilisent cette crate. Une autre utilisation courante de pub use est de réexporter les définitions d’une dépendance dans la crate courante afin d’intégrer les définitions de cette crate à l’API publique de votre crate.
Créer une structure d’API publique utile est plus un art qu’une science, et vous pouvez itérer plusieurs fois pour trouver une API qui fonctionne mieux pour vos utilisateurs. Choisir pub use vous donne de la flexibilité pour l’organisation interne de votre crate et découple la structure interne de ce que vous présentez aux utilisateurs. N’hésitez pas à regarder le code source des crates que vous avez installées pour voir si leur structure interne est différente de leur API publique.
Mise en place d’un compte crates.io
Avant de pouvoir publier une crate, vous devez créer un compte sur crates.io et obtenir un jeton d’API. Pour pouvoir faire cela, visitez la page d’accueil de crates.io et connectez-vous avec votre compte GitHub (le compte GitHub est actuellement une obligation, mais crates.io pourra permettre de créer un compte d’une autre manière un jour). Une fois identifié, consultez les réglages de votre compte à l’adresse https://crates.io/me/ et récupérez votre jeton d’API (NdT : API key). Ensuite, lancez la commande cargo login et collez votre clé d’API quand on vous le demande, comme ceci :
$ cargo login
abcdefghijklmnopqrstuvwxyz012345
Cette commande informera cargo de votre jeton d’API et l’enregistrera localement dans ~/.cargo/credentials. Notez que ce jeton est un secret : ne le partagez avec personne d’autre. Si vous le donnez à quelqu’un pour une quelconque raison, vous devriez le révoquer et générer un nouveau jeton sur crates.io.
Ajouter des métadonnées à une nouvelle crate
Imaginons que vous avez une crate que vous souhaitez publier. Avant de la publier, vous aurez besoin d’ajouter quelques métadonnées dans la section [package] du fichier Cargo.toml de votre crate.
Votre crate va avoir besoin d’un nom unique. Tant que vous travaillez en local, vous pouvez nommer un crate comme vous le souhaitez. Cependant, les noms des crates sur crates.io sont accordés selon le principe du premier arrivé, premier servi. Une fois qu’un nom de crate est accordé, personne d’autre ne peut publier une crate avec ce nom. Avant d’essayer de publier une crate, recherchez le nom que vous souhaitez utiliser. Si le nom a déjà été utilisé, vous allez devoir trouver un autre nom et modifier le champ name dans le fichier Cargo.toml sous la section [package] pour utiliser le nouveau nom pour la publication, comme ceci :
Fichier : Cargo.toml
[package]
name = "jeu_du_plus_ou_du_moins"
Même si vous avez choisi un nom unique, lorsque vous lancez cargo publish pour publier la crate à ce stade, vous allez avoir un avertissement suivi par une erreur :
$ cargo publish
Updating crates.io index
warning: manifest has no description, license, license-file, documentation, homepage or repository.
See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for more info.
--snip--
error: failed to publish to registry at https://crates.io
Caused by:
the remote server responded with an error (status 400 Bad Request): missing or empty metadata fields: description, license. Please see https://doc.rust-lang.org/cargo/reference/manifest.html for more information on configuring these fields
Cela entraîne une erreur, car il vous manque quelques informations essentielles : une description et une licence sont nécessaires pour que les gens puissent savoir ce que fait votre crate et sous quelles conditions ils peuvent l’utiliser. Dans Cargo.toml, ajoutez une description qui ne fait qu’une phrase ou deux, car elle va s’afficher à proximité de votre crate dans les résultats de recherche. Pour le champ license, vous devez donner une valeur d’identification de la licence (NdT : license identifier value). La Linux Foundation’s Software Package Data Exchange (SPDX) liste les identifiants que vous pouvez utiliser pour cette valeur. Par exemple, pour stipuler que votre crate est sous la licence MIT, ajoutez l’identifiant MIT :
Fichier : Cargo.toml
[package]
name = "jeu_du_plus_ou_du_moins"
license = "MIT"
Si vous voulez utiliser une licence qui n’apparaît pas dans le SPDX, vous devez placer le texte de cette licence dans un fichier, inclure ce fichier dans votre projet puis utiliser licence-file pour renseigner le nom de ce fichier plutôt que d’utiliser la clé licence.
Les conseils sur le choix de la licence appropriée pour votre projet sortent du cadre de ce livre. De nombreuses personnes dans la communauté Rust appliquent à leurs projets la même licence que Rust qui utilise la licence double MIT OR Apache-2.0. Cette pratique montre que vous pouvez également indiquer plusieurs identificateurs de licence séparés par OR pour avoir plusieurs licences pour votre projet.
Une fois le nom unique, la version, la description et la licence ajoutés, le fichier Cargo.toml de ce projet qui est prêt à être publié devrait ressembler à ceci :
Fichier : Cargo.toml
[package]
name = "jeu_du_plus_ou_du_moins"
version = "0.1.0"
edition = "2024"
description = "Un jeu où vous devez deviner quel nombre l'ordinateur a choisi."
license = "MIT OR Apache-2.0"
[dependencies]
La documentation de cargo décrit d’autres métadonnées que vous pouvez renseigner pour vous assurer que les autres développeurs puissent découvrir et utiliser votre crate plus facilement.
Publier sur crates.io
Maintenant que vous avez créé un compte, sauvegardé votre jeton de clé, choisi un nom pour votre crate, et précisé les métadonnées requises, vous êtes prêt à publier ! Publier une crate téléverse une version précise sur crates.io pour que les autres puissent l’utiliser.
Faites attention, car une publication est permanente. La version ne pourra jamais être remplacée, et le code ne pourra jamais être effacé, sauf dans des cas particuliers. Le but majeur de crates.io est de fournir une archive durable de code afin que les compilations de tous les projets qui dépendent des crates de crates.io puissent toujours continuer à fonctionner. Si la suppression de version était autorisée, cela rendrait ce but impossible. Cependant, il n’y a pas de limites au nombre de versions de votre crate que vous pouvez publier.
Lancez la commande cargo publish à nouveau. Elle devrait fonctionner à présent :
$ cargo publish
Updating crates.io index
Packaging jeu_du_plus_ou_du_moins v0.1.0 (file:///projects/jeu_du_plus_ou_du_moins)
Packaged 6 files, 1.2KiB (895.0B compressed)
Verifying jeu_du_plus_ou_du_moins v0.1.0 (file:///projects/jeu_du_plus_ou_du_moins)
Compiling jeu_du_plus_ou_du_moins v0.1.0
(file:///projects/jeu_du_plus_ou_du_moins/target/package/jeu_du_plus_ou_du_moins-0.1.0)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.19s
Uploading jeu_du_plus_ou_du_moins v0.1.0 (file:///projects/jeu_du_plus_ou_du_moins)
Uploaded jeu_du_plus_ou_du_moins v0.1.0 to registry `crates-io`
note: waiting for `jeu_du_plus_ou_du_moins v0.1.0` to be available at registry
`crates-io`.
You may press ctrl-c to skip waiting; the crate should be available shortly.
Published jeu_du_plus_ou_du_moins v0.1.0 at registry `crates-io`
Félicitations ! Vous venez de partager votre code avec la communauté Rust, et désormais tout le monde peut facilement ajouter votre crate comme une dépendance de son projet.
Publier une nouvelle version d’une crate existante
Lorsque vous avez fait des changements sur votre crate et que vous êtes prêt à publier une nouvelle version, vous devez changer la valeur de version renseignée dans votre fichier Cargo.toml et la publier à nouveau. Utilisez les règles de versionnage sémantique pour choisir quelle sera la prochaine version la plus appropriée en fonction des changements que vous avez faits. Lancez ensuite cargo publish pour téléverser la nouvelle version.
Retirer des versions de crates.io avec cargo yank
Bien que vous ne puissiez pas enlever des versions précédentes d’une crate, vous pouvez prévenir les futurs projets de ne pas l’ajouter comme une nouvelle dépendance. Cela s’avère pratique lorsqu’une version de crate est défectueuse pour une raison ou une autre. Dans de telles circonstances, cargo permet de déprécier une version de crate.
Déprécier (NdT : yanking) une version évite que les nouveaux projets ajoutent une dépendance à cette version tout en permettant à tous les projets existants qui en dépendent de continuer à fonctionner. En gros, une version dépréciée permet à tous les projets avec un Cargo.lock de ne pas échouer, mais tous les futurs fichiers Cargo.lock générés n’utiliseront pas la version dépréciée.
Pour déprécier une version d’une crate, dans le répertoire de la crate que vous avez préalablement publiée, lancez cargo yank et renseignez quelle version vous voulez déprécier. Par exemple, si nous avons publié une crate nommée jeu_du_plus_ou_du_moins version 1.0.1 et nous voulons la déprécier, alors il nous faut entrer la commande suivante dans le répertoire de projet du jeu_du_plus_ou_du_moins :
$ cargo yank --vers 1.0.1
Updating crates.io index
Yank jeu_du_plus_ou_du_moins@1.0.1
Si vous ajoutez --undo à la commande, vous pouvez aussi annuler une dépréciation et permettre à nouveaux aux projets de dépendre de cette version :
$ cargo yank --vers 1.0.1 --undo
Updating crates.io index
Unyank jeu_du_plus_ou_du_moins@1.0.1
Une dépréciation ne supprime pas du code. Elle ne peut pas, par exemple, supprimer des secrets téléversés par mégarde. Si cela arrive, vous devez régénérer immédiatement ces secrets.