---
title: Crate features, dependencies and MSRV
description: What rtb-assets pulls into your build, which upstream features it turns on for the whole graph, the minimum supported Rust version, and how it is versioned.
tags: [reference, cargo, dependencies, msrv]
---
# Crate features, dependencies and MSRV
Everything on this page is read from `Cargo.toml`, `rust-toolchain.toml`
and `deny.toml` at version 0.6.3.
## What Cargo features does rtb-assets have?
None. `rtb-assets` declares no `[features]` table, so there is nothing
to enable, nothing to disable, and `default-features = false` changes
nothing:
```toml
[dependencies]
rtb-assets = "0.6"
```
Everything the crate can do, it does in every build. That is a
deliberate simplification for a crate this small — the cost is that the
YAML and JSON loaders, and the `miette` diagnostic machinery, come
along even if you only ever call `open`.
## What does it pull in?
| `rust-embed` | 8.12.0 | `debug-embed`, `include-exclude` | backs `EmbeddedSource` |
| `serde` | 1.0.229 | `derive` | deserialisation target for the merge loaders |
| `serde-saphyr` | 1.3.0 | `deserialize` only, default features off | YAML parsing |
| `serde_json` | 1.0.150 | — | the merge is performed on `serde_json::Value` |
| `json-patch` | 4.2.0 | — | the RFC 7396 merge itself |
| `miette` | 7.6.0 | `fancy` | `AssetError` is a `Diagnostic` |
| `thiserror` | 2.0.19 | — | `AssetError` derivation |
There is no async runtime, no TLS stack, and no dependency back on
`rust-tool-base` — `deny.toml` bans `rust-tool-base`, `rtb-cli-bin`,
`openssl-sys` and `native-tls` outright, and `anyhow` except as a
dev-only transitive of `cucumber`. The crate is usable outside the
toolkit, which is the point of it being a crate.
## `debug-embed` is on, and that changes rust-embed's default behaviour
`rust-embed`'s default is to embed files in release builds but read
them from disk at runtime in debug builds, so you can edit an asset and
see the change without recompiling. `rtb-assets` enables the
`debug-embed` feature, which — in rust-embed's own words — means
"always embed the files in the binary, even in debug mode".
The consequences are worth being explicit about:
- **There is no dev-mode disk passthrough**, in any profile. Editing a
file under your `#[folder]` requires a rebuild for `EmbeddedSource`
to see it.
- **Cargo features are additive across the graph.** Because
`rtb-assets` turns `debug-embed` on, every other crate in your build
that uses `rust-embed` gets it too, whether or not it asked. If you
were relying on debug-mode passthrough elsewhere, adding `rtb-assets`
will quietly take it away.
- **The `#[folder]` path resolves against `Cargo.toml`** rather than
the working directory, which is the `debug-embed` behaviour in both
profiles.
To get live-editing behaviour anyway, layer a `DirectorySource` over
the embedded one and point it at the same directory in development.
[Layer precedence](../explanation/layer-precedence.md) makes the disk
copy win.
`include-exclude`, the other feature enabled here, is what lets your
own embed type carry `#[include = "*.yaml"]` and `#[exclude = "*.png"]`
attributes. Matching is on relative paths via globset, and `exclude`
beats `include`.
## `miette`'s `fancy` feature comes with it
`AssetError` derives `miette::Diagnostic`, and the dependency is
declared with the `fancy` feature, which brings the graphical report
renderer and its terminal-detection crates — `owo-colors`,
`supports-color`, `supports-hyperlinks`, `supports-unicode`,
`terminal_size`, `textwrap` and `backtrace`. Feature unification means
your build gets them even if you only ever format an `AssetError` with
`Display`.
For a CLI tool, which is what this crate was extracted from, that is
what you want. For a library or a size-sensitive binary it is a cost
you cannot currently opt out of.
## What Rust version does it need?
`rust-version = "1.89"` and `edition = "2021"`, so 1.89 is the
declared minimum for consumers. `serde-saphyr` 1.3 sets that floor;
under `serde_yaml` it was 1.85, set by `rust-embed` 8.12 pulling in `sha2`
0.11 and `globset` 0.4.19.
Development is pinned separately: `rust-toolchain.toml` fixes the
toolchain to an exact **1.98.1**, not the floating `stable` channel,
with `rustfmt`, `clippy` and `rust-src`. Pinning makes a toolchain
change an intentional, reviewable merge request that Renovate opens,
rather than something that arrives on its own and breaks CI with no
source change.
Nothing in CI compiles the crate against 1.89. The number is the
minimum `serde-saphyr` declares and tests in its own CI, and a
dependency bump can raise the floor without CI here noticing. If you
need it enforced, build with `cargo +1.89 check` before depending on it.
## What lints does it hold itself to?
| `#![forbid(unsafe_code)]` | `src/lib.rs` | no `unsafe` in the shipped crate, and it cannot be re-allowed |
| `unsafe_code = "deny"` | `[lints.rust]` | package-wide, so test files can `#![allow]` where they must |
| `missing_docs = "warn"` | `[lints.rust]` | every public item carries a doc comment |
| `clippy::pedantic`, `clippy::nursery`, `clippy::cargo` | `[lints.clippy]` | warn, with `module_name_repetitions`, `missing_errors_doc`, `missing_panics_doc` and `multiple_crate_versions` allowed |
CI runs `cargo clippy --all-targets -- -D warnings`, so a warning fails
the merge gate.
## How is it versioned and released?
By [colophon](https://gitlab.com/phpboyscout/colophon) from Conventional Commits — a
Release MR is kept open on `main`, and merging it tags; the tag's pipeline publishes.
Tags are per-crate, `rtb-assets-v<version>`, even though this is a
single-crate repository, to stay consistent with the rest of the Rust
toolkit. `cargo-semver-checks` vets the public API on every release MR, so
a breaking change to any signature on this site shows up before
publication.
Do not tag by hand.
## Known dependency risks
`serde-saphyr` is the only YAML parser here. It replaced `serde_yaml`,
because `serde_yaml` is unmaintained and RUSTSEC-2024-0449
flags the `unsafe-libyaml` it pulls in. `cargo deny check` passes with
no advisory waivers, and `deny.toml` deliberately carries none. If one
becomes necessary it will name the advisory, the reaching chain and a
dated sunset condition.
Changing the parser, or the options it runs with, changes the YAML
conversion table in [Merge semantics](merge-semantics.md). That is why
the behaviour is documented there in detail rather than described as
"whatever the parser does".