Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
97 changes: 97 additions & 0 deletions text/3416-feature-metadata.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
- RFC PR: [rust-lang/rfcs#3416](https://github.com/rust-lang/rfcs/pull/3416)
- Rust Issue:
[rust-lang/cargo#14157](https://github.com/rust-lang/cargo/issues/14157)
- Amendment adding a metadata schema: 2026-08-19

## Summary

Expand Down Expand Up @@ -121,6 +122,58 @@ requires an MSRV bump for users of your published package as we have not been
actively streamlining the workflow for maintaining separate development and
published MSRVs.

### Metadata changes

Because the `features` key in metadata output does not allow for additional
fields, a `features_v2` key is introduced. It follows the following rules:

1. The type is an object with feature names as keys (same as `features`) and
objects as values.
2. The value objects *may* contain a key `enables`, a list of strings of
features to enable. With future RFCs, there will be additional keys.
3. If `enables` is not provided or is empty, the feature enables no other
features.
4. If `features` and `features_v2` are both present, they *must* have identical
feature names and enabled features. That is, the keys and values in
`features` are the same as the keys and `<value>.enables`, respectively, in
`features_v2`.

If a new `--format-version=2` is ever introduced, the content of `features`
will be replaced by `features_v2`.

As an example, the following features table:

```toml
[features]
foo = []
# Note that `doc` is not accepted as part of this RFC, but it is included here
# to demonstrate additional keys.
bar = { enabled = ["foo"], doc = "simple docstring for bar" }
baz = { doc = "simple docstring for baz" }
```

Will include the following in its metadata:

```json5
"features": {
"foo": [],
"bar": ["foo"],
"baz": []
},
"features_v2": {
"foo": {},
"bar": { "enables": ["foo"], "doc": "simple docstring for bar" },
"baz": { "doc": "simple docstring for baz" }
}
```

The following `jq` query can be used to select `features_v2` or, if not
available, to get `features` in the same format:

```jq
.features_v2 // (.features | map_values({enables: .}))
```

## Drawbacks

[drawbacks]: #drawbacks
Expand Down Expand Up @@ -151,6 +204,50 @@ like `dependencies`.
However, we likely would want to prefer using new fields over adding more syntax,
like with [disabling default features](https://github.com/rust-lang/cargo/issues/3126).

### Metadata

There are a handful of alternatives for metadata design:

* Instead of using `features_v2`, we could introduce metadata version 2 right
away. A new metadata version may want to include other changes outside of this
RFC's scope, so a way to work with existing metadata is preferred for now.
* Within `features_v2` JSON values, this RFC allows `enables` to be omitted
if empty: it could be always emitted instead. Metadata output is typically
machine-read so, given keys can easily be defaulted, requiring the key would
provide little value. For example, this simple `jq` query adds `"enables": []`
on keys where not present:

```jq
`.features_v2 | map_values({enables: .enables // []})`
```

This decision is forward-compatible with unconditionally emitting `enables`
in the future (similar to accepting feature objects in `Cargo.toml` without
`enables`), so this decision does not block always emitting the key if desired
at some point. The reverse would not be true.
* `features_v2` could list only features that have additional metadata, relying
on the combination of `features` and `features_v2` to fully express the
metadata schema. Under this idea, the following could be considered valid:

```json5
"features": {
"foo": [],
"bar": ["foo"],
},
"features_v2": {
// No `"foo"` key, and no `enables` for `bar`
"bar": { "doc": "simple docstring for bar" },
}
```

This is not done because it conflicts with the idea that `features_v2` will
eventually replace `features`. By making `features_v2` contain all information
that `features` does, this transition will be easier for metadata consumers,
and the (small) complexity of object merging is avoided.
* `features_v2` could be called something else such as, from an informal
proposal, `features_metadata`. `features_v2` was selected to clearly convey
that it will be the form of `features` in a future metadata version.

## Prior art

[prior-art]: #prior-art
Expand Down