#uri #parser #builder #rfc3986

无 std fluent-uri

符合 RFC 3986 的完整功能 URI 引用处理库

12 个版本

0.2.0 2024 年 7 月 30 日
0.2.0-alpha.82024 年 7 月 2 日
0.2.0-alpha.72024 年 6 月 21 日
0.2.0-alpha.52024 年 4 月 17 日
0.1.2 2022 年 4 月 16 日

#158 in 编码

Download history 5047/week @ 2024-05-03 4518/week @ 2024-05-10 6667/week @ 2024-05-17 6746/week @ 2024-05-24 6957/week @ 2024-05-31 8668/week @ 2024-06-07 14248/week @ 2024-06-14 17127/week @ 2024-06-21 18011/week @ 2024-06-28 22224/week @ 2024-07-05 22907/week @ 2024-07-12 28093/week @ 2024-07-19 28196/week @ 2024-07-26 46193/week @ 2024-08-02 118541/week @ 2024-08-09 121289/week @ 2024-08-16

319,322 每月下载量
用于 45 个 crate (10 直接)

MIT 许可证

130KB
2.5K SLoC

fluent-uri

符合 RFC 3986 的完整功能 URI 引用处理库。

  • 快速: 零拷贝解析。经过基准测试,性能极高。[^bench-res]
  • 简单: 设计和文档精心。方便的百分比编码工具。
  • 正确: 禁止不安全代码。广泛针对其他实现进行模糊测试。

crates.io build license

文档 | 讨论

[^bench-res]: 在 基准测试 中,在 Intel Core i5-11300H 处理器上,fluent-uri 解析 URI 的时间为 49ns,而 iref 为 89ns,iri-string 为 135ns。

术语

一个 URI 引用 是一个 URI 或一个 相对引用。如果它以一个 方案(如 httpftpmailto 等)开头,后跟一个冒号(:),则它是一个 URI。例如,http://example.com/mailto:user@example.com 是 URI。否则,它是一个相对引用。例如,//example.org//index.html../foo?bar#baz 是相对引用。

示例

  • 从 URI 引用中解析和提取组件,零拷贝

    const SCHEME_FOO: &Scheme = Scheme::new_or_panic("foo");
    
    let uri_ref = UriRef::parse("foo://[email protected]:8042/over/there?name=ferret#nose")?;
    
    assert_eq!(uri_ref.scheme().unwrap(), SCHEME_FOO);
    
    let auth = uri_ref.authority().unwrap();
    assert_eq!(auth.as_str(), "[email protected]:8042");
    assert_eq!(auth.userinfo().unwrap(), "user");
    assert_eq!(auth.host(), "example.com");
    assert!(matches!(auth.host_parsed(), Host::RegName(name) if name == "example.com"));
    assert_eq!(auth.port().unwrap(), "8042");
    assert_eq!(auth.port_to_u16(), Ok(Some(8042)));
    
    assert_eq!(uri_ref.path(), "/over/there");
    assert_eq!(uri_ref.query().unwrap(), "name=ferret");
    assert_eq!(uri_ref.fragment().unwrap(), "nose");
    
  • 使用构建器模式构建 URI 引用

    const SCHEME_FOO: &Scheme = Scheme::new_or_panic("foo");
    
    let uri_ref = UriRef::builder()
        .scheme(SCHEME_FOO)
        .authority_with(|b| {
            b.userinfo(EStr::new_or_panic("user"))
                .host(EStr::new_or_panic("example.com"))
                .port(8042)
        })
        .path(EStr::new_or_panic("/over/there"))
        .query(EStr::new_or_panic("name=ferret"))
        .fragment(EStr::new_or_panic("nose"))
        .build()
        .unwrap();
    
    assert_eq!(
        uri_ref.as_str(),
        "foo://[email protected]:8042/over/there?name=ferret#nose"
    );
    
  • 相对于基 URI 解析 URI 引用

    let base = UriRef::parse("http://example.com/foo/bar")?;
    
    let uri_ref = UriRef::parse("baz")?;
    assert_eq!(uri_ref.resolve_against(&base).unwrap(), "http://example.com/foo/baz");
    let uri_ref = UriRef::parse("../baz")?;
    assert_eq!(uri_ref.resolve_against(&base).unwrap(), "http://example.com/baz");
    let uri_ref = UriRef::parse("?baz")?;
    assert_eq!(uri_ref.resolve_against(&base).unwrap(), "http://example.com/foo/bar?baz");
    
  • 规范化 URI 引用

    let uri_ref = UriRef::parse("eXAMPLE://a/./b/../b/%63/%7bfoo%7d")?;
    assert_eq!(uri_ref.normalize(), "example://a/b/c/%7Bfoo%7D");
    
  • EStr(百分比编码字符串切片)

    URI 中所有可能进行百分编码的组件都被解析为 EStr,这使得它们可以轻松地进行分割和解码。

    let s = "?name=%E5%BC%A0%E4%B8%89&speech=%C2%A1Ol%C3%A9%21";
    let query = UriRef::parse(s).unwrap().query().unwrap();
    let map: HashMap<_, _> = query
        .split('&')
        .map(|s| s.split_once('=').unwrap_or((s, EStr::EMPTY)))
        .map(|(k, v)| (k.decode().into_string_lossy(), v.decode().into_string_lossy()))
        .collect();
    assert_eq!(map["name"], "张三");
    assert_eq!(map["speech"], "¡Olé!");
    
  • EString(一个百分编码的可增长字符串)

    您可以将键值对编码成查询字符串,并使用它来构建一个 UriRef

    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_ref = UriRef::builder()
        .path(EStr::EMPTY)
        .query(&buf)
        .build()
        .unwrap();
    assert_eq!(uri_ref.as_str(), "?name=%E5%BC%A0%E4%B8%89&speech=%C2%A1Ol%C3%A9%21");
    

依赖关系

~265–770KB
~18K SLoC