Публикация коробки на crates.io

Beginner

This tutorial is from open-source community. Access the source code

Введение

Добро пожаловать в Publishing a Crate to Crates.io. Эта лабораторная работа является частью Rust Book. Вы можете практиковать свои навыки Rust в LabEx.

В этой лабораторной работе мы обсудим, как опубликовать коробку в crates.io, реестре коробок, который распространяет открытый исходный код, что делает его легче для людей найти и использовать вашу пакет.

Publishing a Crate to Crates.io

Мы использовали пакеты из https://crates.io в качестве зависимостей нашего проекта, но вы также можете поделиться своим кодом с другими людьми, опубликовав свои собственные пакеты. Реестр коробок на https://crates.io распространяет исходный код ваших пакетов, поэтому он в основном хранит код, который является открытым исходным.

Rust и Cargo имеют функции, которые делают ваш опубликованный пакет легче для людей найти и использовать. Мы поговорим о некоторых из этих функций далее и затем объясним, как опубликовать пакет.

Создание полезных комментариев документации

Точная документация ваших пакетов поможет другим пользователям понять, как и когда их использовать, поэтому стоит потратить время на написание документации. В главе 3 мы обсуждали, как комментировать Rust-код с использованием двух слэшей, //. Rust также имеет особый вид комментария для документации, который удобно называется комментарием документации, и который генерирует HTML-документацию. HTML отображает содержимое комментариев документации для публичных API-элементов, предназначенных для программистов, которые интересуются тем, как использовать ваш коробок, а не как он реализован.

Комментарии документации используют три слэша, ///, вместо двух и поддерживают разметку Markdown для форматирования текста. Размещайте комментарии документации сразу перед элементом, который они документируют. Список 14-1 показывает комментарии документации для функции add_one в коробке с именем my_crate.

Имя файла: src/lib.rs

/// Добавляет единицу к заданному числу.
///
/// ## Примеры
///
/// ```
/// let arg = 5;
/// let answer = my_crate::add_one(arg);
///
/// assert_eq!(6, answer);
/// ```rust
pub fn add_one(x: i32) -> i32 {
    x + 1
}

Список 14-1: Комментарий документации для функции

Здесь мы даем описание того, что делает функция add_one, начинаем раздел с заголовком Примеры и затем предоставляем код, демонстрирующий, как использовать функцию add_one. Мы можем сгенерировать HTML-документацию из этого комментария документации, запустив cargo doc. Эта команда запускает инструмент rustdoc, поставляемый вместе с Rust, и помещает сгенерированную HTML-документацию в каталог target/doc.

Для удобства запуск cargo doc --open создаст HTML-документацию для текущего коробка (а также документацию для всех зависимостей вашего коробка) и откроет результат в веб-браузере. Перейдите к функции add_one, и вы увидите, как отображается текст в комментариях документации, как показано на рис. 14-1.

Рисунок 14-1: HTML-документация для функции add_one

Часто используемые разделы

В Списке 14-1 мы использовали заголовок Markdown ## Примеры, чтобы создать раздел в HTML с заголовком "Примеры". Вот некоторые другие разделы, которые авторы коробок обычно используют в своей документации:

  • Переполнения: Сценарии, в которых функция, описываемая в документации, может произойти переполнение. Вызывающим функцию, которые не хотят, чтобы их программы переполнились, следует убедиться, что они не вызывают функцию в этих ситуациях.
  • Ошибки: Если функция возвращает Result, описание типов ошибок, которые могут возникнуть, и условий, которые могут привести к возврату этих ошибок, может помочь вызывающим функцию, чтобы они могли написать код для обработки различных типов ошибок по-разному.
  • Безопасность: Если функция небезопасна для вызова (мы обсудим небезопасность в главе 19), должен быть раздел, в котором объясняется, почему функция небезопасна, и охватываются инварианты, которые функция ожидает от вызывающих функцию.

Большинство комментариев документации не требуют всех этих разделов, но это хороший список для напоминания о аспектах вашего кода, которые пользователи будут заинтересованы знать.

Комментарии документации как тесты

