iri-rs 3.4.1

Allocation-conscious URI/IRI (RFC 3986/3987) parser, resolver and normalizer with borrowed and owned types, optional compile-time macros and vocabulary enums.
Documentation

iri-rs

Crate Docs MSRV License

An allocation-conscious Rust implementation of URIs and IRIs (RFC 3986 / RFC 3987) — parse, access components, mutate in-place, resolve references, normalize, compare.

A URI/IRI is a sequence of characters split into distinguishable components:

  foo://user@example.com:8042/over/there?name=ferret#nose
  └┬┘   └──────────┬────────┘└────┬────┘└────┬─────┘└┬─┘
   │               │              │          │       │
 scheme        authority          path      query  fragment

This crate gives you typed borrowed and owned views over each of them.

use iri_rs::Iri;

let iri = Iri::parse("https://www.rust-lang.org/foo/bar?query#frag")?;
assert_eq!(iri.scheme(), "https");
assert_eq!(iri.authority(), Some("www.rust-lang.org"));
assert_eq!(iri.path(), "/foo/bar");
assert_eq!(iri.query(), Some("query"));
assert_eq!(iri.fragment(), Some("frag"));

Mutate in place, no per-component allocations:

use iri_rs::IriBuf;

let mut iri = IriBuf::new("https://rust-lang.org")?;
iri.set_authority(Some("rust-lang.org:40"))?;
iri.set_path("/foo/bar")?;
iri.set_query(Some("q"))?;
assert_eq!(iri, "https://rust-lang.org:40/foo/bar?q");

Why this fork

Fork of iref, static-iref, and iref-enum by Timothée Haudebourg. Public API and RFC behavior largely unchanged; this fork adds:

  • Workspace split into iri-rs-core, iri-rs-static, iri-rs-enum under the iri-rs facade.
  • Reworked IriEnum derive (iri-rs-enum) — compile-time prefix resolution, const-friendly output via from_raw_parts.
  • Reworked compile-time macros (iri!, uri!, iri_ref!, uri_ref!) — validate at macro expansion, emit const-friendly from_raw_parts with pre-computed component positions.
  • SIMD-accelerated UTF-8 validation via simdutf8, memchr-accelerated percent scan, SWAR/byte-level fast paths on new, eq, ord, hash, len.
  • Opt-in bytewise feature: compare, hash and order IRIs byte-wise instead of by RFC 3987 normalization.
  • Criterion bench suite (parse, accessors, resolve, normalize, mutate, normalize_eq, validate).
  • Rust 2024 edition, MSRV 1.85.
  • Renamed crates: irefiri-rs, static-irefiri-rs-static, iref-enumiri-rs-enum.

Credit and history preserved — see Attribution.

Install

cargo add iri-rs

Feature flags

Flag Default Enables
static Compile-time iri!, uri!, iri_ref!, uri_ref! macros
enum #[derive(IriEnum)] for vocabulary enums
serde Serialize / Deserialize for borrowed and owned types
bytewise Compare, hash and order byte-wise instead of by RFC 3987 normalization

bytewise changes comparison, not just hashing

bytewise swaps the PartialEq, Eq, Hash, PartialOrd and Ord impls of Iri, IriRef, Uri and UriRef for byte-wise ones. Without it, those impls compare and hash the RFC 3987 normalized form, so http://example.com/%7Ea and http://example.com/~a are equal and hash alike. With it, they are two different IRIs. Byte-wise is considerably cheaper, and it is the right choice when the IRIs you hold are already in the form you want to compare, but it is a semantic change and not only a speed one.

Cargo unifies features across a dependency graph, which makes this a global switch: one dependency enabling bytewise changes IRI equality for every crate in the build, including crates that never asked for it. Enable it in an application, or in a library whose whole graph you control, rather than in a library published for others to combine.

Compile-time IRIs

With static, IRIs are validated at macro expansion and emitted as const expressions — zero runtime parsing, zero allocation:

use iri_rs::{Iri, iri};

const HOME: Iri<&'static str> = iri!("https://www.rust-lang.org/");

Invalid literals fail to compile. Same for uri!, iri_ref!, uri_ref!.

Vocabulary enums

With enum, map a known vocabulary to a plain enum — cheap storage and comparison, compile-time prefix resolution:

use iri_rs::{iri, IriEnum};

#[derive(IriEnum, PartialEq, Debug)]
#[iri_prefix("schema" = "https://schema.org/")]
pub enum Vocab {
    #[iri("schema:name")] Name,
    #[iri("schema:knows")] Knows,
}

let term = Vocab::try_from(&iri!("https://schema.org/name")).unwrap();
assert_eq!(term, Vocab::Name);

References and resolution

IriRef / IriRefBuf cover absolute and relative references. A strict implementation of the RFC 3986 §5 reference resolution algorithm with Errata 4547:

use iri_rs::{Iri, IriRefBuf};

let base = Iri::parse("http://a/b/c/d;p?q")?;
let mut r = IriRefBuf::new("g;x=1/../y")?;
assert_eq!(r.resolved(&base)?, "http://a/b/c/y");

r.resolve(&base)?;
assert_eq!(r, "http://a/b/c/y");

Equivalence

Equality, ordering and hashing normalize:

  • Dot segmentsa/../a/./b/../b/ca/b/c.
  • Percent encodinghttp://example.orghttp://exa%6dple.org (via pct).
  • Protocol-agnostichttp://example.org and http://example.org:80 are not equal. This crate knows nothing about scheme defaults.
  • Every / counts/foo/bar and /foo/bar/ are not equal.

Two values whose as_str() differs can still hash equal — keep that in mind when used as map keys.

Examples

cargo run --example serde --features serde

Benchmarks

cargo bench -p iri-rs-core

Criterion output: target/criterion/.

MSRV

Rust 1.85 (edition 2024).

Workspace layout

  • iri-rs — facade crate. Re-exports everything behind feature flags. Start here.
  • iri-rs-core — types, parser, resolver, normalizer.
  • iri-rs-staticiri! / uri! / iri_ref! / uri_ref! compile-time macros.
  • iri-rs-enum#[derive(IriEnum)] for vocabulary enums.

Attribution

Original crates: iref, static-iref, and iref-enum by Timothée Haudebourg. Upstream commits are preserved in this repo's history under their original authorship. This fork is a workspace reorganization and a layer of performance work on top of the original design.

License

Dual-licensed, same as upstream. Pick whichever fits: