Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Publikowanie pakietu na Crates.io

Korzystaliśmy już z pakietów z crates.io jako zależności w naszych projektach, ale możesz również udostępniać swój kod innym osobom, publikując własne pakiety. Rejestr pakietów crates.io dystrybuuje kod źródłowy Twoich pakietów, więc hostuje głównie kod open source.

Rust i Cargo posiadają funkcje, które ułatwiają innym osobom odnalezienie i użycie opublikowanego pakietu. Następnie omówimy niektóre z tych funkcji, a potem wyjaśnimy, jak opublikować pakiet.

Tworzenie użytecznych komentarzy dokumentacji

Dokładne udokumentowanie Twoich pakietów pomoże innym użytkownikom zrozumieć, jak i kiedy ich używać, dlatego warto poświęcić czas na napisanie dokumentacji. W Rozdziale 3 omówiliśmy, jak komentować kod w Rust za pomocą dwóch ukośników, //. Rust ma również szczególny rodzaj komentarza do dokumentacji, znany wygodnie jako komentarz dokumentacji, który wygeneruje dokumentację HTML. HTML wyświetla zawartość komentarzy dokumentacji dla publicznych elementów API przeznaczonych dla programistów zainteresowanych sposobem użycia Twojego pakietu, w przeciwieństwie do sposobu implementacji Twojego pakietu.

Komentarze dokumentacji używają trzech ukośników, ///, zamiast dwóch i obsługują notację Markdown do formatowania tekstu. Umieść komentarze dokumentacji tuż przed elementem, który dokumentują. Listing 14-1 pokazuje komentarze dokumentacji dla funkcji add_one w pakiecie o nazwie my_crate.

/// Dodaje jeden do podanej liczby.
///
/// # Przykłady
///
/// ```
/// let arg = 5;
/// let answer = my_crate::add_one(arg);
///
/// assert_eq!(6, answer);
/// ```
pub fn add_one(x: i32) -> i32 {
    x + 1
}

Tutaj podajemy opis tego, co robi funkcja add_one, rozpoczynamy sekcję z nagłówkiem Przykłady, a następnie podajemy kod, który demonstruje, jak używać funkcji add_one. Możemy wygenerować dokumentację HTML z tego komentarza dokumentacji, uruchamiając cargo doc. To polecenie uruchamia narzędzie rustdoc dystrybuowane z Rustem i umieszcza wygenerowaną dokumentację HTML w katalogu target/doc.

Dla wygody, uruchomienie cargo doc --open zbuduje HTML dla dokumentacji twojego bieżącego pakietu (a także dokumentację dla wszystkich zależności twojego pakietu) i otworzy wynik w przeglądarce internetowej. Przejdź do funkcji add_one i zobaczysz, jak tekst z komentarzy dokumentacji jest renderowany, jak pokazano na Rysunku 14-1.

Wyrenderowana dokumentacja HTML dla funkcji `add_one` pakietu `my_crate`

Rysunek 14-1: Dokumentacja HTML dla funkcji add_one

Często używane sekcje

Użyliśmy nagłówka Markdown # Przykłady w Listing 14-1, aby stworzyć sekcję w HTML z tytułem „Przykłady”. Oto inne sekcje, które autorzy pakietów często używają w swojej dokumentacji:

  • Panics: Są to scenariusze, w których dokumentowana funkcja może wywołać panikę. Osoby wywołujące funkcję, które nie chcą, aby ich programy panikowały, powinny upewnić się, że nie wywołują funkcji w tych sytuacjach.
  • Errors: Jeśli funkcja zwraca Result, opisanie rodzajów błędów, które mogą wystąpić, i warunków, które mogą spowodować zwrócenie tych błędów, może być pomocne dla osób wywołujących, aby mogły napisać kod do obsługi różnych rodzajów błędów na różne sposoby.
  • Safety: Jeśli wywołanie funkcji jest unsafe (niebezpieczne) (omawiamy niebezpieczeństwo w Rozdziale 20), powinna istnieć sekcja wyjaśniająca, dlaczego funkcja jest niebezpieczna i omawiająca niezmienniki, których funkcja oczekuje od osób wywołujących.