Добавление блоков примерного кода в комментарии документации может помочь продемонстрировать, как использовать вашу библиотеку, и сделать это имеет дополнительный бонус: запуск 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, мы увидим, что тесты документации поймут, что пример и код не синхронизированы друг с другом!

Комментирование вложенных элементов

Комментарий документации //! добавляет документацию к элементу, который содержит комментарии, а не к элементам, следующим за комментариями. Мы обычно используем эти комментарии документации внутри корневого файла коробки (src/lib.rs по соглашению) или внутри модуля для документирования коробки или модуля в целом.

Например, чтобы добавить документацию, которая описывает назначение коробки my_crate, которая содержит функцию add_one, мы добавляем комментарии документации, начинающиеся с //!, в начало файла src/lib.rs, как показано в Списке 14-2.

Имя файла: src/lib.rs

//! ## Моя коробка
//!
//! `my_crate` - это коллекция утилит, которые делают выполнение
//! некоторых вычислений более удобным.

/// Добавляет единицу к заданному числу.
--snip--

Список 14-2: Документация для коробки my_crate в целом

Заметим, что после последней строки, которая начинается с //!, нет кода. Поскольку мы начали комментарии с //! вместо ///, мы документируем элемент, содержащий этот комментарий, а не элемент, следующий за этим комментарием. В этом случае этот элемент - это файл src/lib.rs, который является корнем коробки. Эти комментарии описывают целую коробку.

Когда мы запускаем cargo doc --open, эти комментарии будут отображаться на первой странице документации для my_crate выше списка публичных элементов в коробке, как показано на рис. 14-2.

Рисунок 14-2: Отрендеренная документация для my_crate, включая комментарий, описывающий коробку в целом

Комментарии документации внутри элементов полезны для описания коробок и модулей особенно. Используйте их, чтобы объяснить общее назначение контейнера, чтобы помочь вашим пользователям понять организацию коробки.

Экспорт удобного публичного API с использованием pub use

Структура вашего публичного 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.

Имя файла: src/lib.rs

//! ## 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--
    }
}

Список 14-3: Библиотека art с элементами, организованными в модули kinds и utils

На рис. 14-3 показано, как будет выглядеть первая страница документации для этой коробки, сгенерированная с помощью cargo doc.

Рисунок 14-3: Первая страница документации для art, которая перечисляет модули kinds и utils

Заметим, что типы PrimaryColor и SecondaryColor не перечислены на первой странице, а также функция mix не перечислена. Мы должны нажать на kinds и utils, чтобы увидеть их.

Другая коробка, которая зависит от этой библиотеки, должна использовать инструкции use, чтобы подтянуть элементы из art в область видимости, указывая текущую определенную структуру модулей. Список 14-4 показывает пример коробки, которая использует элементы PrimaryColor и mix из коробки art.

Имя файла: src/main.rs

use art::kinds::PrimaryColor;
use art::utils::mix;

fn main() {
    let red = PrimaryColor::Red;
    let yellow = PrimaryColor::Yellow;
    mix(red, yellow);
}

Список 14-4: Коробка, которая использует элементы коробки art с экспортом ее внутренней структуры

Автор кода в Списке 14-4, который использует коробку art, должен был понять, что PrimaryColor находится в модуле kinds, а mix находится в модуле utils. Структура модулей коробки art более важна для разработчиков, работающих над коробкой art, чем для тех, кто ее использует. Внутренняя структура не содержит полезной информации для тех, кто пытается понять, как использовать коробку art, а вместо этого вызывает путаницу, потому что разработчики, которые ее используют, должны определить, где искать, и должны указывать имена модулей в инструкциях use.

Чтобы удалить внутреннюю организацию из публичного API, мы можем изменить код коробки art в Списке 14-3, добавив инструкции pub use, чтобы переэкспортировать элементы на верхнем уровне, как показано в Списке 14-5.

Имя файла: src/lib.rs

//! ## 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--
}

pub mod utils {
    --snip--
}

Список 14-5: Добавление инструкций pub use для переэкспорта элементов

Документация API, которую cargo doc генерирует для этой коробки, теперь будет перечислять и ссылаться на переэкспорты на первой странице, как показано на рис. 14-4, что делает типы PrimaryColor и SecondaryColor и функцию mix更容易找到。

