Rust API & semver policy¶
disarm ships two surfaces from one crate, governed by two independent
stability policies:
| Surface | Where | Stability |
|---|---|---|
| Rust crate | disarm::api + error types |
semver, described below |
| Python package | import disarm |
the pinned Python API (enforced by tests/test_api_stability.py) |
A change can be breaking for one and not the other. The Rust semver version (the
crate version in Cargo.toml) and the Python distribution version are kept in
lockstep numerically, but the guarantees below apply only to the Rust surface.
The public Rust surface¶
The only semver-governed Rust API is:
- the
disarm::apimodule — the idiomatic,pyo3-free function surface and its parameter types (TargetScript,NormalizationForm,UrlComponent,ReverseLang,Platform,SlugConfig,AutoLangInspection,HostnameAnalysis, …); - the error types
Error,ErrorKind, andErrorMode.
Everything else is an implementation detail and carries no guarantee:
- modules declared
pub(crate)(the Layer-1 algorithm cores); - the three
#[doc(hidden)] pubmodules (emoji,transliterate,tables), exposed only so the in-repo Criterion/iai benchmarks — separate crates that can see justpubitems — can measure the cores directly. They are excluded from docs.rs and fromcargo-semver-checks. Do not depend on them. - the
extension-modulefeature and thedisarm._corePyO3 layer.
If you find yourself reaching past disarm::api, please open an issue — the
missing capability belongs in api.
What counts as a breaking change¶
Following SemVer and the Rust API guidelines, a major bump is required to:
- remove or rename a public
apiitem, or change a function signature; - add a field to a public struct that is not
#[non_exhaustive], or a variant to a non-#[non_exhaustive]enum; - raise the MSRV (see below).
A minor bump covers additive changes: new api functions, new
#[non_exhaustive] enum variants, new struct fields behind #[non_exhaustive].
The public enums (ErrorKind, TargetScript, NormalizationForm, …) are marked
#[non_exhaustive] precisely so new variants are a minor, not major, change —
always include a _ => arm when matching them.
Note: transliteration output is data-driven (Unicode tables, romanization standards). Output changes from table/standard updates are documented in the changelog but are not treated as semver-breaking — pin a version if you need byte-stable output.
MSRV¶
The minimum supported Rust version is recorded as rust-version in
Cargo.toml. An MSRV increase is a minor-version change and is called out in the
changelog. (Dev-only tooling — benches, cargo test --all-targets — may require
a newer toolchain than the shipped library; that is not part of the MSRV
contract.)
Feature flags¶
| Feature | Default | Purpose |
|---|---|---|
| (none) | ✅ | Pure-Rust core. No pyo3, no libpython. This is what cargo add disarm gives you. |
extension-module |
— | Builds the disarm._core Python extension (pulls in pyo3). Python wheel only — Rust consumers never enable it; a bare cargo build --features extension-module fails to link without an interpreter. |
embed-dicts |
— | Embeds the compiled Arabic/Persian/Hebrew context dictionaries into the binary (otherwise they are loaded at runtime). |
Verifying the published surface¶
# The pure dependency tree must carry no pyo3 (the crates.io core is libpython-free)
cargo tree -e no-dev | grep -qi pyo3 && echo "pyo3 leaked!" || echo "pure core OK"
# What cargo would publish
cargo package --list
# API-compatibility check against the last release
cargo semver-checks check-release