Platform Agnostic Drivers in Rust: Publishing to Crates.io

Platform Agnostic Drivers in Rust: Publishing to Crates.io

After successfully implementing the platform-agnostic driver for the MAX7219 LED Driver IC in the previous post, we move on to step 4 of the series of posts:

  1. Create simple code to configure and test the MAX7219 with a simple application. Link to Post.
  2. Refactor and optimize the code in the first step by adding functions. This step would also create a driver that isn't platform agnostic. Link to post.
  3. Refactor code in the second step to incorporate embedded-hal traits and create a platform-agnostic driver. Link to Post.
  4. Register the driver in crates.io and publish its documentation.
  5. Add advanced features to the driver and introduce a new version of the crate.

The idea here is to highlight the importance of documentation 🙄, and well show how crate publishing is done to encourage contribution. I've broken down the process into 4 steps.

Let's dig in!

Step 1: Comment the Code! #️⃣

In Rust, documentation is generated from the code through different types of comments. One needs to understand how to comment so that the documentation appears properly. I would say for a start there are two types of comments one should be aware of. The first is the //! comment for a module or crate-level documentation. This would be for the types of comments we would include at the beginning of the library file to describe what the driver is doing. Interestingly enough, it also supports Markdown. Here's an example of what I included in the beginning of the file:

//! This crate provides a platform-agnostic driver for the MAX7219 LED Driver IC.
//!
//! This driver was built using the [embedded-hal](https://docs.rs/embedded-hal/0.2.7/embedded_hal/) traits.
//!

The other comment type is the /// which documents the next item like a function or enum. Here's an example:

/// Enumeration of commands in the register map of the MAX7219.
pub enum Command {
    NoOp = 0x00,
    DecodeMode = 0x09,
    Intensity = 0x0A,
    ScanLimit = 0x0B,
    Shutdown = 0x0C,
    DisplayTest = 0x0F,
}

As such, I went through the complete code and documented all the different parts. The full code is available here.

Additionally, I added a README.md file to the project tree in which I simply replicated the introductory commentary of the library file. This is because crates.io uses the readme for the library page.

Step 2: Generate the Documentation 📚

This step might sound intimidating and complicated. However, it turns out that it's much easier than one would expect as it's all automated by Cargo! All you need to do is go to the driver folder root and run the following:

cargo doc

This will generate a local html file that captures the documentation that will eventually be published online to docs.rs. This way you can verify the generated documentation before it is published. You can find the html by navigating down the project folder tree to [root-folder-name]/target/doc/[crate-name]/index.html. It will look something like this:

docs.rs documentation

Step 3: Update the Package Metadata 🏷

Before publishing there is a bunch of metadata that can be included in the Cargo.toml file so that our driver is easily discoverable. More importantly, the metadata provides information that makes the crate more accessible and informative. The metadata is updated in the Cargo.toml file under the [package] section. This is the metadata I included:

[package]
name = "max7219-driver"
version = "0.1.0"
edition = "2021"
authors = ["Omar Hiari"]
description = "This crate provides a platform agnostic driver for the MAX7219 LED Driver IC."
homepage = "https://apollolabsblog.hashnode.dev/"
readme = "README.md"
repository = "https://github.com/apollolabsdev/stm32-nucleo-f401re/tree/main/Drivers/"
license = "MIT OR Apache-2.0"
exclude = [
    "target/debug/*",
    ]

Note the exclude, field. This was added to exclude certain files that need not be uploaded. This is data that increases the crate size and is not necessary to be published as the upload limit is 10MB. Additionally, there are other metadata options to include if one desires. For a full list of metadata, one can refer to this page.

🚨 Important Note

The name of the package is required to be unique. Meaning that it cannot be the same name as another crate already published. For this driver, I actually went to crates.io and searched for max7219 and found that there was already another crate with the same name. As such, I had to change the name of the crate in which I chose max7219-driver instead.

Step 4: Publish to Crates.io! 📦

This is the final step and if you already haven't you need to create a crates.io account first with your GitHub account. After you do, go to crates.io and login with GitHub. In account settings, from the sidebar go to API tokens, you'll see something like this:

crates.io sidebar

Click on "new token" and give the token a name. A new token will be generated that you need to copy immediately and run cargo login in the terminal and provide it the API token and hit enter so that it's stored.

🚨 Important Note

The API token is unique to your account and meant to be a secret so do not share it!

After that, before publishing, Cargo documentation recommends that you first run cargo publish --dry-run to ensure there aren't any warnings or errors before publishing. This step will do the following:

  1. Perform some verification checks on your package.
  2. Compress your source code into a .crate file.
  3. Extract the .crate file into a temporary directory and verify that it compiles.

Also, keep in mind that you are limited to a 10MB crate size. With the dry-run, you'll be able to see the size of the crate. If you end up with more than 10MB, I recommend you refer to the cargo documentation for techniques to reduce the file size. If you recall from earlier, I added the exclude option to eliminate files from being published.

If everything checks out, all that is left is to publish our crate by running the following line:

cargo publish

That's all! Now you can go to crates.io and see your crate published online! You can check out the published crate here.

Conclusion

If one wants to contribute crates/libraries to the Rust open-source ecosystem, then one has to be familiar with the associated tools. Cargo provides a lot of automation around this matter that makes the process simpler than one would think. In this post, the steps for publishing a device driver crate to crates.io have been shown. In the following post, we'll see how to add features and update the published package. Have any questions or thoughts? Please post them in the comments below 👇.

Did you find this article valuable?

Support Omar Hiari by becoming a sponsor. Any amount is appreciated!