Większość komentarzy dokumentacyjnych nie potrzebuje wszystkich tych sekcji, ale jest to dobra lista kontrolna, która przypomina o aspektach Twojego kodu, którymi użytkownicy będą zainteresowani.

Komentarze dokumentacji jako testy

Dodawanie bloków kodu przykładu w komentarzach dokumentacji może pomóc zademonstrować, jak używać Twojej biblioteki i ma dodatkowy bonus: uruchomienie cargo test uruchomi przykłady kodu z Twojej dokumentacji jako testy! Nic nie jest lepsze niż dokumentacja z przykładami. Ale nic nie jest gorsze niż przykłady, które nie działają, ponieważ kod zmienił się od czasu napisania dokumentacji. Jeśli uruchomimy cargo test z dokumentacją dla funkcji add_one z Listing 14-1, zobaczymy sekcję w wynikach testów, która wygląda tak:

   Doc-tests my_crate

running 1 test
test src/lib.rs - add_one (linia 5) ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.27s

Teraz, jeśli zmienimy funkcję lub przykład tak, że assert_eq! w przykładzie wywoła panikę, i ponownie uruchomimy cargo test, zobaczymy, że testy dokumentacji wykryją, że przykład i kod są niezsynchronizowane!

Komentarze do zawartych elementów

Styl komentarza dokumentacji //! dodaje dokumentację do elementu, który zawiera komentarze, a nie do elementów następujących po komentarzach. Zazwyczaj używamy tych komentarzy dokumentacji wewnątrz pliku głównego pakietu (src/lib.rs zgodnie z konwencją) lub wewnątrz modułu, aby udokumentować pakiet lub moduł jako całość.

Na przykład, aby dodać dokumentację opisującą cel pakietu my_crate, który zawiera funkcję add_one, dodajemy komentarze dokumentacji, które zaczynają się od //! na początku pliku src/lib.rs, jak pokazano w Listing 14-2.

//! # Mój Pakiet
//!
//! `my_crate` to zbiór narzędzi ułatwiających wykonywanie pewnych
//! obliczeń.

/// Dodaje jeden do podanej liczby.
// --snip--
///
/// # Przykłady
///
/// ```
/// let arg = 5;
/// let answer = my_crate::add_one(arg);
///
/// assert_eq!(6, answer);
/// ```
pub fn add_one(x: i32) -> i32 {
    x + 1
}

Zauważ, że po ostatniej linii zaczynającej się od //! nie ma żadnego kodu. Ponieważ rozpoczęliśmy komentarze od //! zamiast ///, dokumentujemy element, który zawiera ten komentarz, a nie element, który następuje po tym komentarzu. W tym przypadku elementem jest plik src/lib.rs, który jest korzeniem pakietu. Te komentarze opisują cały pakiet.

Kiedy uruchomimy cargo doc --open, te komentarze zostaną wyświetlone na stronie głównej dokumentacji my_crate powyżej listy publicznych elementów w pakiecie, jak pokazano na Rysunku 14-2.

Komentarze dokumentacyjne wewnątrz elementów są szczególnie przydatne do opisywania pakietów i modułów. Używaj ich do wyjaśniania ogólnego celu kontenera, aby pomóc użytkownikom zrozumieć organizację pakietu.

Wyrenderowana dokumentacja HTML z komentarzem dla całego pakietu `art`

Rysunek 14-2: Wyrenderowana dokumentacja dla my_crate, zawierająca komentarz opisujący cały pakiet

Eksportowanie wygodnego publicznego API

Struktura publicznego API jest kluczową kwestią podczas publikowania pakietu. Osoby korzystające z twojego pakietu są mniej zaznajomione z jego strukturą niż ty i mogą mieć trudności ze znalezieniem elementów, których chcą użyć, jeśli twój pakiet ma dużą hierarchię modułów.

