mirror of
https://github.com/darkicewolf50/RustBrock.git
synced 2025-06-15 04:54:17 -06:00
453 lines
18 KiB
Markdown
453 lines
18 KiB
Markdown
# Publishing a Crate to Crates.io
|
||
We have used packages form [crates.io](http://crates.io) as dependencies of our pojects.
|
||
|
||
You could also share your code with others by publishing your own packages.
|
||
|
||
The crate regsitry at [crates.io](http://crates.io) distributes the source code of your packages, it primarily hosts code that is open source.
|
||
|
||
Rust and Cargo have features that make your published package easier for people to find and use.
|
||
|
||
These features will be covered later and then explaining how to publish a package
|
||
|
||
## Making Useful Documentation Comments
|
||
Accurately documenting your packages will others know how and when to use them.
|
||
|
||
It is worth investing the time to write documentation
|
||
|
||
In ch we said that to comment in Rust you use two slashes, `//`
|
||
|
||
Rust also has a pariticualr kind of comment for documentation.
|
||
|
||
This is known conveniently as a *documentation comments*, that will generate HTML documentation.
|
||
|
||
The HTML diesplays the contents of documentation comments for public API items intended for programmers interested in knowing how to use your crate as opposed to how your crate is *implemented*.
|
||
|
||
The documentation comments uses three slashes `///` instead of two and this supports Markdown notation for formatting the text.
|
||
|
||
The placement of documentation comments just before the item they are documenting.
|
||
|
||
Here is an example of documentation comments for an `add_one` function in a crate named `my_crate`.
|
||
```rust
|
||
/// 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
|
||
}
|
||
```
|
||
|
||
Here we first give a description of what the `add_one` function does.
|
||
|
||
Then we add a section starting with the heading `Examples`, and then provide code that demonstrates how to use the `add_one` function.
|
||
|
||
We can generate te HTML documentation from this documentation comment by running `cargo doc`.
|
||
|
||
This command runs the `rsutdoc` tool which comes distributed with Rust and pts the generated HTML documentation in the *target/doc* directory.
|
||
|
||
For convenience running `cargo doc --open` will build the HTML for your current crate's documentation as well as the documentation for all of your crate's dependencies then it will open the result in a web browser.
|
||
|
||
Navigating to the `add_one` function you will see how the texxt in the documentation comments is rendered as shown below
|
||
<img src="https://doc.rust-lang.org/book/img/trpl14-01.png" />
|
||
|
||
### Commonly Used Sections
|
||
We previously used the `# Examples` Markdown heading to create a section in the HTML with the titl "Examples".
|
||
|
||
Here are some other secions that crate author commonly use in the documentation
|
||
- **Panics**: The scenarios in which the function being documented could panic.
|
||
Funtion callers who don't want thier programs to panic should make sure they dont call the function in this way/situation
|
||
- **Errors**: If the function returns a `Result`, that describes the kinds of errors that may occur and what conditions might cause those errors to be returned.
|
||
This can be helpful to caller so that they can write code to handle different kinds of errors in different ways.
|
||
- **Safety**: If the function is inheritly `unsafe` to call, there should be a section explaing why the function is unsafe and covering the invariants that the function expects callers to uphold.
|
||
|
||
Most documentation comments don't need all of these sctions but this is a good checklist ot remind you of the aspects of your code users will be interested in knowing.
|
||
|
||
### Documentation Comments as Tests
|
||
Adding example code blocks in your documentation comments can help demonstrate how to use the library.
|
||
|
||
Doing so also has an addtional bonus: running `cargo test` will run the code examples in your documentation as tests
|
||
|
||
This ensures that your examples will be tested to work, because that would be the worst to ship an example that doesn't work.
|
||
|
||
If we run `cargo test` with the documentation for `add_one` function we would get this output
|
||
```
|
||
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
|
||
```
|
||
|
||
Now if we change either the function or the example so the `assert_eq!` in the example panics, then we will see that the doc tests catch that the example and the code are out of sync with each other.
|
||
|
||
### Commenting Contained Items
|
||
The style of commnet `//!` adds documentation to the item that contains the comments rather than to the items following the comments.
|
||
|
||
We typically use these doc comments inside the crate root file (src/lib.rs by convention) or inside a module to document the crate or the module as a whole.
|
||
|
||
For example, adding documentation that describes the purpose of the `my_crate` that contains the `add_one` function
|
||
|
||
We add documentation comments qith `//!` to the beginning of the *src/lib.rs* file
|
||
|
||
Here is an example of this
|
||
```rust
|
||
//! # 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
|
||
}
|
||
```
|
||
|
||
Notice that there isn't any code after the last line that begins with `//!`
|
||
|
||
Because we started the comments with `//!` instead of `///` , this inidcates we are documenting the item that contains this comment rather than an item that follows his comment.
|
||
|
||
In this case, that item is the *src/lib.rs* file which is the crate root.
|
||
|
||
These comments describe the entire crate
|
||
|
||
When we run `cargo doc --open`, these comments will display on the front page of the documentation for `my_crate` above the list of public items in the crate
|
||
|
||
Here is what the docs would look like
|
||
<img src="https://doc.rust-lang.org/book/img/trpl14-02.png" />
|
||
|
||
Documentation comments within items are useful for describing crates and modules especially.
|
||
|
||
Use them to explain the overall purpose of the container to help your users understand the crate's organization
|
||
|
||
## Exporting a Convenient Public API with `pub use`
|
||
The structure of your public API is a major consideration when publishing a crate.
|
||
|
||
People who use your crate are less familiar with the structure than you.
|
||
|
||
They might have difficulty finding the pieces they want to use if your crate has a large moudle hierarchy
|
||
|
||
Before we covered how to make items public using the `pub` keyword and bring items into a scope iwth the `use` keyword.
|
||
|
||
The structure that makes sense to you while you are developing the crate might not be very useful or convenient for your users.
|
||
|
||
You might want to organize your structs in a heirary containing multiple levels.
|
||
|
||
The people who want to use a type you have defined deep in the hierary might have trouble finding out that tpye exists.
|
||
|
||
They might also be annoyed having to enter `use` `my_crate::some_module::another_module::UsefulType;` rather than `use` `my_crate::UsefulType;`.
|
||
|
||
The good news is that if the struct isn't convenient for others to use from another library, you dont have to rearrange your interal organization.
|
||
|
||
You can instead re-export items to make a public structure that is different form your private structure by using `pub use`.
|
||
|
||
Re-exporting takes a public item in one location and makes it public in another location as if it we defined in the other location instead
|
||
|
||
For example lets say we made a libarary named `art`
|
||
|
||
Within this library there are two modules:
|
||
- a `kinds` module containing two enums named `PrimaryColor` and `SecondaryColor`
|
||
- a `utils` module containinga function named `mix`
|
||
|
||
Here is what it would look like in *src/lib.rs*
|
||
```rust
|
||
//! # 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--
|
||
}
|
||
}
|
||
```
|
||
Here is what the frontpage docs generated by `cargo doc` would look like
|
||
<img src="https://doc.rust-lang.org/book/img/trpl14-03.png" />
|
||
|
||
Notice that the `PirmaryColor` and `SecondaryColor` types are't listed on the front page nor is the `mix` function.
|
||
|
||
We have to navigate to `kinds` and `utils` to see them.
|
||
|
||
Another crate that depends on this library would need `use` statements that bring items from `art` into scope. Specifying the module structure that is currently defined
|
||
|
||
Here is an example of a crate that uses the `PrimaryColor` and `mix` items form the `art` crate
|
||
```rust
|
||
use art::kinds::PrimaryColor;
|
||
use art::utils::mix;
|
||
|
||
fn main() {
|
||
let red = PrimaryColor::Red;
|
||
let yellow = PrimaryColor::Yellow;
|
||
mix(red, yellow);
|
||
}
|
||
```
|
||
The author of the code in above which uses the `art` crate had to figure out that `PrimaryColor` is in the `kinds` module and `mix` is in the `utils` module.
|
||
|
||
The module struct of the `art` crate is more relevant to developers working on the `art` rate than to those using it.
|
||
|
||
The internal structure doesn't contain any useful info for someone trying to understand how to use the `art` crate, but rather causes confusion becasue devs who use it have to figure out where to look and must specify the module names in the `use` statements.
|
||
|
||
To remove the interal organization from the public API, we can modify the `art` crate to add `pub use` statements to re-export the items at the top level
|
||
```rust
|
||
//! # 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--
|
||
}
|
||
```
|
||
The API docs that `cargo doc` generates for this crate will now list and link re-exprots on the front page.
|
||
|
||
This makes the `PrimaryColor` and `SecondaryColor` types and makes finding the `mix` function easier
|
||
|
||
<img src="https://doc.rust-lang.org/book/img/trpl14-04.png" />
|
||
|
||
The `art` crate users can still see the internal structure as demonstrated in the picture above.
|
||
|
||
They could also use the more convenient structure like in the example below
|
||
```rust
|
||
use art::mix;
|
||
use art::PrimaryColor;
|
||
|
||
fn main() {
|
||
// --snip--
|
||
}
|
||
```
|
||
|
||
In cases where there are many nested modules, re-exporting the types at the top level with `pub use`.
|
||
|
||
This can make a very significant difference in the experience of people who use the crate.
|
||
|
||
Another common use of `pub use` is to re-export definitions of a dependency in the current crate to make that crate's definitions part of your crate's public API.
|
||
|
||
Creating a useful public API structure is more of an art than a science.
|
||
|
||
You can iterate to find the API that works best for your users.
|
||
|
||
Choosing `pub use` gives you flexibility in how you structure your crate internally and decouples that interanl struct from what you present to your users.
|
||
|
||
Look at some of the code of crates you have used previously to see if their internal struct differs from their public API.
|
||
|
||
## Setting Up a Crates.io Account
|
||
Before being able to publish creates, you need an account on [crates.io](https://crates.io) and get an API toekn
|
||
|
||
You can do ths by
|
||
1. visit the homepage at [crates.io](https://crates.io)
|
||
2. Log in via a Github account (could use any other type that is supported)
|
||
3. Once logged in, go to your account settings at [https://crates.io/me/](https://crates.io/me/)
|
||
4. Retrieve your API key
|
||
5. Run the `cargo login` command nad paste the API key when prompted
|
||
|
||
Here is an example
|
||
```
|
||
$ cargo login
|
||
abcdefghijklmnopqrstuvwxyz012345
|
||
```
|
||
|
||
This will inform Cargo of your API token and store it locally in *~/.cargo/credentials*
|
||
|
||
Note that this tken is a *secret*. DO NOT SHARE IT
|
||
|
||
You can revoke it and generate a new token on [crate.io](https://crates.io) if you do accidentally share it.
|
||
|
||
## Adding Metadata to a New Crate
|
||
Lets say you gave a crate you want ot publish.
|
||
|
||
Before doing that you need to add some metadata in the `[package] section of the crate's *Cargo.toml* file.
|
||
|
||
You need a unqiue crate name.
|
||
|
||
When working locally you can name a crate anything you like.
|
||
|
||
However crate names on [crates.io](https://crates.io) are allocated on a first-come, first-serverd basis.
|
||
|
||
Once a crate name is taken, no one else can publish a crate with that same name.
|
||
|
||
SUPER GOOD ADVICE: Before attempting to publish a crate search for the name you want to use.
|
||
|
||
If the name has been used, you will need to find another name and edit the `name` field in the *Cargo.toml* file under the `[package]` section to use the new name for publishing.
|
||
|
||
Here is an example of this
|
||
```toml
|
||
[package]
|
||
name = "guessing_game"
|
||
```
|
||
|
||
Even if you chose a unqiue name when you run `cargo publish` to publish the crate at this point you will get a warning then an error.
|
||
|
||
Here is an example of this output
|
||
```
|
||
$ 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 field
|
||
```
|
||
|
||
These errors are caused by missing some crucial information:
|
||
- A description
|
||
- A license
|
||
|
||
Both of these are required so people will know what your crate does and under what terms they can use it.
|
||
|
||
In *Cargo.toml* add a description that that is a sentence or two. This will appear with your crate in search results.
|
||
|
||
For the `license` field you need to give a *license identifier value*
|
||
|
||
The [Linux Foundation’s Software Package Data Exchange (SPDX)](https://spdx.org/licenses/) lists identifers you can use for this value.
|
||
|
||
For example to specify that you are licensing your crate using the MIT License add the `MIT` identifier
|
||
```toml
|
||
[package]
|
||
name = "guessing_game"
|
||
license = "MIT"
|
||
```
|
||
|
||
If you want to use a license that isnt in te SPDX, you will need to plcae the text of that license in a file.
|
||
|
||
Include that file in your porject and then use `license-file` to specify the name of that file instead of using the `license` key.
|
||
|
||
Guidance on which license is appropriate for your project is a highly personal and higly techincal.
|
||
|
||
Please read through the license to pick the most approprate one for your case use with any considerations to dependances.
|
||
|
||
Many people in the Rust community license their projects in the same way as Rust by using a dual license of `MIT OR Apache-2.0`
|
||
|
||
This demonstrates that you can also specify multiple license identifiers separated by `OR` to have multiple licenses for your project.
|
||
|
||
With a unique name, the version, the description and a license added. The *Cargo.toml* file for a project that is ready to publish would look similar to this:
|
||
```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's documentation](https://doc.rust-lang.org/cargo/) describes other metadata you can specify to ensure othes can discover and use your crate more easily.
|
||
|
||
## Publishing to Crates.io
|
||
Now that you have an account, cargo has saved your API otken, chosen a name for your crate and specified the required metadata. Yuo are ready to publish.
|
||
|
||
Publishing a crate uploads a specific version to [crates.io](https://crates.io) for others to use.
|
||
|
||
A Publish is *PERMANENT*.
|
||
|
||
The version can never be overwritten and the code cannot be deleted.
|
||
|
||
One major goal of [crates.io](https://crates.io) is to act as a permanent archive of code so that builds of all projects that depend on crates from [crates.io](https://crates.io) will continue to always work.
|
||
|
||
Allowing version deletions would make this goal impossible.
|
||
|
||
There is no limit to the number of crate versions you can publish.
|
||
|
||
If you run `cargo publish` again it should succeed now.
|
||
Here is the output now
|
||
```
|
||
$ 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` profile [unoptimized + debuginfo] target(s) in 0.19s
|
||
Uploading guessing_game v0.1.0 (file:///projects/guessing_game)
|
||
```
|
||
|
||
This is a successful output and indicates you have successfully shared your code with the Rst community and now anyone can easily add your crate as a dependency of their project.
|
||
|
||
## Publishing a New Version of an Exisiting Crate
|
||
When you made changes to your crate and are ready to release a new version.
|
||
|
||
First you change the `version` value specified in your *Cargo.toml* file and republish.
|
||
|
||
Use [Semantic Versioning rules](http://semver.org/) to decide what an appropriate nexxt version based on the kinds of changes made.
|
||
|
||
Finally run `cargo publish` to upload a new version
|
||
|
||
### Deprecaitng Versions for mCrates.io with `cargo yank`
|
||
Even though you can remove previous versions of a crate, you can prevent any future projects fom adding them as a new dependency.
|
||
|
||
This is useful when a crate version is broken for some reason.
|
||
|
||
In such situations, Cargo supports *yanking* a crate version.
|
||
|
||
Yanking a version prevents new projects form depending on that version while allowing all exisitng projects to continue to depend on that version.
|
||
|
||
This essentially means a yank won't break any exisiting projects with a *Cargo.lock* with that version will not break. But any new *Cargo.lock* files generated will not be able to use the yanked version.
|
||
|
||
To yank a version of a create.
|
||
1. Go to the directory of the crate that has been previously publish.
|
||
2. Run `cargo yank` and specify which version to yank.
|
||
|
||
For example lets say we have published a create named `guessing_game` version 1.0.1 and we want to yank it.
|
||
|
||
In the project directory for the `guessing_game` crate we would run
|
||
```
|
||
$ cargo yank --vers 1.0.1
|
||
Updating crates.io index
|
||
Yank guessing_game@1.0.1
|
||
```
|
||
If we wanted to undo that we would add `--undo` to the command.
|
||
|
||
This would allow prjects to start depending on that version again
|
||
```
|
||
$ cargo yank --vers 1.0.1 --undo
|
||
Updating crates.io index
|
||
Unyank guessing_game@1.0.1
|
||
```
|
||
ONCE AGAIN NOTICE a yank does *NOT* delete any code.
|
||
|
||
|
||
If you accidentally uploaded secrets, you must reset those secrets immediately. |