Публікація крейту на Crates.io
Ми використовували пакети з crates.io як залежності нашого проєкту, але ви також можете ділитися своїм кодом з іншими людьми шляхом публікації власних пакетів. Реєстр крейтів на crates.io поширює вихідний код ваших пакетів, тому він передусім розміщує код, який є відкритим.
Rust і Cargo мають можливості, які роблять ваш опублікований пакет простішим для людей у пошуку та використанні. Далі ми поговоримо про деякі з цих можливостей, а потім пояснимо, як опублікувати пакет.
Створення корисних коментарів документації
Точне документування ваших пакетів допоможе іншим користувачам знати, коли і як їх
використовувати, тож варто витратити час на написання документації. У Розділі
3 ми обговорювали, як коментувати код Rust за допомогою двох скісних рисок, //. Rust також має
особливий вид коментарів для документації, відомий, цілком доречно, як
коментар документації, який згенерує HTML-документацію. HTML
відображає вміст коментарів документації для публічних елементів API, призначених
для програмістів, зацікавлених у тому, щоб знати, як використовувати ваш крейт, на відміну від того,
як ваш крейт реалізовано.
Коментарі документації використовують три скісні риски, ///, замість двох і підтримують
синтаксис Markdown для форматування тексту. Розміщуйте коментарі документації безпосередньо
перед елементом, який вони документують. Перелік 14-1 показує коментарі документації
для функції add_one у крейті з назвою my_crate.
/// Adds one to the number given.
///
/// # Examples
///
/// ```
/// let arg = 5;
/// let answer = my_crate::add_one(arg);
///
/// assert_eq!(6, answer);
/// ```
pub fn add_one(x: i32) -> i32 {
x + 1
}
Тут ми наводимо опис того, що робить функція add_one, починаємо
розділ із заголовком Examples, а потім надаємо код, який демонструє,
як використовувати функцію add_one. Ми можемо згенерувати HTML-документацію з
цього коментаря документації, запустивши cargo doc. Ця команда запускає інструмент
rustdoc, що поширюється разом із Rust, і розміщує згенеровану HTML-документацію
у каталозі target/doc.
Для зручності запуск cargo doc --open збудує HTML для документації
вашого поточного крейту (а також документацію для всіх залежностей вашого
крейту) і відкриє результат у веббраузері. Перейдіть до функції
add_one, і ви побачите, як текст у коментарях документації
відтворюється, як показано на Рисунку 14-1.
Рисунок 14-1: HTML-документація для функції add_one
Часто використовувані розділи
Ми використали заголовок Markdown # Examples у Переліку 14-1, щоб створити розділ
в HTML із заголовком “Examples.” Ось деякі інші розділи, які автори крейтів
зазвичай використовують у своїй документації:
- Panics: Це сценарії, за яких функція, що документується, може викликати паніку. Виклики функції, які не хочуть, щоб їхні програми викликали паніку, мають переконатися, що вони не викликають функцію в цих ситуаціях.
- Errors: Якщо функція повертає
Result, опис типів помилок, які можуть виникнути, і умов, які можуть спричинити повернення цих помилок, може бути корисним для тих, хто викликає, щоб вони могли писати код для обробки різних типів помилок різними способами. - Safety: Якщо виклик функції є
unsafe(ми обговорюємо unsafety у Розділі 20), має бути розділ, який пояснює, чому функція є unsafe, і охоплює інваріанти, яких функція очікує від викликів дотримуватися.
Більшості коментарів документації не потрібні всі ці розділи, але це добрий чекліст, щоб нагадати вам про аспекти вашого коду, які користувачів зацікавить знати.
Коментарі документації як тести
Додавання блоків прикладів коду у ваші коментарі документації може допомогти продемонструвати,
як використовувати вашу бібліотеку, і має додатковий бонус: запуск cargo test запустить
приклади коду у вашій документації як тести! Немає нічого кращого за
документацію з прикладами. Але немає нічого гіршого за приклади, які не працюють,
бо код змінився відтоді, як документацію було написано. Якщо ми запустимо
cargo test із документацією для функції add_one з Переліку
14-1, ми побачимо розділ у результатах тестів, який виглядає ось так:
Doc-tests my_crate
running 1 test
test src/lib.rs - add_one (line 5) ... ok
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.27s
Тепер, якщо ми змінимо або функцію, або приклад так, що assert_eq!
у прикладі викличе паніку, і знову запустимо cargo test, ми побачимо, що doc-тести
виявляють, що приклад і код більше не синхронізовані між собою!
Коментарі для елементів, що містять інші елементи
Стиль коментаря документації //! додає документацію до елемента, який містить
коментарі, а не до елементів, що йдуть після коментарів. Зазвичай ми
використовуємо ці коментарі документації всередині файла кореня крейту (src/lib.rs за домовленістю)
або всередині модуля, щоб документувати крейт або модуль у цілому.
Наприклад, щоб додати документацію, яка описує призначення крейту my_crate,
що містить функцію add_one, ми додаємо коментарі документації, які
починаються з //!, на початок файла src/lib.rs, як показано в Переліку
14-2.
//! # My Crate
//!
//! `my_crate` is a collection of utilities to make performing certain
//! calculations more convenient.
/// Adds one to the number given.
// --snip--
///
/// # Examples
///
/// ```
/// let arg = 5;
/// let answer = my_crate::add_one(arg);
///
/// assert_eq!(6, answer);
/// ```
pub fn add_one(x: i32) -> i32 {
x + 1
}
Зверніть увагу, що після останнього рядка, який починається з //!, немає жодного коду. Оскільки
ми почали коментарі з //! замість ///, ми документуємо елемент,
який містить цей коментар, а не елемент, що йде після цього коментаря. У
цьому випадку цим елементом є файл src/lib.rs, який є коренем крейту. Ці
коментарі описують увесь крейт.
Коли ми запускаємо cargo doc --open, ці коментарі відображатимуться на головній сторінці
документації для my_crate над списком публічних елементів у
крейтіні, як показано на Рисунку 14-2.
Коментарі документації всередині елементів особливо корисні для опису крейтів і модулів. Використовуйте їх, щоб пояснити загальне призначення контейнера, щоб допомогти вашим користувачам зрозуміти організацію крейту.
Рисунок 14-2: Відтворена документація для my_crate,
включно з коментарем, що описує крейт у цілому
Експорт зручного публічного API
Структура вашого публічного API є важливим чинником під час публікації крейту. Люди, які використовують ваш крейт, менш знайомі зі структурою, ніж ви, і можуть мати труднощі з пошуком частин, які хочуть використовувати, якщо ваш крейт має велику ієрархію модулів.
У Розділі 7 ми розглянули, як робити елементи публічними за допомогою ключового слова pub, і
як вводити елементи в область видимості за допомогою ключового слова use. Однак структура,
яка має сенс для вас під час розробки крейту, може бути не дуже
зручною для ваших користувачів. Ви можете захотіти організувати свої структури в
ієрархію, що містить кілька рівнів, але тоді людям, які хочуть використовувати тип,
визначений вами глибоко в ієрархії, може бути важко дізнатися, що такий тип
існує. Їм також може не подобатися, що потрібно вводити use my_crate::some_module::another_module::UsefulType; замість use my_crate::UsefulType;.
Добра новина полягає в тому, що якщо структура не є зручною для використання іншими
з іншої бібліотеки, вам не потрібно перебудовувати вашу внутрішню організацію:
замість цього ви можете повторно експортувати елементи, щоб створити публічну структуру, яка відрізняється
від вашої приватної структури, використовуючи pub use. Повторний експорт бере публічний
елемент в одному місці і робить його публічним в іншому місці, ніби він був
визначений у цьому іншому місці.
Наприклад, припустімо, ми створили бібліотеку під назвою art для моделювання художніх концепцій.
Усередині цієї бібліотеки є два модулі: модуль kinds, що містить два переліки
з назвами PrimaryColor і SecondaryColor, та модуль utils, що містить
функцію з назвою mix, як показано в Переліку 14-3.
//! # Art
//!
//! A library for modeling artistic concepts.
pub mod kinds {
/// The primary colors according to the RYB color model.
pub enum PrimaryColor {
Red,
Yellow,
Blue,
}
/// The secondary colors according to the RYB color model.
pub enum SecondaryColor {
Orange,
Green,
Purple,
}
}
pub mod utils {
use crate::kinds::*;
/// Combines two primary colors in equal amounts to create
/// a secondary color.
pub fn mix(c1: PrimaryColor, c2: PrimaryColor) -> SecondaryColor {
// --snip--
unimplemented!();
}
}
Рисунок 14-3 показує, як виглядала б головна сторінка документації для цього крейту,
згенерована cargo doc.
Рисунок 14-3: Головна сторінка документації для art,
яка перелічує модулі kinds і utils
Зверніть увагу, що типи PrimaryColor і SecondaryColor не перелічені на
головній сторінці, як і функція mix. Нам потрібно клацнути kinds і utils, щоб
побачити їх.
Іншому крейту, що залежить від цієї бібліотеки, знадобляться оператори use, які
вводять елементи з art в область видимості, вказуючи поточну визначену
структуру модулів. Перелік 14-4 показує приклад крейту, який використовує
елементи PrimaryColor і mix з крейту art.
use art::kinds::PrimaryColor;
use art::utils::mix;
fn main() {
let red = PrimaryColor::Red;
let yellow = PrimaryColor::Yellow;
mix(red, yellow);
}
Автор коду в Переліку 14-4, який використовує крейт art, мав
з’ясувати, що PrimaryColor знаходиться в модулі kinds, а mix — у
модулі utils. Структура модулів крейту art є більш релевантною для
розробників, які працюють над крейтом art, ніж для тих, хто його використовує. Внутрішня
структура не містить жодної корисної інформації для того, хто намагається
зрозуміти, як використовувати крейт art, а радше спричиняє плутанину, тому що
розробники, які його використовують, мають з’ясувати, де шукати, і повинні вказувати
імена модулів в операторах use.
Щоб прибрати внутрішню організацію з публічного API, ми можемо змінити код
крейту art у Переліку 14-3, додавши оператори pub use для повторного експорту
елементів на верхньому рівні, як показано в Переліку 14-5.
//! # Art
//!
//! A library for modeling artistic concepts.
pub use self::kinds::PrimaryColor;
pub use self::kinds::SecondaryColor;
pub use self::utils::mix;
pub mod kinds {
// --snip--
/// The primary colors according to the RYB color model.
pub enum PrimaryColor {
Red,
Yellow,
Blue,
}
/// The secondary colors according to the RYB color model.
pub enum SecondaryColor {
Orange,
Green,
Purple,
}
}
pub mod utils {
// --snip--
use crate::kinds::*;
/// Combines two primary colors in equal amounts to create
/// a secondary color.
pub fn mix(c1: PrimaryColor, c2: PrimaryColor) -> SecondaryColor {
SecondaryColor::Orange
}
}
Документація API, яку cargo doc згенерує для цього крейту, тепер перелічуватиме
і посилатиметься на повторні експорти на головній сторінці, як показано на Рисунку 14-4, роблячи
типи PrimaryColor і SecondaryColor, а також функцію mix простішими для пошуку.
Рисунок 14-4: Головна сторінка документації для art,
яка перелічує повторні експорти
Користувачі крейту art усе ще можуть бачити і використовувати внутрішню структуру з Переліку
14-3, як продемонстровано в Переліку 14-4, або вони можуть використовувати більш зручну
структуру з Переліку 14-5, як показано в Переліку 14-6.
use art::PrimaryColor;
use art::mix;
fn main() {
// --snip--
let red = PrimaryColor::Red;
let yellow = PrimaryColor::Yellow;
mix(red, yellow);
}
У випадках, коли є багато вкладених модулів, повторний експорт типів на верхньому
рівні за допомогою pub use може значно поліпшити досвід людей, які використовують крейт. Інше поширене використання pub use — повторно експортувати
визначення залежності в поточному крейті, щоб зробити ці визначення частиною публічного API вашого крейту.
Створення корисної структури публічного API — це більше мистецтво, ніж наука, і
ви можете ітерувати, щоб знайти API, який найкраще працює для ваших користувачів. Вибір pub use
дає вам гнучкість у тому, як ви структуруєте свій крейт внутрішньо, і відокремлює
цю внутрішню структуру від того, що ви показуєте своїм користувачам. Подивіться на деякий
код крейтів, які ви встановили, щоб побачити, чи відрізняється їхня внутрішня структура
від їхнього публічного API.
Налаштування облікового запису на Crates.io
Перш ніж ви зможете публікувати будь-які крейти, вам потрібно створити обліковий запис на
crates.io і отримати API token. Для цього
відвідайте головну сторінку на crates.io і увійдіть
через обліковий запис GitHub. (Обліковий запис GitHub наразі є обов’язковою вимогою, але
сайт може підтримувати інші способи створення облікового запису в майбутньому.) Після того як
ви ввійдете, перейдіть до налаштувань свого облікового запису на
https://crates.io/me/ і отримайте свій
API key. Потім запустіть команду cargo login і вставте свій API key, коли буде запропоновано, ось так:
$ cargo login
abcdefghijklmnopqrstuvwxyz012345
Ця команда повідомить Cargo про ваш API token і збереже його локально у ~/.cargo/credentials.toml. Зверніть увагу, що цей token є секретом: не діліться ним з кимось іншим. Якщо ви все ж поділитеся ним з кимось з будь-якої причини, ви повинні негайно відкликати його і згенерувати новий token на crates.io.
Додавання метаданих до нового крейту
Припустімо, у вас є крейт, який ви хочете опублікувати. Перед публікацією вам потрібно буде
додати деякі метадані в розділ [package] файла Cargo.toml
крейту.
Ваш крейт потребуватиме унікальної назви. Поки ви працюєте над крейтом локально,
ви можете назвати крейт як завгодно. Однак назви крейтів на
crates.io розподіляються за принципом «хто перший прийшов, того й обслужили». Як тільки назву крейту
зайнято, ніхто інший не може опублікувати крейт із цією назвою. Перед спробою
опублікувати крейт, знайдіть назву, яку хочете використовувати. Якщо назву вже
використано, вам потрібно буде знайти іншу назву і відредагувати поле name у файлі Cargo.toml у
розділі [package], щоб використовувати нову назву для публікації, ось так:
Filename: Cargo.toml
[package]
name = "guessing_game"
Навіть якщо ви вибрали унікальну назву, коли ви зараз запустите cargo publish, щоб опублікувати
крейт, ви отримаєте попередження, а потім помилку:
$ 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
Це завершується помилкою, тому що вам бракує деякої критично важливої інформації: Потрібні
опис і ліцензія, щоб люди знали, що робить ваш крейт і на яких
умовах вони можуть його використовувати. У Cargo.toml додайте опис, який є лише одним або двома реченнями, тому що
він відображатиметься разом із вашим крейтом у результатах пошуку. Для поля license вам потрібно
вказати значення ідентифікатора ліцензії. Software Package Data Exchange (SPDX) Фонду Linux
перелічує ідентифікатори, які ви можете використовувати для цього значення. Наприклад, щоб указати, що
ви ліцензували свій крейт за ліцензією MIT, додайте ідентифікатор MIT:
Filename: Cargo.toml
[package]
name = "guessing_game"
license = "MIT"
Якщо ви хочете використовувати ліцензію, якої немає в SPDX, вам потрібно розмістити
текст цієї ліцензії у файлі, включити цей файл у ваш проєкт, а потім
використати license-file, щоб указати ім’я цього файла замість використання
ключа license.
Роз’яснення того, яка ліцензія підходить для вашого проєкту, виходить за межі
цієї книги. Багато людей у спільноті Rust ліцензують свої проєкти
так само, як Rust, використовуючи подвійну ліцензію MIT OR Apache-2.0. Ця практика
демонструє, що ви також можете вказати кілька ідентифікаторів ліцензій, розділених
OR, щоб мати кілька ліцензій для вашого проєкту.
З унікальною назвою, версією, вашим описом і доданою ліцензією, файл Cargo.toml для проєкту, який готовий до публікації, може виглядати так:
Filename: Cargo.toml
[package]
name = "guessing_game"
version = "0.1.0"
edition = "2024"
description = "A fun game where you guess what number the computer has chosen."
license = "MIT OR Apache-2.0"
[dependencies]
Документація Cargo описує інші метадані, які ви можете вказати, щоб інші могли легше знаходити і використовувати ваш крейт.
Публікація на Crates.io
Тепер, коли ви створили обліковий запис, зберегли свій API token, вибрали назву для вашого крейту і вказали необхідні метадані, ви готові до публікації! Публікація крейту завантажує певну версію на crates.io для використання іншими.
Будьте обережні, тому що публікація є незворотною. Версію ніколи не можна перезаписати, а код не може бути видалений, окрім певних обставин. Одна з головних цілей Crates.io — діяти як постійний архів коду, щоб збірки всіх проєктів, що залежать від крейтів з crates.io, продовжували працювати. Дозвіл видалення версій унеможливив би досягнення цієї мети. Однак кількість версій крейту, які ви можете опублікувати, не обмежена.
Запустіть команду cargo publish ще раз. Тепер вона має виконатися успішно:
$ cargo publish
Updating crates.io index
Packaging guessing_game v0.1.0 (file:///projects/guessing_game)
Packaged 6 files, 1.2KiB (895.0B compressed)
Verifying guessing_game v0.1.0 (file:///projects/guessing_game)
Compiling guessing_game v0.1.0
(file:///projects/guessing_game/target/package/guessing_game-0.1.0)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.19s
Uploading guessing_game v0.1.0 (file:///projects/guessing_game)
Uploaded guessing_game v0.1.0 to registry `crates-io`
note: waiting for `guessing_game 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 guessing_game v0.1.0 at registry `crates-io`
Вітаємо! Тепер ви поділилися своїм кодом зі спільнотою Rust, і будь-хто може легко додати ваш крейт як залежність свого проєкту.
Публікація нової версії наявного крейту
Коли ви внесли зміни до свого крейту і готові випустити нову версію,
ви змінюєте значення version, указанe у вашому файлі Cargo.toml, і
публікуєте повторно. Використовуйте правила семантичного версіонування, щоб вирішити, яким
має бути наступний номер версії, на основі типів змін, які ви внесли.
Потім запустіть cargo publish, щоб завантажити нову версію.
Виведення версій з ужитку на Crates.io
Хоча ви не можете видалити попередні версії крейту, ви можете запобігти тому, щоб будь-які майбутні проєкти додавали їх як нову залежність. Це корисно, коли версія крейту зламана з тієї чи іншої причини. У таких ситуаціях Cargo підтримує yank версії крейту.
Yanking версії запобігає тому, щоб нові проєкти залежали від цієї версії, водночас дозволяючи всім наявним проєктам, які від неї залежать, продовжувати роботу. По суті, yank означає, що всі проєкти з Cargo.lock не зламаються, а будь-які майбутні файли Cargo.lock, які будуть згенеровані, не використовуватимуть yanked версію.
Щоб зробити yank версії крейту, у каталозі крейту, який ви
раніше опублікували, запустіть cargo yank і вкажіть, яку саме версію ви хочете
yankнути. Наприклад, якщо ми опублікували крейт із назвою guessing_game версії
1.0.1 і хочемо зробити її yank, тоді ми запустимо таке в каталозі проєкту
для guessing_game:
$ cargo yank --vers 1.0.1
Updating crates.io index
Yank guessing_game@1.0.1
Додавши --undo до команди, ви також можете скасувати yank і дозволити проєктам
знову почати залежати від цієї версії:
$ cargo yank --vers 1.0.1 --undo
Updating crates.io index
Unyank guessing_game@1.0.1
Yank не видаляє жодного коду. Він не може, наприклад, видалити випадково завантажені секрети. Якщо це сталося, ви повинні негайно скинути ці секрети.