W Rozdziale 7 omówiliśmy, jak uczynić elementy publicznymi za pomocą słowa kluczowego pub oraz jak wprowadzić elementy do zakresu za pomocą słowa kluczowego use. Jednak struktura, która ma sens dla Ciebie podczas tworzenia pakietu, może nie być zbyt wygodna dla Twoich użytkowników. Możesz chcieć zorganizować swoje struktury w hierarchii zawierającej wiele poziomów, ale wtedy osoby, które chcą użyć typu zdefiniowanego głęboko w hierarchii, mogą mieć trudności z odkryciem, że taki typ istnieje. Mogą również być zirytowane koniecznością wpisywania use my_crate::some_module::another_module::UsefulType; zamiast use my_crate::UsefulType;.

Dobrą wiadomością jest to, że jeśli struktura nie jest wygodna do użycia z innej biblioteki, nie musisz zmieniać swojej wewnętrznej organizacji: zamiast tego możesz ponownie eksportować elementy, aby stworzyć publiczną strukturę, która różni się od Twojej prywatnej struktury, używając pub use. Ponowne eksportowanie pobiera publiczny element w jednym miejscu i udostępnia go publicznie w innym miejscu, tak jakby był zdefiniowany w tym innym miejscu.

Na przykład, powiedzmy, że stworzyliśmy bibliotekę o nazwie art do modelowania pojęć artystycznych. W tej bibliotece znajdują się dwa moduły: moduł kinds zawierający dwa wyliczenia o nazwach PrimaryColor i SecondaryColor oraz moduł utils zawierający funkcję o nazwie mix, jak pokazano w Listing 14-3.

//! # Sztuka
//!
//! Biblioteka do modelowania koncepcji artystycznych.

pub mod kinds {
    /// Kolory podstawowe według modelu kolorów RYB.
    pub enum PrimaryColor {
        Red,
        Yellow,
        Blue,
    }

    /// Kolory wtórne według modelu kolorów RYB.
    pub enum SecondaryColor {
        Orange,
        Green,
        Purple,
    }
}

pub mod utils {
    use crate::kinds::*;

    /// Łączy dwa kolory podstawowe w równych proporcjach, aby stworzyć
    /// kolor wtórny.
    pub fn mix(c1: PrimaryColor, c2: PrimaryColor) -> SecondaryColor {
        // --snip--
        unimplemented!();
    }
}

Rysunek 14-3 pokazuje, jak wyglądałaby strona główna dokumentacji dla tego pakietu wygenerowana przez cargo doc.

Wyrenderowana dokumentacja dla pakietu `art`, która zawiera listę modułów `kinds` i `utils`

Rysunek 14-3: Strona główna dokumentacji dla art, która zawiera listę modułów kinds i utils

Zauważ, że typy PrimaryColor i SecondaryColor nie są wymienione na stronie głównej, podobnie jak funkcja mix. Musimy kliknąć kinds i utils, aby je zobaczyć.

Inny pakiet, który zależy od tej biblioteki, potrzebowałby instrukcji use, które wprowadzają elementy z art do zakresu, określając strukturę modułu, która jest aktualnie zdefiniowana. Listing 14-4 pokazuje przykład pakietu, który używa elementów PrimaryColor i mix z pakietu art.

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

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

Autor kodu w Listing 14-4, który używa pakietu art, musiał dowiedzieć się, że PrimaryColor znajduje się w module kinds, a mix w module utils. Struktura modułów pakietu art jest bardziej istotna dla programistów pracujących nad pakietem art niż dla tych, którzy go używają. Wewnętrzna struktura nie zawiera żadnych użytecznych informacji dla kogoś próbującego zrozumieć, jak używać pakietu art, ale raczej powoduje zamieszanie, ponieważ programiści, którzy go używają, muszą dowiedzieć się, gdzie szukać i muszą określać nazwy modułów w instrukcjach use.

Aby usunąć wewnętrzną organizację z publicznego API, możemy zmodyfikować kod pakietu art w Listing 14-3, aby dodać instrukcje pub use do ponownego eksportu elementów na najwyższym poziomie, jak pokazano w Listing 14-5.