Рисунок 14-4: Первая страница документации для art, которая перечисляет переэкспорты

Пользователи коробки art по-прежнему могут видеть и использовать внутреннюю структуру из Списка 14-3, как показано в Списке 14-4, или они могут использовать более удобную структуру из Списка 14-5, как показано в Списке 14-6.

Имя файла: src/main.rs

use art::mix;
use art::PrimaryColor;

fn main() {
    --snip--
}

Список 14-6: Программа, которая использует переэкспортированные элементы из коробки art

В случаях, когда есть много вложенных модулей, переэкспорт типов на верхнем уровне с использованием pub use может значительно улучшить опыт пользователей, которые используют коробку. Другое распространенное использование pub use - это переэкспорт определений зависимости в текущей коробке, чтобы определения этой коробки стали частью публичного API вашей коробки.

Создание полезной структуры публичного API - это больше искусство, чем наука, и вы можете поэкспериментировать, чтобы найти API, которое лучше всего подходит для ваших пользователей. Выбор pub use дает вам гибкость в организации внутренней структуры вашей коробки и позволяет отделить внутреннюю структуру от той, которую вы представляете пользователям. Посмотрите на часть кода некоторых установленных коробок, чтобы увидеть, отличается ли их внутренняя структура от их публичного API.

Настройка учетной записи на crates.io

Прежде чем вы сможете опубликовать любые коробки, вам необходимо создать учетную запись на https://crates.io и получить API-токен. Для этого перейдите на домашнюю страницу по адресу https://crates.io и войдите с помощью учетной записи GitHub. (В настоящее время учетная запись GitHub обязательна, но в будущем сайт может поддерживать и другие способы создания учетной записи.) После входа перейдите в настройки своей учетной записи по адресу https://crates.io/me и получите свой API-ключ. Затем выполните команду cargo login с вашим API-ключом, как показано ниже:

cargo login abcdefghijklmnopqrstuvwxyz012345

Эта команда сообщит Cargo о вашем API-токене и сохранит его локально в ~/.cargo/credentials. Обратите внимание, что этот токен является секретом: не делитесь им ни с кем. Если вы по какой-либо причине поделитесь им с кем-то, вы должны его отозвать и сгенерировать новый токен на https://crates.io.

Добавление метаданных в новую коробку

Предположим, у вас есть коробка, которую вы хотите опубликовать. Перед публикацией вам нужно добавить некоторую метадату в раздел [package] файла Cargo.toml вашей коробки.

Ваша коробка должна иметь уникальное имя. Во время локальной работы над коробкой вы можете назвать ее как угодно. Однако имена коробок на https://crates.io распределяются по принципу "первым пришел - первым served". Как только имя коробки занято, никто другой не может опубликовать коробку с таким именем. Перед попыткой опубликовать коробку, поищите имя, которое вы хотите использовать. Если имя уже используется, вам нужно найти другое имя и отредактировать поле name в файле Cargo.toml в разделе [package], чтобы использовать новое имя для публикации, вот так:

Имя файла: 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: missing or empty metadata fields:
description, license. Please see https://doc.rust-
lang.org/cargo/reference/manifest.html for how to upload metadata

Это приводит к ошибке, потому что у вас отсутствуют некоторые важные сведения: требуется описание и лицензия, чтобы люди знали, что делает ваша коробка и по каким условиям они могут ее использовать. В Cargo.toml добавьте описание, состоящее из одного или двух предложений, потому что оно будет отображаться с вашей коробкой в результатах поиска. Для поля license вам нужно указать значение идентификатора лицензии. Программа обмена данными пакетов ПО (SPDX) Linux Foundation на http://spdx.org/licenses перечисляет идентификаторы, которые вы можете использовать для этого значения. Например, чтобы указать, что вы лицензировали свою коробку по лицензии MIT, добавьте идентификатор MIT:

Имя файла: Cargo.toml

[package]
name = "guessing_game"
license = "MIT"

Если вы хотите использовать лицензию, которая не отображается в SPDX, вам нужно поместить текст этой лицензии в файл, включить файл в свой проект, а затем использовать license-file, чтобы указать имя этого файла вместо использования ключа license.

