As of now, Docs.rs does not display the available features that a crate offers. I'd like to change this, but there's some design work that needs to be done before hand for things to go smoothly, with these being the largest concerns
- Opt-in/opt-out functionality
- Displaying optional dependencies
- The final UI displayed to users
Introduction
Crates have features. These are options that can be set within a crate's Cargo.toml that enable and disable dependencies and all-around functionality of a crate. As an example, winapi gates each Windows module behind a feature so that you only enable what you need to use (hopefully allowing a smaller dependency footprint and faster compile times). Docs.rs displaying these features has been a requested feature (of Docs.rs) for a while now, since it'll allow users to easily see what they have to choose from without having to dive into source code. Note that any changes made only apply after they've been deployed, we could potentially re-parse old Cargo.toml files, but that'd be lots of extra work for the server to do while delivering content to the user.
Opt-in/opt-out functionality
Docs.rs uses a number of keys in the package.metadata
section of crates' Cargo.toml, and I think that's a great place to store this information, an excluded-features
key of some sort probably being the best.
This key would allow you to select features for Docs.rs to not display to users, so in this example Cargo.toml
[features]
my-public-feature = ["my-public-feature"]
my-secret-feature = []
[package.metadata.docs.rs]
excluded-features = ["my-secret-feature"]
The only things displayed to users would be that there's the my-public-feature
that activates something (similar to how rustdoc handles private/hidden struct fields or enum variants), creating a user-sided display to the effect of
Features | Activates |
---|---|
my-public-feature |
...some features omitted |
Similarly we could include an include-features
key that worked inversely, disabling all features except the ones specifically listed, but I think that's a less useful tool.
These inclusion/exclusion keys are useful for two main reasons, first being that many crates have something to the effect of a "benchmarking feature" that exports otherwise private items for benchmarking, and the second is for cases where users have many generated or detected features, but there's a preferred subset of features that users should be using.
Displaying optional dependencies
Cargo creates a feature for each optional dependency in your Cargo.toml, so with this manifest
serde = { version = "*", optional = true }
The serde
feature is implicitly created. This could potentially create a lot of spam in crates with many optional deps, but automatically omitting all optional dependencies could also lead to an information loss, like in the serde
example above (where theoretically when serde is enabled, the crate gains serde support).
There's a few ways to address this, either leaving it up to maintainers to select important information via excluding features or creating an entirely separate table for optional dependency features. In the latter case something like this could be expected
[features]
give-me-serde = ["serde"]
derive-me-serde = ["serde/derive"]
some-other-feature = []
serde = { version = "1.0.115", optional = true }
Feature | Activates |
---|---|
give-me-serde |
serde |
derive-me-serde |
serde/derive |
some-other-feature |
none |
Optional dependencies | Feature name |
---|---|
serde v1.0.115 |
serde |
The distinction between the dependency's name & version and the name of the feature is needed because Cargo allows renaming dependencies, so the following manifest would be valid and create the following features display
serde = { version = "1.0.115", optional = true }
middle_serde = { version = "0.6.7", package = "serde", optional = true }
old_serde = { version = "0.0.0", package = "serde", optional = true }
Optional dependencies | Feature name |
---|---|
serde v1.0.115 |
serde |
serde v0.6.7 |
middle_serde |
serde v0.0.0 |
old_serde |
User-sided UI
I'm not a very design-inclined person, so the problems of layout and making things pretty are fairly lost on me. Whichever of the approaches to displayed information is taken, it's clear that it'll be a sizeable amount of information, which all needs to go somewhere. The best ideas I have are either below a crate's readme or as a separate tab alongside Documentation
, Crate
, Source
and Builds
that's entirely dedicated to displaying features.
We could make a summarized version of features listed in the crate dropdown, but it's already quite full and I think that a link to wherever features were displayed has the same effect.
Possibilities and extensions
Every crate has a default feature set, whether implicitly created or explicitly created using the features.default
key in their Cargo.toml. The default features of a crate are particularly important, as it tells you what will be enabled by default. As such the default features should have attention called to them, whether by highlighting them in a special way, hoisting them to the top of the features list or both.
Another generally useful thing to display would be what the current docs are being built with. Crates can set the features that Docs.rs builds them with by using the metadata section, which can mean that things exclusive to a feature may or may not be displayed depending on what's set. This would help with that by telling the users exactly what features are being shown to them, and could be included as part of the features section, creating something to this effect:
These docs were built with the following features: give-me-serde
Feature | Activates |
---|---|
give-me-serde |
serde |