//! # Sztuka
//!
//! Biblioteka do modelowania koncepcji artystycznych.

pub use self::kinds::PrimaryColor;
pub use self::kinds::SecondaryColor;
pub use self::utils::mix;

pub mod kinds {
    // --snip--
    /// Kolory podstawowe według modelu kolorów RYB.
    pub enum PrimaryColor {
        Red,
        Yellow,
        Blue,
    }

    /// Kolory wtórne według modelu kolorów RYB.
    pub enum SecondaryColor {
        Orange,
        Green,
        Purple,
    }
}

pub mod utils {
    // --snip--
    use crate::kinds::*;

    /// Łączy dwa kolory podstawowe w równych proporcjach, aby stworzyć
    /// kolor wtórny.
    pub fn mix(c1: PrimaryColor, c2: PrimaryColor) -> SecondaryColor {
        SecondaryColor::Orange
    }
}

Dokumentacja API wygenerowana przez cargo doc dla tego pakietu będzie teraz listować i linkować ponowne eksporty na stronie głównej, jak pokazano na Rysunku 14-4, ułatwiając znalezienie typów PrimaryColor i SecondaryColor oraz funkcji mix.

Wyrenderowana dokumentacja dla pakietu `art` z ponownymi eksportami na stronie głównej

Rysunek 14-4: Strona główna dokumentacji dla art, która zawiera listę ponownych eksportów

Użytkownicy pakietu art nadal mogą przeglądać i używać wewnętrznej struktury z Listing 14-3, jak zademonstrowano w Listing 14-4, lub mogą używać bardziej wygodnej struktury z Listing 14-5, jak pokazano w Listing 14-6.

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

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

W przypadkach, gdy istnieje wiele zagnieżdżonych modułów, ponowne eksportowanie typów na najwyższym poziomie za pomocą pub use może znacznie wpłynąć na doświadczenie osób korzystających z pakietu. Innym częstym zastosowaniem pub use jest ponowne eksportowanie definicji zależności w bieżącym pakiecie, aby definicje tego pakietu stały się częścią publicznego API Twojego pakietu.

Tworzenie użytecznej struktury publicznego API to bardziej sztuka niż nauka, a Ty możesz iterować, aby znaleźć API, które najlepiej pasuje do Twoich użytkowników. Wybór pub use daje Ci elastyczność w wewnętrznej strukturze pakietu i oddziela tę wewnętrzną strukturę od tego, co prezentujesz użytkownikom. Przyjrzyj się kodowi niektórych zainstalowanych pakietów, aby sprawdzić, czy ich wewnętrzna struktura różni się od ich publicznego API.

Konfigurowanie konta Crates.io

Zanim będziesz mógł publikować jakiekolwiek pakiety, musisz utworzyć konto na crates.io i uzyskać token API. Aby to zrobić, odwiedź stronę główną crates.io i zaloguj się za pomocą konta GitHub. (Konto GitHub jest obecnie wymagane, ale witryna może w przyszłości obsługiwać inne sposoby tworzenia konta.) Po zalogowaniu odwiedź ustawienia swojego konta pod adresem https://crates.io/me/ i pobierz swój klucz API. Następnie uruchom polecenie cargo login i wklej swój klucz API po wyświetleniu monitu, w następujący sposób:

$ cargo login
abcdefghijklmnopqrstuvwxyz012345

To polecenie poinformuje Cargo o Twoim tokenie API i zapisze go lokalnie w ~/.cargo/credentials.toml. Zauważ, że ten token jest tajny: nie udostępniaj go nikomu. Jeśli z jakiegoś powodu go udostępnisz, powinieneś go unieważnić i wygenerować nowy token na crates.io.

Dodawanie metadanych do nowego pakietu

Powiedzmy, że masz pakiet, który chcesz opublikować. Przed publikacją musisz dodać kilka metadanych w sekcji [package] pliku Cargo.toml pakietu.