Рекомендации о том, какую лицензию выбрать для вашего проекта, выходят за рамки этой книги. Многие люди в сообществе Rust лицензируют свои проекты так же, как и Rust, используя двойную лицензию MIT OR Apache-2.0. Эта практика показывает, что вы также можете указать несколько идентификаторов лицензий, разделенных OR, чтобы иметь несколько лицензий для вашего проекта.

С уникальным именем, версией, описанием и лицензией, файл Cargo.toml для проекта, готового к публикации, может выглядеть так:

Имя файла: Cargo.toml

[package]
name = "guessing_game"
version = "0.1.0"
edition = "2021"
description = "A fun game where you guess what number the
computer has chosen."
license = "MIT OR Apache-2.0"

[dependencies]

Документация Cargo на https://doc.rust-lang.org/cargo описывает другие метаданные, которые вы можете указать, чтобы обеспечить более простое обнаружение и использование вашей коробки другими.

Публикация на crates.io

Теперь, когда вы создали учетную запись, сохранили свой API-токен, выбрали имя для своей коробки и указали необходимые метаданные, вы готовы опубликовать! Публикация коробки загружает определенную версию на https://crates.io для использования другими.

Обратите внимание, потому что публикация - это вечная. Версия никогда не может быть перезаписана, и код не может быть удален. Одной из основных целей Crates.io является служить постоянным архивом кода, чтобы сборки всех проектов, которые зависят от коробок с https://crates.io, продолжали работать. Разрешение удаления версий сделало бы невозможным достижение этой цели. Однако количество версий коробок, которые вы можете опубликовать, не ограничено.

Запустите команду cargo publish снова. Теперь она должна выполниться успешно:

$ cargo publish
    Updating crates.io index
   Packaging guessing_game v0.1.0 (file:///projects/guessing_game)
   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 [unoptimized + debuginfo] target(s) in 0.19s
   Uploading guessing_game v0.1.0 (file:///projects/guessing_game)

Поздравляем! Теперь вы поделились своим кодом с сообществом Rust, и任何人都可以轻松地将您的板条箱添加为他们项目的依赖项。

Публикация новой версии существующей коробки

Когда вы внесли изменения в свою коробку и готовы выпустить новую версию, вы изменяете значение version, указанное в файле Cargo.toml, и republisheте ее. Используйте правила Semantic Versioning по адресу http://semver.org, чтобы определить, какое подходящее следующее номер версии, исходя из типа внесенных вами изменений. Затем запустите cargo publish, чтобы загрузить новую версию.

Отметка версий на crates.io как устаревших с помощью cargo yank

Хотя вы не можете удалить предыдущие версии коробки, вы можете предотвратить добавление их в качестве новых зависимостей в будущих проектах. Это полезно, когда версия коробки по какой-то причине не работает. В таких случаях Cargo поддерживает помечаение версии коробки как устаревшей.

Помечание версии как устаревшей предотвращает добавление этой версии в зависимости для новых проектов, при этом позволяет всем существующим проектам, которые зависят от нее, продолжать работу. По сути, помечаение означает, что все проекты с Cargo.lock не будут сломаны, и любые будущие сгенерированные файлы Cargo.lock не будут использовать помеченную версию.

Чтобы пометить версию коробки как устаревшую, в директории коробки, которую вы ранее опубликовали, запустите cargo yank и укажите, какую версию вы хотите пометить. Например, если мы опубликовали коробку под названием guessing_game версии 1.0.1 и хотим пометить ее, в директории проекта guessing_game мы выполним:

$ cargo yank --vers 1.0.1
Updating crates.io index
Yank guessing_game@1.0.1

Добавив --undo к команде, вы также можете отменить пометку и снова разрешить проектам зависеть от версии:

$ cargo yank --vers 1.0.1 --undo
Updating crates.io index
Unyank guessing_game@1.0.1

Пометка версии как устаревшей не удаляет никакого кода. Например, она не может удалить случайно загруженные секреты. Если это случилось, вы должны немедленно сбросить эти секреты.

Резюме

Поздравляем! Вы завершили лабораторную работу по публикации коробки на crates.io. Вы можете практиковаться в других лабораторных работах в LabEx, чтобы улучшить свои навыки.