#uri #parser #builder #rfc3986

no-std fluent-uri

A full-featured URI handling library compliant with RFC 3986

8 releases

new 0.2.0-alpha.5 Apr 17, 2024
0.2.0-alpha.4 Apr 9, 2024
0.2.0-alpha.2 Mar 29, 2024
0.1.4 Mar 11, 2023
0.1.2 Apr 16, 2022

#107 in Encoding

Download history 1424/week @ 2023-12-23 1372/week @ 2023-12-30 1440/week @ 2024-01-06 1408/week @ 2024-01-13 1699/week @ 2024-01-20 2233/week @ 2024-01-27 1801/week @ 2024-02-03 2342/week @ 2024-02-10 2297/week @ 2024-02-17 2512/week @ 2024-02-24 3099/week @ 2024-03-02 4358/week @ 2024-03-09 3536/week @ 2024-03-16 3428/week @ 2024-03-23 3482/week @ 2024-03-30 3427/week @ 2024-04-06

14,601 downloads per month
Used in 19 crates (7 directly)

MIT license

115KB
2K SLoC

fluent-uri

A full-featured URI handling library compliant with RFC 3986. It is:

  • Fast: Zero-copy parsing. Faster than several common URI parsers in Rust[^bench-res].
  • Easy: Carefully designed and documented APIs. Handy percent-encoding utilities.
  • Correct: Forbids unsafe code. Extensively fuzz-tested against other implementations.

crates.io build license

Documentation | Discussions

[^bench-res]: It took 59ns for fluent-uri, 89ns for iref, and 130ns for iri-string to parse the same URI in a benchmark on an Intel Core i5-11300H processor.

Features

  • Parsing.
  • Building.
  • Reference resolution.
  • Normalization.

Examples

  • Uri<&str> and Uri<String> (borrowed and owned variants of URI reference):

    You can parse into a Uri<&str> from a string slice. Uri<&'a str> outputs references with lifetime 'a where possible (thanks to borrow-or-share):

    // Keep a reference to the path after dropping the `Uri`.
    let path = Uri::parse("foo:bar")?.path();
    assert_eq!(path, "bar");
    

    You can build a Uri<String> using the builder pattern:

    let uri: Uri<String> = Uri::builder()
        .scheme(Scheme::new("foo"))
        .authority(|b| {
            b.userinfo(EStr::new("user"))
                .host(EStr::new("example.com"))
                .port(8042)
        })
        .path(EStr::new("/over/there"))
        .query(EStr::new("name=ferret"))
        .fragment(EStr::new("nose"))
        .build();
    
    assert_eq!(
        uri.as_str(),
        "foo://user@example.com:8042/over/there?name=ferret#nose"
    );
    

    You can resolve a URI reference against a base URI:

    let base = Uri::parse("http://example.com/foo/bar")?;
    
    assert_eq!(Uri::parse("baz")?.resolve(&base)?, "http://example.com/foo/baz");
    assert_eq!(Uri::parse("../baz")?.resolve(&base)?, "http://example.com/baz");
    assert_eq!(Uri::parse("?baz")?.resolve(&base)?, "http://example.com/foo/bar?baz");
    

    You can normalize a URI reference:

    let uri = Uri::parse("eXAMPLE://a/./b/../b/%63/%7bfoo%7d")?;
    assert_eq!(uri.normalize(), "example://a/b/c/%7Bfoo%7D");
    
  • EStr (Percent-encoded string slices):

    All components in a URI that may be percent-encoded are parsed as EStrs, which allows easy splitting and fast decoding:

    let query = "name=%E5%BC%A0%E4%B8%89&speech=%C2%A1Ol%C3%A9%21";
    let map: HashMap<_, _> = EStr::<Query>::new(query)
        .split('&')
        .map(|s| s.split_once('=').unwrap_or((s, EStr::new(""))))
        .map(|(k, v)| (k.decode().into_string_lossy(), v.decode().into_string_lossy()))
        .collect();
    assert_eq!(map["name"], "张三");
    assert_eq!(map["speech"], "¡Olé!");
    
  • EString (A percent-encoded, growable string):

    You can encode key-value pairs to a query string and use it to build a Uri:

    let pairs = [("name", "张三"), ("speech", "¡Olé!")];
    let mut buf = EString::<Query>::new();
    for (k, v) in pairs {
        if !buf.is_empty() {
            buf.push_byte(b'&');
        }
        buf.encode::<Data>(k);
        buf.push_byte(b'=');
        buf.encode::<Data>(v);
    }
    
    assert_eq!(buf, "name=%E5%BC%A0%E4%B8%89&speech=%C2%A1Ol%C3%A9%21");
    
    let uri = Uri::builder()
        .path(EStr::new(""))
        .query(&buf)
        .build();
    assert_eq!(uri.as_str(), "?name=%E5%BC%A0%E4%B8%89&speech=%C2%A1Ol%C3%A9%21");
    

Dependencies

~0.3–0.8MB
~19K SLoC