Twój pakiet będzie potrzebował unikalnej nazwy. Podczas pracy nad pakietem lokalnie, możesz nazwać pakiet jak chcesz. Jednak nazwy pakietów na crates.io są przydzielane na zasadzie „kto pierwszy, ten lepszy”. Gdy nazwa pakietu zostanie zajęta, nikt inny nie może opublikować pakietu o tej nazwie. Przed próbą publikacji pakietu, wyszukaj nazwę, której chcesz użyć. Jeśli nazwa została użyta, będziesz musiał znaleźć inną nazwę i edytować pole name w pliku Cargo.toml w sekcji [package], aby użyć nowej nazwy do publikacji, w następujący sposób:

Nazwa pliku: Cargo.toml

[package]
name = "guessing_game"

Nawet jeśli wybrałeś unikalną nazwę, kiedy uruchomisz cargo publish, aby opublikować pakiet w tym momencie, otrzymasz ostrzeżenie, a następnie błąd:

$ cargo publish
    Aktualizowanie indeksu crates.io
warning: manifest nie ma opisu, licencji, pliku licencyjnego, dokumentacji, strony głównej lub repozytorium.
Zobacz https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata aby uzyskać więcej informacji.
--snip--
error: nie udało się opublikować w rejestrze pod adresem https://crates.io

Spowodowane przez:
  zdalny serwer odpowiedział błędem (status 400 Bad Request): brakujące lub puste pola metadanych: description, license. Zobacz https://doc.rust-lang.org/cargo/reference/manifest.html, aby uzyskać więcej informacji na temat konfiguracji tych pól

Prowadzi to do błędu, ponieważ brakuje Ci pewnych kluczowych informacji: opis i licencja są wymagane, aby ludzie wiedzieli, co robi Twój pakiet i na jakich warunkach mogą go używać. W Cargo.toml dodaj opis składający się z jednego lub dwóch zdań, ponieważ będzie on wyświetlany wraz z Twoim pakietem w wynikach wyszukiwania. Dla pola license musisz podać wartość identyfikatora licencji. Software Package Data Exchange (SPDX) Fundacji Linuksa wymienia identyfikatory, których możesz użyć dla tej wartości. Na przykład, aby określić, że licencjonowałeś swój pakiet za pomocą licencji MIT, dodaj identyfikator MIT:

Nazwa pliku: Cargo.toml

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

Jeśli chcesz użyć licencji, która nie pojawia się w SPDX, musisz umieścić tekst tej licencji w pliku, dołączyć ten plik do swojego projektu, a następnie użyć license-file, aby określić nazwę tego pliku zamiast używania klucza license.

Porady dotyczące wyboru odpowiedniej licencji dla Twojego projektu wykraczają poza zakres tej książki. Wiele osób w społeczności Rusta licencjonuje swoje projekty w ten sam sposób co Rust, używając podwójnej licencji MIT OR Apache-2.0. Ta praktyka pokazuje, że możesz również określić wiele identyfikatorów licencji oddzielonych OR, aby mieć wiele licencji dla swojego projektu.

Po dodaniu unikalnej nazwy, wersji, opisu i licencji, plik Cargo.toml dla projektu gotowego do publikacji może wyglądać następująco:

Nazwa pliku: Cargo.toml

[package]
name = "guessing_game"
version = "0.1.0"
edition = "2024"
description = "Ciekawa gra, w której zgadujesz liczbę wybraną przez komputer."
license = "MIT OR Apache-2.0"

[dependencies]

Dokumentacja Cargo opisuje inne metadane, które można określić, aby inni mogli łatwiej odkryć i używać Twojego pakietu.

Publikowanie na Crates.io

Teraz, gdy utworzyłeś konto, zapisałeś swój token API, wybrałeś nazwę dla swojego pakietu i określiłeś wymagane metadane, jesteś gotowy do publikacji! Publikowanie pakietu przesyła konkretną wersję do crates.io, aby inni mogli z niej korzystać.

