negotiator 0.1.0

HTTP content negotiation for Accept, Accept-Charset, Accept-Encoding, and Accept-Language headers. A faithful port of the `negotiator` npm package. Zero dependencies, no_std.
Documentation
  • Coverage
  • 100%
    23 out of 23 items documented2 out of 19 items with examples
  • Size
  • Source code size: 57.79 kB This is the summed size of all the files inside the crates.io package for this release.
  • Documentation size: 358.29 kB This is the summed size of all files generated by rustdoc for all configured targets
  • Ø build duration
  • this release: 2s Average build duration of successful builds.
  • all releases: 2s Average build duration of successful builds in releases after 2024-10-23.
  • Links
  • Homepage
  • trananhtung/negotiator
    0 0 0
  • crates.io
  • Dependencies
  • Versions
  • Owners
  • trananhtung

negotiator

All Contributors

crates.io docs.rs CI license

HTTP content negotiation for Rust.

Pick the response representation a client prefers from the Accept, Accept-Charset, Accept-Encoding, and Accept-Language request headers — quality values, wildcards, language prefix matching, and all.

negotiator is a faithful Rust port of the widely-used negotiator npm package (the engine behind Express's req.accepts*), which has no Rust equivalent.

  • Zero dependencies
  • #![no_std] (needs only alloc)
  • Differential-tested against the reference negotiator implementation across all four negotiations

Install

[dependencies]
negotiator = "0.1"

Usage

use negotiator::Negotiator;

let n = Negotiator::new()
    .accept("text/html, application/json;q=0.9, */*;q=0.1")
    .accept_language("en-US, fr;q=0.8")
    .accept_encoding("gzip, br;q=0.9");

// Pick the best of what the server can produce.
assert_eq!(n.media_type(Some(&["application/json", "text/html"])).as_deref(), Some("text/html"));
assert_eq!(n.language(Some(&["fr", "en"])).as_deref(), Some("en"));
assert_eq!(n.encoding(Some(&["gzip", "br"]), None).as_deref(), Some("gzip"));

// Or list everything the client accepts, in order of preference.
assert_eq!(
    n.media_types(None),
    ["text/html", "application/json", "*/*"]
);

You can also call the free functions directly:

use negotiator::preferred_languages;

assert_eq!(
    preferred_languages(Some("en-US, en;q=0.9, fr;q=0.8"), Some(&["en", "fr"])),
    ["en", "fr"]
);

Semantics

Each negotiation parses the relevant header into quality-weighted entries and ranks the server's available options by quality, then specificity (an exact match beats a wildcard; en-US beats en beats *), then the header and option order — exactly as the npm package does.

  • A header argument of None means the header is absent, which RFC 7231 treats as accept everything (* / */*). Some("") means an explicit empty header (accept nothing). Accept-Encoding is the exception: a missing or empty header leaves only the implicit identity encoding.
  • preferred_encodings always considers an implicit identity encoding (at the lowest advertised quality) unless the header lists identity or *, and accepts an optional preferred list to bias quality ties toward a server-preferred order.

Note on case folding

The reference uses JavaScript's toLowerCase; this crate folds case with ASCII rules, which is identical for the ASCII tokens that appear in these headers (media types, charsets, encodings, language tags) and differs only on exotic non-ASCII input.

Contributors ✨

This project follows the all-contributors specification. Contributions of any kind are welcome — code, docs, bug reports, ideas, reviews! See the emoji key for how each contribution is recognized, and open a PR or issue to get involved.

Thanks goes to these wonderful people:

License

Licensed under either of MIT or Apache-2.0 at your option.