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 aident les développeurs à trouver et utiliser les paquets que vous publiez. 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 prend 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
.
Fichier : src/lib.rs
/// 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 dossier 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 :
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 : les 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 19), 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.
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 faire 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 !
Commenter l'élément qui contient l'élément courant
Un autre style de commentaire de documentation, //!
, ajoute de la
documentation à l'élément qui contient ce commentaire plutôt que d'ajouter la
documentation à l'élément qui suit 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, si nous souhaitons ajouter de la documentation qui décrit le rôle
de la crate ma_crate
qui contient la fonction ajouter_un
, nous pouvons
ajouter des commentaires de documentation qui commencent par //!
au début du
fichier src/lib.rs, comme dans l'encart 14-2 :
Fichier : src/lib.rs
//! # 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
}
Remarquez 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, l'élément qui contient ce commentaire 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.
Exporter une API publique conviviale avec pub use
Dans le chapitre 7, nous avons vu comment organiser notre code en modules en
utilisant le mot-clé mod
, comment faire pour rendre des éléments publics 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 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.
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. A 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 :
Fichier : src/lib.rs
//! # 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!();
}
}
L'illustration 14-3 montre la page d'accueil de la documentation de cette crate
générée par cargo doc
qui devrait ressembler à cela :
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
:
Fichier : src/main.rs
use art::types::CouleurPrimaire;
use art::utilitaires::mixer;
fn main() {
let rouge = CouleurPrimaire::Rouge;
let jaune = CouleurPrimaire::Jaune;
mixer(rouge, jaune);
}
L'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 les
développeurs qui utilisent la crate art
. La structure interne qui divise les
éléments de la crate dans le module types
et le module utilitaires
ne
contient aucune information utile à quelqu'un qui essaye de comprendre comment
utiliser la crate art
. Au lieu de cela, la structure du module de la crate
art
génère de la confusion car les développeurs doivent découvrir où trouver
les éléments, et la structure n'est pas pratique car les développeurs 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 :
Fichier : src/lib.rs
//! # 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
}
}
La 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.
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 :
Fichier : src/main.rs
use art::mixer;
use art::CouleurPrimaire;
fn main() {
// -- partie masquée ici --
let rouge = CouleurPrimaire::Rouge;
let jaune = CouleurPrimaire::Jaune;
mixer(rouge, jaune);
}
Dans 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.
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
avec
votre clé d'API, 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
Maintenant que vous avez un compte, imaginons que vous avez une crate que vous
souhaitez publier. Avant de la publier, vous aurez besoin d'ajouter quelques
métadonnées à votre crate en les ajoutant à 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 sur le site le nom que vous souhaitez utiliser.
Si le nom a été utilisé par une autre crate, 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.
-- partie masquée ici --
error: failed to publish to registry at https://crates.io
Caused by:
the remote server responded with an error: missing or empty metadata fields: description, license. Please see https://doc.rust-lang.org/cargo/reference/manifest.html for how to upload metadata
La raison est qu'il 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. Pour corriger cette erreur, vous devez rajouter ces informations dans le fichier 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.
La Linux Foundation’s Software Package Data Exchange (SPDX) liste les
identifications 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 = "2021"
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 lorsque vous publiez une crate car une publication est permanente. La version ne pourra jamais être remplacée, et le code ne pourra jamais être effacé. 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)
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 [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)
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 une version évite que les nouveaux projets ajoutent une dépendance à cette version tout en permettant à tous les projets existants de continuer à en dépendre en leur permettant toujours de télécharger et dépendre de cette version. 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, lancez cargo yank
et renseignez quelle
version vous voulez déprécier :
$ cargo yank --vers 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
Une dépréciation ne supprime pas du code. Par exemple, la fonctionnalité de dépréciation n'est pas conçue pour supprimer des secrets téléversés par mégarde. Si cela arrive, vous devez régénérer immédiatement ces secrets.