Bądź ostrożny, ponieważ publikacja jest trwała. Wersja nigdy nie może zostać nadpisana, a kod nie może zostać usunięty, z wyjątkiem pewnych okoliczności. Jednym z głównych celów Crates.io jest pełnienie funkcji stałego archiwum kodu, tak aby kompilacje wszystkich projektów, które zależą od pakietów z crates.io, nadal działały. Dopuszczenie usuwania wersji uniemożliwiłoby osiągnięcie tego celu. Nie ma jednak limitu liczby wersji pakietów, które możesz opublikować.

Uruchom ponownie polecenie cargo publish. Powinno się teraz udać:

$ cargo publish
    Aktualizowanie indeksu crates.io
   Pakowanie guessing_game v0.1.0 (file:///projects/guessing_game)
    Spakowano 6 plików, 1.2KiB (895.0B skompresowanych)
   Weryfikowanie guessing_game v0.1.0 (file:///projects/guessing_game)
   Kompilowanie guessing_game v0.1.0
(file:///projects/guessing_game/target/package/guessing_game-0.1.0)
    Zakończono `dev` profil [unoptimized + debuginfo] cel(e) w 0.19s
   Przesyłanie guessing_game v0.1.0 (file:///projects/guessing_game)
    Przesłano guessing_game v0.1.0 do rejestru `crates-io`
note: oczekiwanie na dostępność `guessing_game v0.1.0` w rejestrze
`crates-io`. Możesz nacisnąć ctrl-c, aby pominąć oczekiwanie; pakiet powinien być dostępny wkrótce.
   Opublikowano guessing_game v0.1.0 w rejestrze `crates-io`

Gratulacje! Udostępniłeś swój kod społeczności Rusta, a każdy może łatwo dodać Twój pakiet jako zależność swojego projektu.

Publikowanie nowej wersji istniejącego pakietu

Kiedy wprowadzisz zmiany w swoim pakiecie i będziesz gotowy do wydania nowej wersji, zmieniasz wartość version określoną w pliku Cargo.toml i publikujesz ponownie. Użyj zasad Semantycznego Wersjonowania, aby zdecydować, jaki jest odpowiedni numer następnej wersji, w oparciu o rodzaj wprowadzonych zmian. Następnie uruchom cargo publish, aby przesłać nową wersję.

Wycofywanie wersji z Crates.io

Chociaż nie możesz usuwać poprzednich wersji pakietu, możesz uniemożliwić przyszłym projektom dodawanie ich jako nowych zależności. Jest to przydatne, gdy wersja pakietu jest zepsuta z jakiegoś powodu. W takich sytuacjach Cargo obsługuje wycofanie wersji pakietu.

Wycofanie wersji zapobiega zależnościom nowych projektów od tej wersji, jednocześnie umożliwiając wszystkim istniejącym projektom, które są od niej zależne, dalsze działanie. Zasadniczo, wycofanie oznacza, że wszystkie projekty z plikiem Cargo.lock nie zostaną zepsute, a żadne przyszłe pliki Cargo.lock nie będą używać wycofanej wersji.

Aby wycofać wersję pakietu, w katalogu pakietu, który wcześniej opublikowałeś, uruchom cargo yank i określ, którą wersję chcesz wycofać. Na przykład, jeśli opublikowaliśmy pakiet o nazwie guessing_game w wersji 1.0.1 i chcemy go wycofać, uruchomimy następujące polecenie w katalogu projektu guessing_game:

$ cargo yank --vers 1.0.1
    Aktualizowanie indeksu crates.io
        Wycofuję guessing_game@1.0.1

Dodając --undo do polecenia, możesz również cofnąć wycofanie i ponownie zezwolić projektom na zależność od danej wersji:

$ cargo yank --vers 1.0.1 --undo
    Aktualizowanie indeksu crates.io
      Przywracam guessing_game@1.0.1

Wycofanie nie usuwa żadnego kodu. Nie może na przykład usunąć przypadkowo przesłanych sekretów. Jeśli tak się stanie, musisz natychmiast zresetować te sekrety.