The two caveats from the dependency investigation. ## The capabilities feature Camera and location attachments are now available behind `features = ["capabilities"]`, which pulls `nigig-uikit` and supplies `UikitAttachmentProvider`. Measured: 89 crates by default, 275 with the feature on. That cost is real and it is inherent, not packaging waste. `camera_widget` imports `send_geocode_request` and `request_map_tile` from `nigig-core`, both of which call `spawn_async` — the shared Tokio runtime — and the first makes an HTTPS call to Nominatim. A camera that geocodes needs an async runtime and an HTTP client; there is no lighter honest version. It is affordable because it is opt-in, and because any app enabling it already depends on `nigig-core`, so that app's own tree grows by nothing. Everything touching `nigig-uikit` is in one module, so the boundary is a file rather than `#[cfg]` scattered through the widget. The provider holds no widgets of its own: the host owns the `CameraWidget` already in its tree and this asks it to open, because a provider that instantiated a second camera would fight the first for the device. A second request while one is outstanding is refused rather than overwriting. The table turns that refusal into `AttachmentUnavailable`, so the user is told the camera is busy instead of watching their first request vanish. File picking is deliberately declined here — `robius-file-picker` already ships unconditionally and costs nothing, and two paths for one job is one too many. Two CI gates, both verified to fail when they should: the opt-in build must keep compiling, and the default build must pull none of `tokio`, `reqwest`, `hyper`, `clap`, `csv`, `image`, `nigig-uikit` or `nigig-core`. The second checks the resolved `cargo tree` rather than the manifest, because feature unification can switch an optional dependency on from a sibling crate. Tests 99 default, 105 with the feature. Both clippy-clean. ## The matrix_client defect Raised in REVIEWS/MATRIX_CLIENT_FEATURE_GATE.md rather than fixed. It is not my crate, nothing depends on the broken combination, and a blind fix could change behaviour someone relies on. `matrix_client` declares `native = ["dep:tokio", "dep:reqwest", "dep:rusqlite"]` but its source gates on `#[cfg(not(target_arch = "wasm32"))]`. Two switches for the same modules, so on a native target with the feature off the modules compile and their dependencies do not — 19 errors, 26 ungated uses across 7 files. There is no CI job for the crate, which is why it rotted unnoticed. The note corrects an overstatement I made while arguing for the trait hook. I said fixing this would unblock wasm. It would not: `matrix_client` already builds clean for wasm32 with `--no-default-features`, and `nigig-core` has 8 wasm errors of its own (`crate::platform::spawn` missing) that have nothing to do with it. The only broken combination is native-target-with-feature-off, which nothing builds. I also said earlier that `matrix_client` was heavy — it is a 7-dependency local crate, not matrix-sdk. That was wrong and it inflated the case for the trait hook; the note records the measured numbers instead.
139 lines
4.7 KiB
Markdown
139 lines
4.7 KiB
Markdown
# `matrix_client`: the `native` feature does not do what it says
|
|
|
|
- **Date:** 2026-08-18
|
|
- **Status:** raised, not fixed. This is not my crate and nothing currently
|
|
depends on the broken combination; fixing it blind risks changing
|
|
behaviour someone is relying on.
|
|
- **Found while:** measuring what `makepad-table` would cost if it depended
|
|
on `nigig-uikit` for camera and location.
|
|
|
|
## The defect
|
|
|
|
`crates/matrix_client/Cargo.toml` declares a feature that gates three heavy
|
|
dependencies:
|
|
|
|
```toml
|
|
[features]
|
|
default = ["native"]
|
|
native = ["dep:tokio", "dep:reqwest", "dep:rusqlite"]
|
|
```
|
|
|
|
The source does **not** gate on that feature. It gates on the target:
|
|
|
|
```rust
|
|
// crates/matrix_client/src/lib.rs
|
|
#[cfg(not(target_arch = "wasm32"))]
|
|
pub mod auth;
|
|
#[cfg(not(target_arch = "wasm32"))]
|
|
pub mod client;
|
|
// ... and so on for http, persistence, sync_service, worker
|
|
```
|
|
|
|
Two different switches for the same set of modules. On a native target with
|
|
the feature turned off, the modules still compile but their dependencies are
|
|
absent:
|
|
|
|
```
|
|
cargo check -p matrix_client --no-default-features
|
|
-> 19 errors: unresolved import `reqwest`, cannot find `tokio`,
|
|
unresolved import `rusqlite`
|
|
```
|
|
|
|
26 ungated uses across 7 files:
|
|
|
|
| File | Uses |
|
|
|---|---|
|
|
| `worker.rs` | 9 |
|
|
| `error.rs` | 4 |
|
|
| `persistence.rs` | 4 |
|
|
| `sync_service.rs` | 4 |
|
|
| `client.rs` | 2 |
|
|
| `http.rs` | 2 |
|
|
| `types.rs` | 1 |
|
|
|
|
## What is *not* broken
|
|
|
|
The wasm path is coherent:
|
|
|
|
```
|
|
cargo check -p matrix_client --no-default-features --target wasm32-unknown-unknown
|
|
-> 0 errors
|
|
```
|
|
|
|
`target_arch = "wasm32"` and the absent dependencies agree there, because on
|
|
wasm the `[target.'cfg(not(target_arch = "wasm32"))'.dependencies]` section
|
|
is skipped anyway. The only broken combination is **native target with the
|
|
feature off**, which nothing in this repo builds and no CI job covers —
|
|
which is why it rotted without anyone noticing.
|
|
|
|
## Correcting an overstatement
|
|
|
|
I previously said fixing this would "unblock wasm". That was wrong, and I
|
|
should have checked before saying it. `nigig-core` has 8 wasm errors of its
|
|
own that have nothing to do with `matrix_client`:
|
|
|
|
```
|
|
cargo check -p nigig-core --no-default-features --target wasm32-unknown-unknown
|
|
-> 8 errors: cannot find function `spawn` in module `crate::platform`
|
|
```
|
|
|
|
Fixing `matrix_client` moves nobody closer to a wasm build on its own.
|
|
|
|
## Why it was not fixed here
|
|
|
|
It buys the thing that surfaced it precisely nothing. `makepad-table` wanted
|
|
`nigig-uikit` for its camera and location widgets, and `camera_widget`
|
|
imports exactly two items from `nigig-core`:
|
|
|
|
```rust
|
|
use nigig_core::syncing::{send_geocode_request, request_map_tile, ...};
|
|
```
|
|
|
|
Both call `spawn_async`, which *is* the shared Tokio runtime, and
|
|
`send_geocode_request` makes an HTTPS call to Nominatim. So the camera widget
|
|
requires `nigig-core/native` by its own nature. `matrix_client` sits on the
|
|
same flag: making it honour the feature would let *it* build without tokio,
|
|
but `nigig-uikit` turns tokio straight back on through `nigig-core`. Net
|
|
saving for that use case: zero.
|
|
|
|
Measured, so the numbers are not guesses:
|
|
|
|
| Configuration | Crates in the tree |
|
|
|---|---|
|
|
| `makepad-widgets` alone | 88 |
|
|
| `+ nigig-uikit` | 274 |
|
|
| `matrix_client` with `native` | 199 |
|
|
| `matrix_client` without `native` (if it compiled) | 140 |
|
|
|
|
## Two ways to fix it
|
|
|
|
**A. Make the source honour the feature.** Change 26 sites from
|
|
`#[cfg(not(target_arch = "wasm32"))]` to `#[cfg(feature = "native")]`, and
|
|
move the dependencies out of the target section into the plain one, where
|
|
`optional = true` is what decides. This is the honest reading of what the
|
|
feature claims. It is also the version that could change behaviour for a
|
|
native build that somehow relied on the modules existing without the
|
|
dependencies — none exists today, but the check is a compile, not a test.
|
|
|
|
**B. Delete the feature.** If `matrix_client` is native-only in practice —
|
|
and the 26 sites say it is — then `native` is a claim the crate does not
|
|
honour, and removing it is more truthful than a flag nobody can use. The
|
|
dependencies stay non-optional in the target section, where the wasm split
|
|
already works.
|
|
|
|
B is smaller and matches observed reality. A is right if a wasm or
|
|
dependency-light build is actually wanted, in which case `nigig-core`'s 8
|
|
errors need fixing too, and that is the larger piece.
|
|
|
|
## Whichever is chosen, it needs a gate
|
|
|
|
There is no CI job for `matrix_client`. A feature that nothing builds is a
|
|
feature that breaks silently, which is exactly what happened. One line in a
|
|
workflow:
|
|
|
|
```yaml
|
|
- name: The native feature means what it says
|
|
run: cargo check -p matrix_client --no-default-features
|
|
```
|
|
|
|
That step fails today. It should be added with the fix, not before it.
|