chore: checkpoint before Python removal

This commit is contained in:
2026-03-26 22:33:59 +00:00
parent 683cec9307
commit e568ddf82a
29972 changed files with 11269302 additions and 2 deletions

576
vendor/iri-string/tests/build.rs vendored Normal file
View File

@@ -0,0 +1,576 @@
//! Tests for builder.
mod components;
#[macro_use]
mod utils;
use iri_string::build::Builder;
use iri_string::format::write_to_slice;
use iri_string::types::*;
use self::components::{Components, TestCase, TEST_CASES};
/// Pairs of components and composed IRI should be consistent.
///
/// This also (implicitly) tests that build-and-decompose and decompose-and-build
/// operations are identity conversions.
#[test]
fn consistent_components_and_composed() {
for case in TEST_CASES.iter().copied() {
let mut builder = Builder::new();
case.components.feed_builder(&mut builder, false);
// composed -> components.
let built = builder
.build::<IriReferenceStr>()
.expect("should be valid IRI reference");
assert_eq_display!(built, case.composed);
// components -> composed.
let composed = IriReferenceStr::new(case.composed).expect("should be valid IRI reference");
let scheme = composed.scheme_str();
let (user, password, host, port) = match composed.authority_components() {
None => (None, None, None, None),
Some(authority) => {
let (user, password) = match authority.userinfo() {
None => (None, None),
Some(userinfo) => match userinfo.find(':').map(|pos| userinfo.split_at(pos)) {
Some((user, password)) => (Some(user), Some(&password[1..])),
None => (Some(userinfo), None),
},
};
(user, password, Some(authority.host()), authority.port())
}
};
let path = composed.path_str();
let query = composed.query().map(|s| s.as_str());
let fragment = composed.fragment().map(|s| s.as_str());
let roundtrip_result = Components {
scheme,
user,
password,
host,
port,
path,
query,
fragment,
};
assert_eq!(roundtrip_result, case.components, "case={case:#?}");
}
}
fn assert_builds_for_case(case: &TestCase<'_>, builder: &Builder<'_>) {
if case.is_iri_class() {
{
let built = builder
.clone()
.build::<IriReferenceStr>()
.expect("should be valid IRI reference");
assert_eq_display!(built, case.composed);
}
{
let built = builder.clone().build::<IriStr>();
if case.is_absolute() {
let built = built.expect("should be valid IRI");
assert_eq_display!(built, case.composed);
} else {
assert!(built.is_err(), "should be invalid as IRI");
}
}
{
let built = builder.clone().build::<IriAbsoluteStr>();
if case.is_absolute_without_fragment() {
let built = built.expect("should be valid absolute IRI");
assert_eq_display!(built, case.composed);
} else {
assert!(built.is_err(), "should be invalid as absolute IRI");
}
}
{
let built = builder.clone().build::<IriRelativeStr>();
if case.is_relative() {
let built = built.expect("should be valid relative IRI reference");
assert_eq_display!(built, case.composed);
} else {
assert!(
built.is_err(),
"should be invalid as relative IRI reference"
);
}
}
}
if case.is_uri_class() {
{
let built = builder
.clone()
.build::<UriReferenceStr>()
.expect("should be valid URI reference");
assert_eq_display!(built, case.composed);
}
{
let built = builder.clone().build::<UriStr>();
if case.is_absolute() {
let built = built.expect("should be valid URI");
assert_eq_display!(built, case.composed);
} else {
assert!(built.is_err(), "should be invalid as URI");
}
}
{
let built = builder.clone().build::<UriAbsoluteStr>();
if case.is_absolute_without_fragment() {
let built = built.expect("should be valid absolute URI");
assert_eq_display!(built, case.composed);
} else {
assert!(built.is_err(), "should be invalid as absolute URI");
}
}
{
let built = builder.clone().build::<UriRelativeStr>();
if case.is_relative() {
let built = built.expect("should be valid relative URI reference");
assert_eq_display!(built, case.composed);
} else {
assert!(
built.is_err(),
"should be invalid as relative URI reference"
);
}
}
}
}
/// Build should succeed or fail, depending on the target syntax and the source string.
#[test]
fn build_simple() {
for case in TEST_CASES.iter() {
let mut builder = Builder::new();
case.components.feed_builder(&mut builder, false);
assert_builds_for_case(case, &builder);
}
}
/// Fields of a builder can be unset.
#[test]
fn reuse_dirty_builder() {
let dirty = {
let mut b = Builder::new();
b.scheme("scheme");
b.userinfo(("user", "password"));
b.host("host");
b.port("90127");
b.path("/path/path-again");
b.query("query");
b.fragment("fragment");
b
};
for case in TEST_CASES.iter() {
let mut builder = dirty.clone();
case.components.feed_builder(&mut builder, true);
assert_builds_for_case(case, &builder);
}
}
/// Builder can normalize absolute IRIs.
#[test]
fn build_normalized_absolute() {
for case in TEST_CASES.iter().filter(|case| case.is_absolute()) {
assert!(
!case.is_relative(),
"every IRI is absolute or relative, but not both"
);
let mut builder = Builder::new();
case.components.feed_builder(&mut builder, false);
builder.normalize();
let built_iri = builder
.clone()
.build::<IriStr>()
.expect("should be valid IRI reference");
assert_eq_display!(built_iri, case.normalized_iri, "case={case:#?}");
if case.is_uri_class() {
let built_uri = builder
.build::<UriStr>()
.expect("should be valid URI reference");
assert_eq_display!(built_uri, case.normalized_uri, "case={case:#?}");
}
}
}
/// Builder can normalize relative IRIs.
#[test]
fn build_normalized_relative() {
for case in TEST_CASES.iter().filter(|case| case.is_relative()) {
assert!(
!case.is_absolute(),
"every IRI is absolute or relative, but not both"
);
let mut builder = Builder::new();
case.components.feed_builder(&mut builder, false);
builder.normalize();
let built = builder
.clone()
.build::<IriRelativeStr>()
.expect("should be valid relative IRI reference");
assert_eq_display!(built, case.normalized_iri, "case={case:#?}");
if case.is_uri_class() {
let built_uri = builder
.build::<UriReferenceStr>()
.expect("should be valid relative URI reference");
assert_eq_display!(built_uri, case.normalized_uri, "case={case:#?}");
}
}
}
/// Build result can judge RFC3986-normalizedness correctly.
#[test]
fn build_normalizedness() {
for case in TEST_CASES.iter().filter(|case| case.is_absolute()) {
let mut builder = Builder::new();
case.components.feed_builder(&mut builder, false);
builder.normalize();
let built = builder
.clone()
.build::<IriStr>()
.expect("should be valid IRI reference");
let built_judge = built.ensure_rfc3986_normalizable().is_ok();
assert_eq!(
built_judge,
case.is_rfc3986_normalizable(),
"RFC3986-normalizedness should be correctly judged: case={case:#?}"
);
let mut buf = [0_u8; 512];
let s = write_to_slice(&mut buf, &built).expect("not enough buffer");
let built_slice = IriStr::new(s).expect("should be valid IRI reference");
assert!(
built_slice.is_normalized_but_authorityless_relative_path_preserved(),
"should be normalized"
);
let slice_judge = built_slice.is_normalized_rfc3986();
assert_eq!(
slice_judge, built_judge,
"RFC3986-normalizedness should be consistently judged: case={case:#?}"
);
}
}
/// `Builder::port` should accept `u8` value.
#[test]
fn set_port_u8() {
let mut builder = Builder::new();
builder.port(8_u8);
let built = builder
.clone()
.build::<UriReferenceStr>()
.expect("should be valid URI reference");
assert_eq_display!(built, "//:8", "should accept `u8`");
}
/// `Builder::port` should accept `u16` value.
#[test]
fn set_port_u16() {
let mut builder = Builder::new();
builder.port(65535_u16);
let built = builder
.clone()
.build::<UriReferenceStr>()
.expect("should be valid URI reference");
assert_eq_display!(built, "//:65535", "should accept `u16`");
}
/// `Builder::port` should accept `&str` value.
#[test]
fn set_port_str() {
let mut builder = Builder::new();
builder.port("8080");
let built = builder
.clone()
.build::<UriReferenceStr>()
.expect("should be valid URI reference");
assert_eq_display!(built, "//:8080", "should accept `&str`");
}
/// `Builder::port` should accept `&str` value even it is quite large.
#[test]
fn set_port_str_large() {
let mut builder = Builder::new();
builder.port("12345678901234567890");
let built = builder
.clone()
.build::<UriReferenceStr>()
.expect("should be valid URI reference");
assert_eq_display!(
built,
"//:12345678901234567890",
"should accept `&str` even it is quite large"
);
}
/// `Builder::ip_address` should accept `std::net::Ipv4Addr` value.
#[test]
#[cfg(feature = "std")]
fn set_ip_address_ipv4addr() {
let mut builder = Builder::new();
builder.ip_address(std::net::Ipv4Addr::new(192, 0, 2, 0));
let built = builder
.clone()
.build::<UriReferenceStr>()
.expect("should be valid URI reference");
assert_eq_display!(built, "//192.0.2.0", "should accept `std::net::Ipv4Addr`");
}
/// `Builder::ip_address` should accept `std::net::Ipv6Addr` value.
#[test]
#[cfg(feature = "std")]
fn set_ip_address_ipv6addr() {
let mut builder = Builder::new();
builder.ip_address(std::net::Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1));
let built = builder
.clone()
.build::<UriReferenceStr>()
.expect("should be valid URI reference");
assert_eq_display!(
built,
"//[2001:db8::1]",
"should accept `std::net::Ipv6Addr`"
);
}
/// `Builder::ip_address` should accept `std::net::IpAddr` value.
#[test]
#[cfg(feature = "std")]
fn set_ip_address_ipaddr() {
let mut builder = Builder::new();
builder.ip_address(std::net::IpAddr::V4(std::net::Ipv4Addr::new(192, 0, 2, 0)));
let built = builder
.clone()
.build::<UriReferenceStr>()
.expect("should be valid URI reference");
assert_eq_display!(built, "//192.0.2.0", "should accept `std::net::IpAddr`");
}
/// `Builder::userinfo` should accept `&str`.
#[test]
fn set_userinfo_str() {
let mut builder = Builder::new();
{
builder.userinfo("user:password");
let built = builder
.clone()
.build::<UriReferenceStr>()
.expect("should be valid URI reference");
assert_eq_display!(built, "//user:password@", "should accept `&str`");
}
{
builder.userinfo("arbitrary-valid-string");
let built = builder
.clone()
.build::<UriReferenceStr>()
.expect("should be valid URI reference");
assert_eq_display!(built, "//arbitrary-valid-string@", "should accept `&str`");
}
{
builder.userinfo("arbitrary:valid:string");
let built = builder
.clone()
.build::<UriReferenceStr>()
.expect("should be valid URI reference");
assert_eq_display!(built, "//arbitrary:valid:string@", "should accept `&str`");
}
}
/// `Builder::userinfo` should accept `(&str, &str)`.
#[test]
fn set_userinfo_pair_str_str() {
let mut builder = Builder::new();
{
builder.userinfo(("user", "password"));
let built = builder
.clone()
.build::<UriReferenceStr>()
.expect("should be valid URI reference");
assert_eq_display!(built, "//user:password@", "should accept `&str`");
}
{
builder.userinfo(("", ""));
let built = builder
.clone()
.build::<UriReferenceStr>()
.expect("should be valid URI reference");
assert_eq_display!(built, "//:@", "empty user and password should be preserved");
}
}
/// `Builder::userinfo` should accept `(&str, Option<&str>)`.
#[test]
fn set_userinfo_pair_str_optstr() {
let mut builder = Builder::new();
{
builder.userinfo(("user", Some("password")));
let built = builder
.clone()
.build::<UriReferenceStr>()
.expect("should be valid URI reference");
assert_eq_display!(
built,
"//user:password@",
"should accept `(&str, Option<&str>)`"
);
}
{
builder.userinfo(("", Some("")));
let built = builder
.clone()
.build::<UriReferenceStr>()
.expect("should be valid URI reference");
assert_eq_display!(built, "//:@", "empty user and password should be preserved");
}
{
builder.userinfo(("user", None));
let built = builder
.clone()
.build::<UriReferenceStr>()
.expect("should be valid URI reference");
assert_eq_display!(
built,
"//user@",
"password given as `None` should be absent"
);
}
}
/// Builder should reject a colon in user.
#[test]
fn user_with_colon() {
let mut builder = Builder::new();
builder.userinfo(("us:er", Some("password")));
let result = builder.clone().build::<UriReferenceStr>();
assert!(result.is_err(), "`user` part cannot have a colon");
}
/// Builder should be able to build a normalized IRI even when it requires
/// edge case handling of RFC 3986 normalization.
#[test]
fn normalize_double_slash_prefix() {
let mut builder = Builder::new();
builder.scheme("scheme");
builder.path("/..//bar");
builder.normalize();
let built = builder
.build::<IriStr>()
.expect("normalizable by `/.` path prefix");
// Naive application of RFC 3986 normalization/resolution algorithm
// results in `scheme://bar`, but this is unintentional. `bar` should be
// the second path segment, not a host. So this should be rejected.
assert!(
built.ensure_rfc3986_normalizable().is_err(),
"not normalizable by RFC 3986 algorithm"
);
// In contrast to RFC 3986, WHATWG URL Standard defines serialization
// algorithm and handles this case specially. In this case, the result
// is `scheme:/.//bar`, this won't be considered fully normalized from
// the RFC 3986 point of view, but more normalization would be
// impossible and this would practically work in most situations.
assert_eq_display!(built, "scheme:/.//bar");
}
/// Builder should be able to build a normalized IRI even when it requires
/// edge case handling of RFC 3986 normalization.
#[test]
fn absolute_double_slash_path_without_authority() {
let mut builder = Builder::new();
builder.scheme("scheme");
builder.path("//bar");
// Should fail without normalization.
{
let result = builder.clone().build::<IriStr>();
assert!(
result.is_err(),
"`scheme://bar` is unintended so the build should fail"
);
}
// With normalization, the build succeeds.
builder.normalize();
let built = builder
.build::<IriStr>()
.expect("normalizable by `/.` path prefix");
// Naive application of RFC 3986 normalization/resolution algorithm
// results in `scheme://bar`, but this is unintentional. `bar` should be
// the second path segment, not a host. So this should be rejected.
assert!(
built.ensure_rfc3986_normalizable().is_err(),
"not normalizable by RFC 3986 algorithm"
);
// In contrast to RFC 3986, WHATWG URL Standard defines serialization
// algorithm and handles this case specially. In this case, the result
// is `scheme:/.//bar`, this won't be considered fully normalized from
// the RFC 3986 point of view, but more normalization would be
// impossible and this would practically work in most situations.
assert_eq_display!(built, "scheme:/.//bar");
}
/// Authority requires the path to be empty or absolute (without normalization enabled).
#[test]
fn authority_and_relative_path() {
let mut builder = Builder::new();
builder.host("example.com");
builder.path("relative/path");
assert!(
builder.clone().build::<IriReferenceStr>().is_err(),
"authority requires the path to be empty or absolute"
);
// Even if normalization is enabled, the relative path is unacceptable.
builder.normalize();
assert!(
builder.build::<IriReferenceStr>().is_err(),
"authority requires the path to be empty or absolute"
);
}
#[test]
fn no_authority_and_double_slash_prefix_without_normalization() {
let mut builder = Builder::new();
// This would be interpreted as "network-path reference" (see RFC 3986
// section 4.2), so this should be rejected.
builder.path("//double-slash");
assert!(builder.build::<IriReferenceStr>().is_err());
}
#[test]
fn no_authority_and_double_slash_prefix_with_normalization() {
let mut builder = Builder::new();
builder.path("//double-slash");
builder.normalize();
let built = builder
.build::<IriReferenceStr>()
.expect("normalizable by `/.` path prefix");
assert_eq_display!(built, "/.//double-slash");
assert!(built.ensure_rfc3986_normalizable().is_err());
}
#[test]
fn no_authority_and_relative_first_segment_colon() {
let mut builder = Builder::new();
// This would be interpreted as scheme `foo` and host `bar`,
// so this should be rejected.
builder.path("foo:bar");
assert!(builder.clone().build::<IriReferenceStr>().is_err());
// Normalization does not change the situation.
builder.normalize();
assert!(builder.build::<IriReferenceStr>().is_err());
}

1771
vendor/iri-string/tests/components/mod.rs vendored Normal file

File diff suppressed because it is too large Load Diff

215
vendor/iri-string/tests/gh-issues.rs vendored Normal file
View File

@@ -0,0 +1,215 @@
//! Test cases for issues reported on GitHub.
#[macro_use]
mod utils;
use iri_string::types::UriReferenceStr;
mod issue_17 {
use super::*;
#[test]
fn ipv6_literal_authority_host() {
let uri = UriReferenceStr::new("//[::1]").expect("valid relative URI");
let authority = uri
.authority_components()
.expect("the URI has authority `[::1]`");
assert_eq!(authority.host(), "[::1]");
}
#[test]
fn extra_trailing_colon_in_ipv6_literal() {
assert!(UriReferenceStr::new("//[::1:]").is_err());
}
#[test]
fn ipvfuture_literal_capital_v() {
assert!(UriReferenceStr::new("//[v0.0]").is_ok());
assert!(UriReferenceStr::new("//[V0.0]").is_ok());
}
#[test]
fn ipvfuture_empty_part() {
assert!(
UriReferenceStr::new("//[v0.]").is_err(),
"address should not be empty"
);
assert!(
UriReferenceStr::new("//[v.0]").is_err(),
"version should not be empty"
);
assert!(
UriReferenceStr::new("//[v.]").is_err(),
"neither address nor version should be empty"
);
}
}
mod issue_36 {
use super::*;
#[cfg(feature = "alloc")]
use iri_string::format::ToDedicatedString;
use iri_string::types::UriAbsoluteStr;
// "/.//.".resolve_against("a:/")
// => "a:" + remove_dot_segments("/.//.")
//
// STEP OUTPUT BUFFER INPUT BUFFER
// 1 : /.//.
// 2B: //.
// 2E: / /.
// 2B: / /
// 2E: //
// (see RFC 3986 section 5.2.4 for this notation.)
//
// => "a://"
//
// However, this is invalid since it should be semantically
// `<scheme="a">:<path="//">` but this string will be parsed as
// `<scheme="a">://<path="">`. So, `./` should be inserted to break
// `//` at the beginning of the path part.
#[test]
fn abnormal_resolution() {
let base = UriAbsoluteStr::new("a:/").expect("valid absolute URI");
{
let relative = UriReferenceStr::new("/.//.").expect("valid relative URI");
let result = relative.resolve_against(base);
assert!(
result.ensure_rfc3986_normalizable().is_err(),
"strict RFC 3986 resolution should fail for base={:?}, ref={:?}",
base,
relative
);
assert_eq_display!(
result,
"a:/.//",
"resolution result will be modified using serialization by WHATWG URL Standard"
);
}
{
let relative = UriReferenceStr::new(".//.").expect("valid relative URI");
let result = relative.resolve_against(base);
assert!(
result.ensure_rfc3986_normalizable().is_err(),
"strict RFC 3986 resolution should fail for base={:?}, ref={:?}",
base,
relative
);
assert_eq_display!(
result,
"a:/.//",
"resolution result will be modified using serialization by WHATWG URL Standard"
);
}
}
#[test]
fn abnormal_normalization() {
let uri = UriAbsoluteStr::new("a:/.//.").expect("valid absolute URI");
let normalized = uri.normalize();
assert!(
normalized.ensure_rfc3986_normalizable().is_err(),
"strict RFC 3986 normalization should fail for uri={:?}",
uri
);
assert_eq_display!(
normalized,
"a:/.//",
"normalization result will be modified using serialization by WHATWG URL Standard"
);
#[cfg(feature = "alloc")]
{
assert!(
!normalized.to_dedicated_string().is_normalized_rfc3986(),
"not normalizable by strict RFC 3986 algorithm"
);
}
}
#[test]
fn abnormal_normalization2() {
{
let uri = UriAbsoluteStr::new("a:/bar//.").expect("valid absolute URI");
assert_eq_display!(uri.normalize(), "a:/bar//");
}
{
let uri = UriAbsoluteStr::new("a:/bar/..//.").expect("valid absolute URI");
assert_eq_display!(
uri.normalize(),
"a:/.//",
"normalization result will be modified using serialization by WHATWG URL Standard"
);
}
{
let uri = UriAbsoluteStr::new("a:/.//bar/.").expect("valid absolute URI");
assert_eq_display!(
uri.normalize(),
"a:/.//bar/",
"normalization result will be modified using serialization by WHATWG URL Standard"
);
}
{
let uri = UriAbsoluteStr::new("a:/././././././foo/./.././././././././././/.")
.expect("valid absolute URI");
assert_eq_display!(
uri.normalize(),
"a:/.//",
"normalization result will be modified using serialization by WHATWG URL Standard"
);
}
}
#[test]
fn normalization_pct_triplet_loss() {
let uri = UriAbsoluteStr::new("a://%92%99").expect("valid absolute URI");
assert_eq_display!(uri.normalize(), "a://%92%99");
// Other problems are found during fixing this bug. The test cases for
// them have been added to generic test case data source.
}
}
/// <https://github.com/lo48576/iri-string/pull/46>
#[cfg(feature = "alloc")]
mod issue_46 {
use iri_string::types::{UriFragmentStr, UriRelativeString};
#[test]
fn set_fragment_to_relative() {
let mut uri =
UriRelativeString::try_from("//user:password@example.com/path?query#frag.old")
.expect("valid relative URI");
assert_eq!(uri, "//user:password@example.com/path?query#frag.old");
assert_eq!(uri.fragment_str(), Some("frag.old"));
uri.set_fragment(None);
assert_eq!(uri, "//user:password@example.com/path?query");
assert_eq!(uri.fragment(), None);
let frag_new = UriFragmentStr::new("frag-new").expect("valid URI fragment");
uri.set_fragment(Some(frag_new));
assert_eq!(uri.fragment_str(), Some("frag-new"));
}
}
/// <https://github.com/lo48576/iri-string/issues/48>
mod issue_48 {
use super::*;
use iri_string::types::IriStr;
#[test]
fn percent_decode_invalid_utf8() {
let s = "a:%F9%A8%8E%80";
let iri = IriStr::new(s).expect("valid URI with percent encoded invalid UTF-8 bytes");
assert_eq_display!(
iri.normalize(),
s,
"percent-encoded invalid UTF-8 bytes should be left as is without being decoded"
);
}
}

95
vendor/iri-string/tests/iri.rs vendored Normal file
View File

@@ -0,0 +1,95 @@
//! Tests specific to IRIs (not URIs).
#[macro_use]
mod utils;
use iri_string::format::write_to_slice;
#[cfg(feature = "alloc")]
use iri_string::format::ToDedicatedString;
#[cfg(feature = "alloc")]
use iri_string::types::IriReferenceString;
use iri_string::types::{IriReferenceStr, UriReferenceStr};
#[derive(Debug, Clone, Copy)]
struct TestCase {
iri: &'static str,
uri: &'static str,
}
// `[(iri, uri)]`.
const CASES: &[TestCase] = &[
TestCase {
iri: "?alpha=\u{03B1}",
uri: "?alpha=%CE%B1",
},
TestCase {
iri: "?katakana-letter-i=\u{30A4}",
uri: "?katakana-letter-i=%E3%82%A4",
},
TestCase {
iri: "?sushi=\u{1f363}",
uri: "?sushi=%F0%9F%8D%A3",
},
];
#[test]
fn iri_to_uri() {
let mut buf = [0_u8; 256];
let mut buf2 = [0_u8; 256];
for case in CASES.iter().copied() {
let expected = UriReferenceStr::new(case.uri).expect("should be valid URI reference");
let iri = IriReferenceStr::new(case.iri).expect("should be valid URI reference");
let encoded = iri.encode_to_uri();
assert_eq_display!(encoded, expected);
let encoded_uri = write_to_slice(&mut buf, &encoded).expect("not enough buffer");
let encoded_uri = UriReferenceStr::new(encoded_uri).expect("should be valid URI reference");
assert_eq!(encoded_uri, expected);
let encoded_again = AsRef::<IriReferenceStr>::as_ref(encoded_uri).encode_to_uri();
assert_eq_display!(encoded_again, expected);
let encoded_again_uri =
write_to_slice(&mut buf2, &encoded_again).expect("not enough buffer");
let encoded_again_uri =
UriReferenceStr::new(encoded_again_uri).expect("should be valid URI reference");
assert_eq!(encoded_again_uri, expected);
}
}
#[cfg(feature = "alloc")]
#[test]
fn iri_to_uri_allocated() {
for case in CASES.iter().copied() {
let expected = UriReferenceStr::new(case.uri).expect("should be valid URI reference");
let iri = IriReferenceStr::new(case.iri).expect("should be valid URI reference");
let encoded = iri.encode_to_uri().to_dedicated_string();
assert_eq!(encoded, expected);
let encoded_again = AsRef::<IriReferenceStr>::as_ref(&encoded)
.encode_to_uri()
.to_dedicated_string();
assert_eq!(encoded_again, expected);
}
}
#[cfg(feature = "alloc")]
#[test]
fn iri_to_uri_inline() {
for case in CASES.iter().copied() {
let expected = UriReferenceStr::new(case.uri).expect("should be valid URI reference");
let mut iri =
IriReferenceString::try_from(case.iri).expect("should be valid URI reference");
iri.encode_to_uri_inline();
assert_eq!(iri, expected);
iri.encode_to_uri_inline();
assert_eq!(
iri, expected,
"``encode_to_uri_inline()` method should be idempotent"
);
}
}

218
vendor/iri-string/tests/normalize.rs vendored Normal file
View File

@@ -0,0 +1,218 @@
//! Tests for normalization.
mod components;
#[macro_use]
mod utils;
#[cfg(feature = "alloc")]
use iri_string::format::ToDedicatedString;
use iri_string::types::*;
use self::components::TEST_CASES;
/// Semantically different IRIs should not be normalized into the same IRI.
#[test]
fn different_iris() {
for case in TEST_CASES
.iter()
.filter(|case| !case.different_iris.is_empty())
{
let normalized = IriStr::new(case.normalized_iri).expect("should be valid IRI reference");
for other in case.different_iris.iter().copied() {
let other = IriStr::new(other).expect("should be valid IRI reference");
assert_ne!(
normalized, other,
"<{}> should not be normalized to <{other}>, case={case:#?}",
case.composed
);
}
}
}
/// Normalization should work for IRI.
#[test]
fn normalize_uri() {
for case in TEST_CASES
.iter()
.filter(|case| case.is_uri_class() && case.is_absolute())
{
let source = UriStr::new(case.composed).expect("should be valid URI");
let normalized = source.normalize();
let expected = UriStr::new(case.normalized_uri).expect("should be valid URI");
assert_eq_display!(normalized, expected, "case={case:#?}");
#[cfg(feature = "alloc")]
assert_eq!(normalized.to_string(), expected.as_str(), "case={case:#?}");
#[cfg(feature = "alloc")]
assert_eq!(normalized.to_dedicated_string(), expected, "case={case:#?}");
assert_eq!(
case.is_rfc3986_normalizable(),
normalized.ensure_rfc3986_normalizable().is_ok(),
"case={case:#?}"
);
}
}
/// Normalization should work for IRI.
#[test]
fn normalize_iri() {
for case in TEST_CASES
.iter()
.filter(|case| case.is_iri_class() && case.is_absolute())
{
let source = IriStr::new(case.composed).expect("should be valid IRI");
let normalized = source.normalize();
let expected = IriStr::new(case.normalized_iri).expect("should be valid IRI");
assert_eq_display!(normalized, expected, "case={case:#?}");
#[cfg(feature = "alloc")]
assert_eq!(normalized.to_string(), expected.as_str(), "case={case:#?}");
#[cfg(feature = "alloc")]
assert_eq!(normalized.to_dedicated_string(), expected, "case={case:#?}");
assert_eq!(
case.is_rfc3986_normalizable(),
normalized.ensure_rfc3986_normalizable().is_ok(),
"case={case:#?}"
);
}
}
/// WHATWG-like normalization should work for IRI.
#[test]
fn normalize_uri_whatwg_like() {
for case in TEST_CASES
.iter()
.filter(|case| case.is_uri_class() && case.is_absolute())
{
let source = UriStr::new(case.composed).expect("should be valid URI");
let normalized = source.normalize_but_preserve_authorityless_relative_path();
let expected = UriStr::new(
case.normalized_uri_whatwg_like
.unwrap_or(case.normalized_uri),
)
.expect("should be valid URI");
assert_eq_display!(normalized, expected, "case={case:#?}");
#[cfg(feature = "alloc")]
assert_eq!(normalized.to_string(), expected.as_str(), "case={case:#?}");
#[cfg(feature = "alloc")]
assert_eq!(normalized.to_dedicated_string(), expected, "case={case:#?}");
assert_eq!(
case.is_rfc3986_normalizable(),
normalized.ensure_rfc3986_normalizable().is_ok(),
"case={case:#?}"
);
}
}
/// WHATWG-like normalization should work for IRI.
#[test]
fn normalize_iri_whatwg_like() {
for case in TEST_CASES
.iter()
.filter(|case| case.is_iri_class() && case.is_absolute())
{
let source = IriStr::new(case.composed).expect("should be valid IRI");
let normalized = source.normalize_but_preserve_authorityless_relative_path();
let expected = IriStr::new(
case.normalized_iri_whatwg_like
.unwrap_or(case.normalized_iri),
)
.expect("should be valid IRI");
assert_eq_display!(normalized, expected, "case={case:#?}");
#[cfg(feature = "alloc")]
assert_eq!(normalized.to_string(), expected.as_str(), "case={case:#?}");
#[cfg(feature = "alloc")]
assert_eq!(normalized.to_dedicated_string(), expected, "case={case:#?}");
assert_eq!(
case.is_rfc3986_normalizable(),
normalized.ensure_rfc3986_normalizable().is_ok(),
"case={case:#?}"
);
}
}
/// Normalization should be idempotent.
#[test]
fn normalize_idempotent() {
let mut buf = [0_u8; 512];
for case in TEST_CASES
.iter()
.filter(|case| case.is_iri_class() && case.is_absolute())
{
let source = IriStr::new(case.composed).expect("should be valid IRI");
let normalized = source.normalize();
let expected = IriStr::new(case.normalized_iri).expect("should be valid IRI");
let normalized_s =
iri_string::format::write_to_slice(&mut buf, &normalized).expect("not enough buffer");
let normalized_s = IriStr::new(normalized_s).expect("should be valid IRI reference");
// Normalize again.
let normalized_again = normalized_s.normalize();
assert_eq_display!(normalized_again, expected, "case={case:#?}");
}
}
/// Normalizedness checks.
#[test]
fn normalizedness() {
#[derive(Debug, Clone, Copy)]
struct Case {
iri: &'static str,
is_normalized_default: bool,
is_normalized_rfc3986: bool,
is_normalized_whatwg_like: bool,
}
const CASES: &[Case] = &[
Case {
iri: "scheme:/.//foo",
is_normalized_default: true,
is_normalized_rfc3986: false,
is_normalized_whatwg_like: true,
},
Case {
iri: "scheme:.///foo",
is_normalized_default: false,
is_normalized_rfc3986: false,
is_normalized_whatwg_like: true,
},
Case {
iri: "scheme://authority/.//foo",
is_normalized_default: false,
is_normalized_rfc3986: false,
is_normalized_whatwg_like: false,
},
Case {
iri: "scheme:relative/..//foo",
is_normalized_default: false,
is_normalized_rfc3986: false,
is_normalized_whatwg_like: true,
},
];
for case in CASES {
let iri = IriStr::new(case.iri).expect("should be valid IRI");
assert_eq!(
iri.is_normalized(),
case.is_normalized_default,
"case={case:?}"
);
assert_eq!(
iri.is_normalized_rfc3986(),
case.is_normalized_rfc3986,
"case={case:?}"
);
assert_eq!(
iri.is_normalized_but_authorityless_relative_path_preserved(),
case.is_normalized_whatwg_like,
"case={case:?}"
);
}
}

View File

@@ -0,0 +1,174 @@
//! Tests for percent encoding.
#[cfg(feature = "alloc")]
extern crate alloc;
#[macro_use]
mod utils;
#[cfg(all(feature = "alloc", not(feature = "std")))]
use alloc::string::ToString;
use iri_string::percent_encode::{PercentEncodedForIri, PercentEncodedForUri};
#[test]
fn regname_uri() {
let encoded = PercentEncodedForUri::from_reg_name("alpha.\u{03B1}.reg.name");
let expected = "alpha.%CE%B1.reg.name";
assert_eq_display!(encoded, expected);
#[cfg(feature = "alloc")]
assert_eq!(encoded.to_string(), expected);
}
#[test]
fn regname_iri() {
let encoded = PercentEncodedForIri::from_reg_name("alpha.\u{03B1}.reg.name");
let expected = "alpha.\u{03B1}.reg.name";
assert_eq_display!(encoded, expected);
#[cfg(feature = "alloc")]
assert_eq!(encoded.to_string(), expected);
}
#[test]
fn path_segment_uri() {
let encoded = PercentEncodedForUri::from_path_segment("\u{03B1}/<alpha>?#");
let expected = "%CE%B1%2F%3Calpha%3E%3F%23";
assert_eq_display!(encoded, expected);
#[cfg(feature = "alloc")]
assert_eq!(encoded.to_string(), expected);
}
#[test]
fn path_segment_iri() {
let encoded = PercentEncodedForIri::from_path_segment("\u{03B1}/<alpha>?#");
let expected = "\u{03B1}%2F%3Calpha%3E%3F%23";
assert_eq_display!(encoded, expected);
#[cfg(feature = "alloc")]
assert_eq!(encoded.to_string(), expected);
}
#[test]
fn path_uri() {
let encoded = PercentEncodedForUri::from_path("\u{03B1}/<alpha>?#");
let expected = "%CE%B1/%3Calpha%3E%3F%23";
assert_eq_display!(encoded, expected);
#[cfg(feature = "alloc")]
assert_eq!(encoded.to_string(), expected);
}
#[test]
fn path_iri() {
let encoded = PercentEncodedForIri::from_path("\u{03B1}/<alpha>?#");
let expected = "\u{03B1}/%3Calpha%3E%3F%23";
assert_eq_display!(encoded, expected);
#[cfg(feature = "alloc")]
assert_eq!(encoded.to_string(), expected);
}
#[test]
fn query_uri() {
let encoded = PercentEncodedForUri::from_query("\u{03B1}/<alpha>?#");
let expected = "%CE%B1/%3Calpha%3E?%23";
assert_eq_display!(encoded, expected);
#[cfg(feature = "alloc")]
assert_eq!(encoded.to_string(), expected);
}
#[test]
fn query_iri() {
let encoded = PercentEncodedForIri::from_query("\u{03B1}/<alpha>?#");
let expected = "\u{03B1}/%3Calpha%3E?%23";
assert_eq_display!(encoded, expected);
#[cfg(feature = "alloc")]
assert_eq!(encoded.to_string(), expected);
}
#[test]
fn fragment_uri() {
let encoded = PercentEncodedForUri::from_fragment("\u{03B1}/<alpha>?#");
let expected = "%CE%B1/%3Calpha%3E?%23";
assert_eq_display!(encoded, expected);
#[cfg(feature = "alloc")]
assert_eq!(encoded.to_string(), expected);
}
#[test]
fn fragment_iri() {
let encoded = PercentEncodedForIri::from_fragment("\u{03B1}/<alpha>?#");
let expected = "\u{03B1}/%3Calpha%3E?%23";
assert_eq_display!(encoded, expected);
#[cfg(feature = "alloc")]
assert_eq!(encoded.to_string(), expected);
}
#[test]
fn unreserve_uri_unreserved() {
let encoded = PercentEncodedForUri::unreserve("%a0-._~\u{03B1}");
let expected = "%25a0-._~%CE%B1";
assert_eq_display!(encoded, expected);
#[cfg(feature = "alloc")]
assert_eq!(encoded.to_string(), expected);
}
#[test]
fn unreserve_iri_unreserved() {
let encoded = PercentEncodedForIri::unreserve("%a0-._~\u{03B1}");
let expected = "%25a0-._~\u{03B1}";
assert_eq_display!(encoded, expected);
#[cfg(feature = "alloc")]
assert_eq!(encoded.to_string(), expected);
}
#[test]
fn unreserve_uri_reserved() {
let encoded = PercentEncodedForUri::unreserve(":/?#[]@ !$&'()*+,;=");
let expected = "%3A%2F%3F%23%5B%5D%40%20%21%24%26%27%28%29%2A%2B%2C%3B%3D";
assert_eq_display!(encoded, expected);
#[cfg(feature = "alloc")]
assert_eq!(encoded.to_string(), expected);
}
#[test]
fn unreserve_iri_reserved() {
let encoded = PercentEncodedForIri::unreserve(":/?#[]@ !$&'()*+,;=");
let expected = "%3A%2F%3F%23%5B%5D%40%20%21%24%26%27%28%29%2A%2B%2C%3B%3D";
assert_eq_display!(encoded, expected);
#[cfg(feature = "alloc")]
assert_eq!(encoded.to_string(), expected);
}
#[test]
fn characters_uri_unreserved() {
let encoded = PercentEncodedForUri::characters("%a0-._~\u{03B1}");
let expected = "%25a0-._~%CE%B1";
assert_eq_display!(encoded, expected);
#[cfg(feature = "alloc")]
assert_eq!(encoded.to_string(), expected);
}
#[test]
fn characters_iri_unreserved() {
let encoded = PercentEncodedForIri::characters("%a0-._~\u{03B1}");
let expected = "%25a0-._~\u{03B1}";
assert_eq_display!(encoded, expected);
#[cfg(feature = "alloc")]
assert_eq!(encoded.to_string(), expected);
}
#[test]
fn characters_uri_reserved() {
let encoded = PercentEncodedForUri::characters(":/?#[]@ !$&'()*+,;=");
let expected = ":/?#[]@%20!$&'()*+,;=";
assert_eq_display!(encoded, expected);
#[cfg(feature = "alloc")]
assert_eq!(encoded.to_string(), expected);
}
#[test]
fn characters_iri_reserved() {
let encoded = PercentEncodedForIri::characters(":/?#[]@ !$&'()*+,;=");
let expected = ":/?#[]@%20!$&'()*+,;=";
assert_eq_display!(encoded, expected);
#[cfg(feature = "alloc")]
assert_eq!(encoded.to_string(), expected);
}

473
vendor/iri-string/tests/resolve.rs vendored Normal file
View File

@@ -0,0 +1,473 @@
//! Tests for IRI resolution.
mod components;
#[macro_use]
mod utils;
mod resolve_refimpl;
use iri_string::format::write_to_slice;
#[cfg(feature = "alloc")]
use iri_string::format::ToDedicatedString;
use iri_string::resolve::FixedBaseResolver;
use iri_string::types::*;
#[cfg(feature = "alloc")]
use self::resolve_refimpl::resolve as resolve_refimpl;
/// Test cases for strict resolvers.
// [(base, [(reference, output, Option<output_normalized>)])]
#[allow(clippy::type_complexity)]
const TEST_CASES: &[(&str, &[(&str, &str, Option<&str>)])] = &[
// RFC 3986, section 5.2.4.
("scheme:///a/b/c/./../../", &[("g", "scheme:///a/g", None)]),
("scheme:///a/b/c/./../", &[("../g", "scheme:///a/g", None)]),
("scheme:///a/b/c/./", &[("../../g", "scheme:///a/g", None)]),
("scheme:///a/b/c/", &[("./../../g", "scheme:///a/g", None)]),
("scheme:///a/b/", &[("c/./../../g", "scheme:///a/g", None)]),
("scheme:///a/", &[("b/c/./../../g", "scheme:///a/g", None)]),
("scheme:///", &[("a/b/c/./../../g", "scheme:///a/g", None)]),
("scheme:mid/content=5/../", &[("6", "scheme:mid/6", None)]),
("scheme:mid/content=5/", &[("../6", "scheme:mid/6", None)]),
("scheme:mid/", &[("content=5/../6", "scheme:mid/6", None)]),
("scheme:", &[("mid/content=5/../6", "scheme:mid/6", None)]),
// RFC 3986, section 5.4.1.
(
"http://a/b/c/d;p?q",
&[
("g:h", "g:h", None),
("g", "http://a/b/c/g", None),
("./g", "http://a/b/c/g", None),
("g/", "http://a/b/c/g/", None),
("/g", "http://a/g", None),
("//g", "http://g", None),
("?y", "http://a/b/c/d;p?y", None),
("g?y", "http://a/b/c/g?y", None),
("#s", "http://a/b/c/d;p?q#s", None),
("g#s", "http://a/b/c/g#s", None),
("g?y#s", "http://a/b/c/g?y#s", None),
(";x", "http://a/b/c/;x", None),
("g;x", "http://a/b/c/g;x", None),
("g;x?y#s", "http://a/b/c/g;x?y#s", None),
("", "http://a/b/c/d;p?q", None),
(".", "http://a/b/c/", None),
("./", "http://a/b/c/", None),
("..", "http://a/b/", None),
("../", "http://a/b/", None),
("../g", "http://a/b/g", None),
("../..", "http://a/", None),
("../../", "http://a/", None),
("../../g", "http://a/g", None),
],
),
// RFC 3986, section 5.4.2.
(
"http://a/b/c/d;p?q",
&[
("../../../g", "http://a/g", None),
("../../../../g", "http://a/g", None),
("/./g", "http://a/g", None),
("/../g", "http://a/g", None),
("g.", "http://a/b/c/g.", None),
(".g", "http://a/b/c/.g", None),
("g..", "http://a/b/c/g..", None),
("..g", "http://a/b/c/..g", None),
("./../g", "http://a/b/g", None),
("./g/.", "http://a/b/c/g/", None),
("g/./h", "http://a/b/c/g/h", None),
("g/../h", "http://a/b/c/h", None),
("g;x=1/./y", "http://a/b/c/g;x=1/y", None),
("g;x=1/../y", "http://a/b/c/y", None),
("g?y/./x", "http://a/b/c/g?y/./x", None),
("g?y/../x", "http://a/b/c/g?y/../x", None),
("g#s/./x", "http://a/b/c/g#s/./x", None),
("g#s/../x", "http://a/b/c/g#s/../x", None),
("http:g", "http:g", None),
],
),
// Custom cases.
(
"http://a/b/c/d/e/../..",
&[
// `/a/b/c/d/e/../..` but without dot segments removal.
("", "http://a/b/c/d/e/../..", Some("http://a/b/c/")),
// `/a/b/c/d/e/../..`
("..", "http://a/b/c/", None),
// `/a/b/c/d/e/../../`
("../", "http://a/b/c/", None),
// `/a/b/c/d/e/../.`
(".", "http://a/b/c/d/", None),
// `/a/b/c/d/e/.././`
("./", "http://a/b/c/d/", None),
// `/a/b/c/d/e/../..?query` but without dot segments removal.
(
"?query",
"http://a/b/c/d/e/../..?query",
Some("http://a/b/c/?query"),
),
// `/a/b/c/d/e/../..#frag` but without dot segments removal.
(
"#frag",
"http://a/b/c/d/e/../..#frag",
Some("http://a/b/c/#frag"),
),
// If the authority is specified, paths won't be merged.
("http://example.com", "http://example.com", None),
("http://example.com/", "http://example.com/", None),
// If the path of the reference is not empty, remove_dot_segments is applied.
("http://example.com/..", "http://example.com/", None),
// If the scheme is specified, paths won't be merged.
("scheme:", "scheme:", None),
("scheme:foo#frag", "scheme:foo#frag", None),
],
),
// Custom cases.
(
"https://a/b/c",
&[
("", "https://a/b/c", None),
("x/", "https://a/b/x/", None),
("x//", "https://a/b/x//", None),
("x///", "https://a/b/x///", None),
("x//y", "https://a/b/x//y", None),
("x//y/", "https://a/b/x//y/", None),
("x//y//", "https://a/b/x//y//", None),
// `/b/x//..//y//`.
// STEP OUTPUT BUFFER INPUT BUFFER
// 1 : /b/x//..//y//
// 2E: /b /x//..//y//
// 2E: /b/x //..//y//
// 2E: /b/x/ /..//y//
// 2C: /b/x //y//
// 2E: /b/x/ /y//
// 2E: /b/x//y //
// 2E: /b/x//y/ /
// 2E: /b/x//y//
("x//..//y//", "https://a/b/x//y//", None),
],
),
// Custom cases.
(
"scheme:a/b/c",
&[
("", "scheme:a/b/c", None),
("x/", "scheme:a/b/x/", None),
("x//", "scheme:a/b/x//", None),
("x///", "scheme:a/b/x///", None),
("x//y", "scheme:a/b/x//y", None),
("x//y/", "scheme:a/b/x//y/", None),
// `a/b/x//..//y//`.
// STEP OUTPUT BUFFER INPUT BUFFER
// 1 : a/b/x//..//y//
// 2E: a /b/x//..//y//
// 2E: a/b /x//..//y//
// 2E: a/b/x //..//y//
// 2E: a/b/x/ /..//y//
// 2C: a/b/x //y//
// 2E: a/b/x/ /y//
// 2E: a/b/x//y //
// 2E: a/b/x//y/ /
// 2E: a/b/x//y//
("x//..//y//", "scheme:a/b/x//y//", None),
],
),
// Custom cases.
(
"scheme:a",
&[
// `x/../..`.
// STEP OUTPUT BUFFER INPUT BUFFER
// 1 : x/../..
// 2E: x /../..
// 2C: /..
// 2C: /
// 2E: /
("x/../..", "scheme:/", None),
// `x/../../y`.
// STEP OUTPUT BUFFER INPUT BUFFER
// 1 : x/../../y
// 2E: x /../../y
// 2C: /../y
// 2C: /y
// 2E: /y
("x/../../y", "scheme:/y", None),
],
),
// Custom cases.
// Empty base path should be considered as `/` when the base authority is present.
(
"scheme://host",
&[
("", "scheme://host", None),
(".", "scheme://host/", None),
("..", "scheme://host/", None),
("foo", "scheme://host/foo", None),
],
),
// Custom cases.
(
"HTTP://USER:PASS@EXAMPLE.COM:80/1/2/3/4/.././5/../6/?QUERY",
&[(
"A/b/c/d/e/f/g/h/i/../../../j/k/l/../../../../m/n/./o",
"HTTP://USER:PASS@EXAMPLE.COM:80/1/2/3/6/A/b/c/d/e/m/n/o",
Some("http://USER:PASS@example.com:80/1/2/3/6/A/b/c/d/e/m/n/o"),
)],
),
(
"HTTP://USER:PASS@EXAMPLE.COM:/%7e/2/beta=%CE%B2/4/.././5/../6/",
&[(
"a/b/alpha=%CE%B1/d/e/f/g/h/i/../../../j/k/l/../../../../%3c/%7e/./%3e?query#fragment",
"HTTP://USER:PASS@EXAMPLE.COM:/%7e/2/beta=%CE%B2/6/a/b/alpha=%CE%B1/d/e/%3c/%7e/%3e?query#fragment",
Some("http://USER:PASS@example.com/~/2/beta=\u{03B2}/6/a/b/alpha=\u{03B1}/d/e/%3C/~/%3E?query#fragment")
)],
),
(
"http://user:pass@example.com:/%7e/2/beta=%ce%b2/4/.././5/../6/",
&[(
"a/b/alpha=%ce%b1/d/e/f/g/h/i/../../../j/k/l/../../../../%3c/%7e/./%3e?query#fragment",
"http://user:pass@example.com:/%7e/2/beta=%ce%b2/6/a/b/alpha=%ce%b1/d/e/%3c/%7e/%3e?query#fragment",
Some("http://user:pass@example.com/~/2/beta=\u{03B2}/6/a/b/alpha=\u{03B1}/d/e/%3C/~/%3E?query#fragment")
)],
),
];
#[test]
fn resolve() {
for (base, pairs) in TEST_CASES {
let base = IriAbsoluteStr::new(base).expect("should be valid base IRI");
for (target, expected, _normalized_expected) in *pairs {
let target = IriReferenceStr::new(target).expect("should be valid IRI reference");
let resolved = target.resolve_against(base);
assert_eq_display!(resolved, expected, "base={base:?}, target={target:?}");
#[cfg(feature = "alloc")]
assert_eq!(
resolved.to_dedicated_string().as_str(),
*expected,
"base={base:?}, target={target:?}"
);
}
}
}
#[test]
fn resolve_normalize() {
for (base, pairs) in TEST_CASES {
let base = IriAbsoluteStr::new(base).expect("should be valid base IRI");
for (target, expected, expected_normalized) in *pairs {
let target = IriReferenceStr::new(target).expect("should be valid IRI reference");
let resolved_normalized = target.resolve_against(base).and_normalize();
let expected = expected_normalized.unwrap_or(*expected);
assert_eq_display!(
resolved_normalized,
expected,
"base={base:?}, target={target:?}"
);
#[cfg(feature = "alloc")]
assert_eq!(
resolved_normalized.to_dedicated_string().as_str(),
expected,
"base={base:?}, target={target:?}"
);
}
}
}
#[test]
fn fixed_base_resolver() {
for (base, pairs) in TEST_CASES {
let base = IriAbsoluteStr::new(base).expect("should be valid base IRI");
let resolver = FixedBaseResolver::new(base);
for (target, expected, _normalized_expected) in *pairs {
let target = IriReferenceStr::new(target).expect("should be valid IRI reference");
let resolved = resolver.resolve(target);
assert_eq_display!(resolved, expected, "base={base:?}, target={target:?}");
#[cfg(feature = "alloc")]
assert_eq!(
resolved.to_dedicated_string().as_str(),
*expected,
"base={base:?}, target={target:?}"
);
}
}
}
#[cfg(feature = "alloc")]
#[test]
fn same_result_as_reference_impl() {
for (base, pairs) in TEST_CASES {
let base = IriAbsoluteStr::new(base).expect("should be valid base IRI");
for (target, expected, _normalized_expected) in *pairs {
let target = IriReferenceStr::new(target).expect("should be valid IRI reference");
let resolved = target.resolve_against(base).to_dedicated_string();
let expected_refimpl = resolve_refimpl(target, base);
assert_eq!(
*expected, expected_refimpl,
"base={base:?}, target={target:?}"
);
assert_eq!(
resolved, expected_refimpl,
"base={base:?}, target={target:?}"
);
}
}
}
#[test]
fn percent_encoded_dots() {
// [(base, ref, result)]
const TEST_CASES: &[(&str, &str, &str)] = &[
("scheme:", ".", "scheme:"),
("scheme:", "%2e", "scheme:"),
("scheme:", "%2E", "scheme:"),
("scheme://a", ".", "scheme://a/"),
("scheme://a", "%2e", "scheme://a/"),
("scheme://a", "%2E", "scheme://a/"),
("scheme://a/b/c", ".", "scheme://a/b/"),
("scheme://a/b/c", "%2e", "scheme://a/b/"),
("scheme://a/b/c", "%2E", "scheme://a/b/"),
("scheme://a/b/c", "./g", "scheme://a/b/g"),
("scheme://a/b/c", "%2e/g", "scheme://a/b/g"),
("scheme://a/b/c", "%2E/g", "scheme://a/b/g"),
("scheme://a/b/c/d/e/f", "../../../g", "scheme://a/b/g"),
(
"scheme://a/b/c/d/e/f",
"%2E%2E/%2E%2e/%2E./g",
"scheme://a/b/g",
),
(
"scheme://a/b/c/d/e/f",
"%2e%2E/%2e%2e/%2e./g",
"scheme://a/b/g",
),
("scheme://a/b/c/d/e/f", ".%2E/.%2e/../g", "scheme://a/b/g"),
];
for (base, reference, expected) in TEST_CASES {
let base = IriAbsoluteStr::new(base).expect("should be valid base IRI");
let reference = IriReferenceStr::new(reference).expect("should be valid IRI reference");
let resolved = reference.resolve_against(base);
assert_eq_display!(resolved, *expected);
#[cfg(feature = "alloc")]
assert_eq!(resolved.to_dedicated_string(), *expected);
}
}
#[test]
fn write_to_slice_dont_require_extra_capacity() {
let mut buf = [0_u8; 128];
for (base, pairs) in TEST_CASES {
let base = IriAbsoluteStr::new(base).expect("should be valid base IRI");
let resolver = FixedBaseResolver::new(base);
for (target, expected, _normalized_expected) in *pairs {
if expected.is_empty() {
continue;
}
let target = IriReferenceStr::new(target).expect("should be valid IRI reference");
let resolved = resolver.resolve(target);
let result_small = write_to_slice(&mut buf[..expected.len() - 1], &resolved);
assert!(result_small.is_err(), "should fail due to too small buffer");
let result_enough = write_to_slice(&mut buf[..expected.len()], &resolved);
assert!(result_enough.is_ok(), "buffer should have enough size");
assert_eq!(
result_enough.unwrap(),
*expected,
"correct result should be written"
);
}
}
}
#[test]
fn resolution_result_live_longer_than_fixed_base_resolver() {
let mut buf = [0_u8; 128];
let base = IriAbsoluteStr::new("http://example.com/").expect("should be valid base IRI");
let reference = IriReferenceStr::new("foo/bar").expect("should be valid IRI reference");
let resolved = {
let resolver = FixedBaseResolver::new(base);
resolver.resolve(reference)
};
// Note that `the result of `resolver.resolve()` is still alive here.
let result = write_to_slice(&mut buf, &resolved).expect("`buf` should have enough capacity");
assert_eq!(result, "http://example.com/foo/bar");
}
#[test]
fn uri_resolution_against_self_with_normalization() {
for case in components::TEST_CASES
.iter()
.filter(|case| case.is_uri_class() && case.is_absolute())
{
let reference = UriStr::new(case.composed).expect("should be valid URI");
let resolved_normalized = AsRef::<UriReferenceStr>::as_ref(reference)
.resolve_against(reference.to_absolute())
.and_normalize();
assert_eq_display!(resolved_normalized, case.normalized_uri, "case={case:#?}");
#[cfg(feature = "alloc")]
assert_eq!(
resolved_normalized.to_string(),
case.normalized_uri,
"case={case:#?}"
);
#[cfg(feature = "alloc")]
assert_eq!(
resolved_normalized.to_dedicated_string(),
case.normalized_uri,
"case={case:#?}"
);
assert_eq!(
case.is_rfc3986_normalizable(),
resolved_normalized.ensure_rfc3986_normalizable().is_ok(),
"case={case:#?}"
);
}
}
#[test]
fn iri_resolution_against_self_with_normalization() {
for case in components::TEST_CASES
.iter()
.filter(|case| case.is_iri_class() && case.is_absolute())
{
let reference = IriStr::new(case.composed).expect("should be valid IRI");
let resolved_normalized = AsRef::<IriReferenceStr>::as_ref(reference)
.resolve_against(reference.to_absolute())
.and_normalize();
assert_eq_display!(resolved_normalized, case.normalized_iri, "case={case:#?}");
#[cfg(feature = "alloc")]
assert_eq!(
resolved_normalized.to_string(),
case.normalized_iri,
"case={case:#?}"
);
#[cfg(feature = "alloc")]
assert_eq!(
resolved_normalized.to_dedicated_string(),
case.normalized_iri,
"case={case:#?}"
);
assert_eq!(
case.is_rfc3986_normalizable(),
resolved_normalized.ensure_rfc3986_normalizable().is_ok(),
"case={case:#?}"
);
}
}

View File

@@ -0,0 +1,179 @@
//! Reference implementation based on RFC 3986 section 5.
#![cfg(feature = "alloc")]
extern crate alloc;
use alloc::format;
#[cfg(not(feature = "std"))]
use alloc::string::String;
use iri_string::spec::Spec;
use iri_string::types::{RiAbsoluteStr, RiReferenceStr, RiString};
fn to_major_components<S: Spec>(
s: &RiReferenceStr<S>,
) -> (Option<&str>, Option<&str>, &str, Option<&str>, Option<&str>) {
(
s.scheme_str(),
s.authority_str(),
s.path_str(),
s.query().map(|s| s.as_str()),
s.fragment().map(|s| s.as_str()),
)
}
/// Resolves the relative IRI.
///
/// See <https://www.rfc-editor.org/rfc/rfc3986.html#section-5.2.2>.
pub(super) fn resolve<S: Spec>(
reference: &RiReferenceStr<S>,
base: &RiAbsoluteStr<S>,
) -> RiString<S> {
let (r_scheme, r_authority, r_path, r_query, r_fragment) = to_major_components(reference);
let (b_scheme, b_authority, b_path, b_query, _) = to_major_components(base.as_ref());
let t_scheme: &str;
let t_authority: Option<&str>;
let t_path: String;
let t_query: Option<&str>;
if let Some(r_scheme) = r_scheme {
t_scheme = r_scheme;
t_authority = r_authority;
t_path = remove_dot_segments(r_path.into());
t_query = r_query;
} else {
if r_authority.is_some() {
t_authority = r_authority;
t_path = remove_dot_segments(r_path.into());
t_query = r_query;
} else {
if r_path.is_empty() {
t_path = b_path.into();
if r_query.is_some() {
t_query = r_query;
} else {
t_query = b_query;
}
} else {
if r_path.starts_with('/') {
t_path = remove_dot_segments(r_path.into());
} else {
t_path = remove_dot_segments(merge(b_path, r_path, b_authority.is_some()));
}
t_query = r_query;
}
t_authority = b_authority;
}
t_scheme = b_scheme.expect("non-relative IRI must have a scheme");
}
let t_fragment: Option<&str> = r_fragment;
let s = recompose(t_scheme, t_authority, &t_path, t_query, t_fragment);
RiString::<S>::try_from(s).expect("resolution result must be a valid IRI")
}
/// Merges the two paths.
///
/// See <https://www.rfc-editor.org/rfc/rfc3986.html#section-5.2.3>.
fn merge(base_path: &str, ref_path: &str, base_authority_defined: bool) -> String {
if base_authority_defined && base_path.is_empty() {
format!("/{}", ref_path)
} else {
let base_path_end = base_path.rfind('/').map_or(0, |s| s + 1);
format!("{}{}", &base_path[..base_path_end], ref_path)
}
}
/// Removes dot segments from the path.
///
/// See <https://www.rfc-editor.org/rfc/rfc3986.html#section-5.2.4>.
fn remove_dot_segments(mut input: String) -> String {
let mut output = String::new();
while !input.is_empty() {
if input.starts_with("../") {
// 2A.
input.drain(..3);
} else if input.starts_with("./") {
// 2A.
input.drain(..2);
} else if input.starts_with("/./") {
// 2B.
input.replace_range(..3, "/");
} else if input == "/." {
// 2B.
input.replace_range(..2, "/");
} else if input.starts_with("/../") {
// 2C.
input.replace_range(..4, "/");
remove_last_segment_and_preceding_slash(&mut output);
} else if input == "/.." {
// 2C.
input.replace_range(..3, "/");
remove_last_segment_and_preceding_slash(&mut output);
} else if input == "." {
// 2D.
input.drain(..1);
} else if input == ".." {
// 2D.
input.drain(..2);
} else {
// 2E.
let first_seg_end = if let Some(after_slash) = input.strip_prefix('/') {
// `+1` is the length of the initial slash.
after_slash
.find('/')
.map_or_else(|| input.len(), |pos| pos + 1)
} else {
input.find('/').unwrap_or(input.len())
};
output.extend(input.drain(..first_seg_end));
}
}
output
}
/// Removes the last path segment and the preceding slash if any.
///
/// See <https://www.rfc-editor.org/rfc/rfc3986.html#section-5.2.4>,
/// step 2C.
fn remove_last_segment_and_preceding_slash(output: &mut String) {
match output.rfind('/') {
Some(slash_pos) => {
output.drain(slash_pos..);
}
None => output.clear(),
}
}
/// Recomposes the components.
///
/// See <https://www.rfc-editor.org/rfc/rfc3986.html#section-5.3>.
fn recompose(
scheme: &str,
authority: Option<&str>,
path: &str,
query: Option<&str>,
fragment: Option<&str>,
) -> String {
let mut result = String::new();
result.push_str(scheme);
result.push(':');
if let Some(authority) = authority {
result.push_str("//");
result.push_str(authority);
}
result.push_str(path);
if let Some(query) = query {
result.push('?');
result.push_str(query);
}
if let Some(fragment) = fragment {
result.push('#');
result.push_str(fragment);
}
result
}

99
vendor/iri-string/tests/serde.rs vendored Normal file
View File

@@ -0,0 +1,99 @@
//! Serde test.
#![cfg(feature = "serde")]
use serde_test::{assert_tokens, Token};
use iri_string::types::*;
mod utils;
macro_rules! define_tests {
($positive:ident, $negative:ident, ($spec:ident, $kind:ident), $slice:ty, $owned:ty,) => {
define_tests! {
@positive,
$positive,
($spec, $kind),
$slice,
$owned,
}
};
(@positive, $name:ident, ($spec:ident, $kind:ident), $slice:ty, $owned:ty,) => {
#[test]
fn $name() {
for raw in utils::positive(utils::Spec::$spec, utils::Kind::$kind) {
let s = <$slice>::new(raw).expect("Should not fail: valid string");
assert_tokens(&s, &[Token::BorrowedStr(raw)]);
#[cfg(all(feature = "serde", feature = "alloc"))]
{
let s = s.to_owned();
assert_tokens(&s, &[Token::BorrowedStr(raw)]);
}
}
}
};
}
define_tests! {
uri,
not_uri,
(Uri, Normal),
UriStr,
UriString,
}
define_tests! {
uri_absolute,
not_uri_absolute,
(Uri, Absolute),
UriAbsoluteStr,
UriAbsoluteString,
}
define_tests! {
uri_reference,
not_uri_reference,
(Uri, Reference),
UriReferenceStr,
UriReferenceString,
}
define_tests! {
uri_relative,
not_uri_relative,
(Uri, Relative),
UriRelativeStr,
UriRelativeString,
}
define_tests! {
iri,
not_iri,
(Iri, Normal),
IriStr,
IriString,
}
define_tests! {
iri_absolute,
not_iri_absolute,
(Iri, Absolute),
IriAbsoluteStr,
IriAbsoluteString,
}
define_tests! {
iri_reference,
not_iri_reference,
(Iri, Reference),
IriReferenceStr,
IriReferenceString,
}
define_tests! {
iri_relative,
not_iri_relative,
(Iri, Relative),
IriRelativeStr,
IriRelativeString,
}

View File

@@ -0,0 +1,342 @@
//! Conversions between types.
use iri_string::types::*;
fn assert_convertible<T>(source: &str)
where
T: ?Sized + PartialEq<str> + core::fmt::Debug,
for<'a> &'a T: TryFrom<&'a str>,
for<'a> <&'a T as TryFrom<&'a str>>::Error: core::fmt::Debug,
{
match <&T>::try_from(source) {
Ok(parsed) => assert_eq!(parsed, source),
Err(e) => panic!("should be convertible: source={:?}: {:?}", source, e),
}
}
fn assert_non_convertible<T>(source: &str)
where
T: ?Sized + PartialEq<str> + core::fmt::Debug,
for<'a> &'a T: TryFrom<&'a str>,
for<'a> <&'a T as TryFrom<&'a str>>::Error: core::fmt::Debug,
{
if let Ok(parsed) = <&T>::try_from(source) {
panic!(
"should not be convertible: source={:?}, parsed={:?}",
source, parsed
);
}
}
#[test]
fn rfc3986_uris_absolute_without_fragment() {
const URIS: &[&str] = &[
// RFC 3986 itself.
"https://www.rfc-editor.org/rfc/rfc3986.html",
"https://datatracker.ietf.org/doc/html/rfc3986",
// RFC 3986 section 1.1.2.
"ftp://ftp.is.co.za/rfc/rfc1808.txt",
"http://www.ietf.org/rfc/rfc2396.txt",
"ldap://[2001:db8::7]/c=GB?objectClass?one",
"mailto:John.Doe@example.com",
"news:comp.infosystems.www.servers.unix",
"tel:+1-816-555-1212",
"telnet://192.0.2.16:80/",
"urn:oasis:names:specification:docbook:dtd:xml:4.1.2",
// RFC 3986 section 3.
"urn:example:animal:ferret:nose",
// RFC 3986 section 3.3.
"mailto:fred@example.com",
"foo://info.example.com?fred",
// RFC 3986 section 5.4.
"http://a/b/c/d;p?q",
// RFC 3986 section 5.4.1.
"g:h",
"http://a/b/c/g",
"http://a/b/c/g/",
"http://a/g",
"http://g",
"http://a/b/c/d;p?y",
"http://a/b/c/g?y",
"http://a/b/c/;x",
"http://a/b/c/g;x",
"http://a/b/c/d;p?q",
"http://a/b/c/",
"http://a/b/",
"http://a/b/g",
"http://a/",
// RFC 3986 section 5.4.2.
"http://a/b/c/g.",
"http://a/b/c/.g",
"http://a/b/c/g..",
"http://a/b/c/..g",
"http://a/b/c/g/h",
"http://a/b/c/h",
"http://a/b/c/g;x=1/y",
"http://a/b/c/y",
"http://a/b/c/g?y/./x",
"http://a/b/c/g?y/../x",
// RFC 3986 section 6.2.2.
"example://a/b/c/%7Bfoo%7D",
"eXAMPLE://a/./b/../b/%63/%7bfoo%7d",
// RFC 3986 section 6.2.2.1.
"HTTP://www.EXAMPLE.com/",
"http://www.example.com/",
// RFC 3986 section 6.2.3.
"http://example.com",
"http://example.com/",
"http://example.com:/",
"http://example.com:80/",
"http://example.com/?",
"mailto:Joe@Example.COM",
"mailto:Joe@example.com",
// RFC 3986 section 6.2.4.
"http://example.com/data",
"http://example.com/data/",
"ftp://cnn.example.com&story=breaking_news@10.0.0.1/top_story.htm",
// RFC 3986 section Appendix C.
"http://www.w3.org/Addressing/",
"ftp://foo.example.com/rfc/",
// RFC 3987 itself.
"https://www.rfc-editor.org/rfc/rfc3987.html",
"https://datatracker.ietf.org/doc/html/rfc3987",
// RFC 3987 section 3.1.
"http://xn--rsum-bpad.example.org",
"http://r%C3%A9sum%C3%A9.example.org",
// RFC 3987 section 3.2.
"http://example.com/%F0%90%8C%80%F0%90%8C%81%F0%90%8C%82",
// RFC 3987 section 3.2.1.
"http://www.example.org/r%C3%A9sum%C3%A9.html",
"http://www.example.org/r%E9sum%E9.html",
"http://www.example.org/D%C3%BCrst",
"http://www.example.org/D%FCrst",
"http://xn--99zt52a.example.org/%e2%80%ae",
"http://xn--99zt52a.example.org/%E2%80%AE",
// RFC 3987 section 4.4.
"http://ab.CDEFGH.ij/kl/mn/op.html",
"http://ab.CDE.FGH/ij/kl/mn/op.html",
"http://AB.CD.ef/gh/IJ/KL.html",
"http://ab.cd.EF/GH/ij/kl.html",
"http://ab.CD.EF/GH/IJ/kl.html",
"http://ab.CDE123FGH.ij/kl/mn/op.html",
"http://ab.cd.ef/GH1/2IJ/KL.html",
"http://ab.cd.ef/GH%31/%32IJ/KL.html",
"http://ab.CDEFGH.123/kl/mn/op.html",
// RFC 3987 section 5.3.2.
"eXAMPLE://a/./b/../b/%63/%7bfoo%7d/ros%C3%A9",
// RFC 3987 section 5.3.2.1.
"HTTP://www.EXAMPLE.com/",
"http://www.example.com/",
// RFC 3987 section 5.3.2.3.
"http://example.org/~user",
"http://example.org/%7euser",
"http://example.org/%7Euser",
// RFC 3987 section 5.3.3.
"http://example.com",
"http://example.com/",
"http://example.com:/",
"http://example.com:80/",
//"http://xn--rsum-bpad.example.org", // duplicate
// RFC 3987 section 5.3.4.
"http://example.com/data",
"http://example.com/data/",
// RFC 3987 section 6.4.
//"http://www.example.org/r%C3%A9sum%C3%A9.html", // duplicate
//"http://www.example.org/r%E9sum%E9.html", // duplicate
];
for uri in URIS {
assert_convertible::<IriReferenceStr>(uri);
assert_convertible::<UriReferenceStr>(uri);
assert_convertible::<IriStr>(uri);
assert_convertible::<UriStr>(uri);
assert_convertible::<IriAbsoluteStr>(uri);
assert_convertible::<UriAbsoluteStr>(uri);
assert_non_convertible::<IriRelativeStr>(uri);
assert_non_convertible::<UriRelativeStr>(uri);
}
}
#[test]
fn rfc3986_uris_absolute_with_fragment() {
const URIS: &[&str] = &[
// RFC 3986 section 3.
"foo://example.com:8042/over/there?name=ferret#nose",
// RFC 3986 section 5.4.1.
"http://a/b/c/d;p?q#s",
"http://a/b/c/g#s",
"http://a/b/c/g?y#s",
"http://a/b/c/g;x?y#s",
// RFC 3986 section 5.4.2.
"http://a/b/c/g#s/./x",
"http://a/b/c/g#s/../x",
// RFC 3986 section Appendix B.
"http://www.ics.uci.edu/pub/ietf/uri/#Related",
// RFC 3986 section Appendix C.
"http://www.ics.uci.edu/pub/ietf/uri/historical.html#WARNING",
// RFC 3987 section 3.1.
"http://www.example.org/red%09ros%C3%A9#red",
// RFC 3987 section 4.4.
"http://AB.CD.EF/GH/IJ/KL?MN=OP;QR=ST#UV",
];
for uri in URIS {
assert_convertible::<IriReferenceStr>(uri);
assert_convertible::<UriReferenceStr>(uri);
assert_convertible::<IriStr>(uri);
assert_convertible::<UriStr>(uri);
assert_non_convertible::<IriAbsoluteStr>(uri);
assert_non_convertible::<UriAbsoluteStr>(uri);
assert_non_convertible::<IriRelativeStr>(uri);
assert_non_convertible::<UriRelativeStr>(uri);
}
}
#[test]
fn rfc3986_uris_relative() {
const URIS: &[&str] = &[
// RFC 3986 section 5.4.1.
"g",
"./g",
"g/",
"/g",
"//g",
"?y",
"g?y",
"#s",
"g#s",
"g?y#s",
";x",
"g;x",
"g;x?y#s",
"",
".",
"./",
"..",
"../",
"../g",
"../..",
"../../",
"../../g",
// RFC 3986 section 5.4.2.
"/./g",
"/../g",
"g.",
".g",
"g..",
"..g",
"./../g",
"./g/.",
"g/./h",
"g/../h",
"g;x=1/./y",
"g;x=1/../y",
"g?y/./x",
"g?y/../x",
"g#s/./x",
"g#s/../x",
];
for uri in URIS {
assert_convertible::<IriReferenceStr>(uri);
assert_convertible::<UriReferenceStr>(uri);
assert_non_convertible::<IriStr>(uri);
assert_non_convertible::<UriStr>(uri);
assert_non_convertible::<IriAbsoluteStr>(uri);
assert_non_convertible::<UriAbsoluteStr>(uri);
assert_convertible::<IriRelativeStr>(uri);
assert_convertible::<UriRelativeStr>(uri);
}
}
#[test]
fn rfc3987_iris_absolute_without_fragment() {
const URIS: &[&str] = &[
// RFC 3987 section 3.1.
"http://r\u{E9}sum\u{E9}.example.org",
// RFC 3987 section 3.2.
"http://example.com/\u{10300}\u{10301}\u{10302}",
"http://www.example.org/D\u{FC}rst",
"http://\u{7D0D}\u{8C46}.example.org/%E2%80%AE",
// RFC 3987 section 5.2.
"http://example.org/ros\u{E9}",
// RFC 3987 section 5.3.2.
"example://a/b/c/%7Bfoo%7D/ros\u{E9}",
// RFC 3987 section 5.3.2.2.
"http://www.example.org/r\u{E9}sum\u{E9}.html",
"http://www.example.org/re\u{301}sume\u{301}.html",
// RFC 3987 section 5.3.3.
//"http://r\u{E9}sum\u{E9}.example.org", // duplicate
// RFC 3987 section 6.4.
//"http://www.example.org/r\u{E9}sum\u{E9}.html", // duplicate
];
for uri in URIS {
assert_convertible::<IriReferenceStr>(uri);
assert_non_convertible::<UriReferenceStr>(uri);
assert_convertible::<IriStr>(uri);
assert_non_convertible::<UriStr>(uri);
assert_convertible::<IriAbsoluteStr>(uri);
assert_non_convertible::<UriAbsoluteStr>(uri);
assert_non_convertible::<IriRelativeStr>(uri);
assert_non_convertible::<UriRelativeStr>(uri);
}
}
#[test]
fn rfc3987_iris_absolute_with_fragment() {
const URIS: &[&str] = &[
// RFC 3987 section 6.4.
"http://www.example.org/r%E9sum%E9.xml#r\u{E9}sum\u{E9}",
];
for uri in URIS {
assert_convertible::<IriReferenceStr>(uri);
assert_non_convertible::<UriReferenceStr>(uri);
assert_convertible::<IriStr>(uri);
assert_non_convertible::<UriStr>(uri);
assert_non_convertible::<IriAbsoluteStr>(uri);
assert_non_convertible::<UriAbsoluteStr>(uri);
assert_non_convertible::<IriRelativeStr>(uri);
assert_non_convertible::<UriRelativeStr>(uri);
}
}
#[test]
fn test_invalid_char() {
const URIS: &[&str] = &[
"##", // Fragment cannot have `#`.
"<", // `<` cannot appear in an IRI reference.
">", // `>` cannot appear in an IRI reference.
];
for uri in URIS {
assert_non_convertible::<IriReferenceStr>(uri);
assert_non_convertible::<UriReferenceStr>(uri);
assert_non_convertible::<IriStr>(uri);
assert_non_convertible::<UriStr>(uri);
assert_non_convertible::<IriAbsoluteStr>(uri);
assert_non_convertible::<UriAbsoluteStr>(uri);
assert_non_convertible::<IriRelativeStr>(uri);
assert_non_convertible::<UriRelativeStr>(uri);
}
}
#[test]
fn invalid_percent_encoding() {
const URIS: &[&str] = &["%", "%0", "%0g", "%f", "%fg", "%g", "%g0", "%gf", "%gg"];
for uri in URIS {
assert_non_convertible::<IriReferenceStr>(uri);
assert_non_convertible::<UriReferenceStr>(uri);
assert_non_convertible::<IriStr>(uri);
assert_non_convertible::<UriStr>(uri);
assert_non_convertible::<IriAbsoluteStr>(uri);
assert_non_convertible::<UriAbsoluteStr>(uri);
assert_non_convertible::<IriRelativeStr>(uri);
assert_non_convertible::<UriRelativeStr>(uri);
}
}
#[test]
fn compare_different_types()
where
UriAbsoluteStr: PartialEq<IriReferenceStr>,
IriReferenceStr: PartialEq<UriAbsoluteStr>,
IriAbsoluteStr: PartialEq<UriReferenceStr>,
UriReferenceStr: PartialEq<IriAbsoluteStr>,
{
}

404
vendor/iri-string/tests/template.rs vendored Normal file
View File

@@ -0,0 +1,404 @@
//! Tests for URI template.
#![cfg(feature = "alloc")]
#[macro_use]
mod utils;
use std::cell::Cell;
use iri_string::spec::UriSpec;
use iri_string::template::context::{Context, DynamicContext, Visitor};
use iri_string::template::simple_context::{SimpleContext, Value};
use iri_string::template::UriTemplateStr;
/// Returns the context used by examples in RFC 6570 section 3.2.
fn rfc6570_context() -> SimpleContext {
let mut ctx = SimpleContext::new();
ctx.insert(
"count",
Value::List(vec!["one".to_owned(), "two".to_owned(), "three".to_owned()]),
);
ctx.insert(
"dom",
Value::List(vec!["example".to_owned(), "com".to_owned()]),
);
ctx.insert("dub", Value::String("me/too".to_owned()));
ctx.insert("hello", Value::String("Hello World!".to_owned()));
ctx.insert("half", Value::String("50%".to_owned()));
ctx.insert("var", Value::String("value".to_owned()));
ctx.insert("who", Value::String("fred".to_owned()));
ctx.insert("base", Value::String("http://example.com/home/".to_owned()));
ctx.insert("path", Value::String("/foo/bar".to_owned()));
ctx.insert(
"list",
Value::List(vec![
"red".to_owned(),
"green".to_owned(),
"blue".to_owned(),
]),
);
ctx.insert(
"keys",
Value::Assoc(vec![
("semi".to_owned(), ";".to_owned()),
("dot".to_owned(), ".".to_owned()),
("comma".to_owned(), ",".to_owned()),
]),
);
ctx.insert("v", Value::String("6".to_owned()));
ctx.insert("x", Value::String("1024".to_owned()));
ctx.insert("y", Value::String("768".to_owned()));
ctx.insert("empty", Value::String("".to_owned()));
ctx.insert("empty_keys", Value::Assoc(vec![]));
ctx.insert("undef", Value::Undefined);
ctx
}
/// Expression and expected expansion.
const SUCCESS_CASES: &[(&str, &str)] = &[
// Section 3.2.1. Variable Expansion.
("{count}", "one,two,three"),
("{count*}", "one,two,three"),
("{/count}", "/one,two,three"),
("{/count*}", "/one/two/three"),
("{;count}", ";count=one,two,three"),
("{;count*}", ";count=one;count=two;count=three"),
("{?count}", "?count=one,two,three"),
("{?count*}", "?count=one&count=two&count=three"),
("{&count*}", "&count=one&count=two&count=three"),
// Section 3.2.2. Simple String Expansion.
("{var}", "value"),
("{hello}", "Hello%20World%21"),
("{half}", "50%25"),
("O{empty}X", "OX"),
("O{undef}X", "OX"),
("{x,y}", "1024,768"),
("{x,hello,y}", "1024,Hello%20World%21,768"),
("?{x,empty}", "?1024,"),
("?{x,undef}", "?1024"),
("?{undef,y}", "?768"),
("{var:3}", "val"),
("{var:30}", "value"),
("{list}", "red,green,blue"),
("{list*}", "red,green,blue"),
("{keys}", "semi,%3B,dot,.,comma,%2C"),
("{keys*}", "semi=%3B,dot=.,comma=%2C"),
// Section 3.2.3. Reserved Expansion.
("{+var}", "value"),
("{+hello}", "Hello%20World!"),
("{+half}", "50%25"),
("{base}index", "http%3A%2F%2Fexample.com%2Fhome%2Findex"),
("{+base}index", "http://example.com/home/index"),
("O{+empty}X", "OX"),
("O{+undef}X", "OX"),
("{+path}/here", "/foo/bar/here"),
("here?ref={+path}", "here?ref=/foo/bar"),
("up{+path}{var}/here", "up/foo/barvalue/here"),
("{+x,hello,y}", "1024,Hello%20World!,768"),
("{+path,x}/here", "/foo/bar,1024/here"),
("{+path:6}/here", "/foo/b/here"),
("{+list}", "red,green,blue"),
("{+list*}", "red,green,blue"),
("{+keys}", "semi,;,dot,.,comma,,"),
("{+keys*}", "semi=;,dot=.,comma=,"),
// Section 3.2.4. Fragment Expansion.
("{#var}", "#value"),
("{#hello}", "#Hello%20World!"),
("{#half}", "#50%25"),
("foo{#empty}", "foo#"),
("foo{#undef}", "foo"),
("{#x,hello,y}", "#1024,Hello%20World!,768"),
("{#path,x}/here", "#/foo/bar,1024/here"),
("{#path:6}/here", "#/foo/b/here"),
("{#list}", "#red,green,blue"),
("{#list*}", "#red,green,blue"),
("{#keys}", "#semi,;,dot,.,comma,,"),
("{#keys*}", "#semi=;,dot=.,comma=,"),
// Section 3.2.5. Label Expansion with Dot-Prefix.
("{.who}", ".fred"),
("{.who,who}", ".fred.fred"),
("{.half,who}", ".50%25.fred"),
("www{.dom*}", "www.example.com"),
("X{.var}", "X.value"),
("X{.empty}", "X."),
("X{.undef}", "X"),
("X{.var:3}", "X.val"),
("X{.list}", "X.red,green,blue"),
("X{.list*}", "X.red.green.blue"),
("X{.keys}", "X.semi,%3B,dot,.,comma,%2C"),
("X{.keys*}", "X.semi=%3B.dot=..comma=%2C"),
("X{.empty_keys}", "X"),
("X{.empty_keys*}", "X"),
// Section 3.2.6. Path Segment Expansion.
("{/who}", "/fred"),
("{/who,who}", "/fred/fred"),
("{/half,who}", "/50%25/fred"),
("{/who,dub}", "/fred/me%2Ftoo"),
("{/var}", "/value"),
("{/var,empty}", "/value/"),
("{/var,undef}", "/value"),
("{/var,x}/here", "/value/1024/here"),
("{/var:1,var}", "/v/value"),
("{/list}", "/red,green,blue"),
("{/list*}", "/red/green/blue"),
("{/list*,path:4}", "/red/green/blue/%2Ffoo"),
("{/keys}", "/semi,%3B,dot,.,comma,%2C"),
("{/keys*}", "/semi=%3B/dot=./comma=%2C"),
// Section 3.2.7. Path-Style Parameter Expansion.
("{;who}", ";who=fred"),
("{;half}", ";half=50%25"),
("{;empty}", ";empty"),
("{;v,empty,who}", ";v=6;empty;who=fred"),
("{;v,bar,who}", ";v=6;who=fred"),
("{;x,y}", ";x=1024;y=768"),
("{;x,y,empty}", ";x=1024;y=768;empty"),
("{;x,y,undef}", ";x=1024;y=768"),
("{;hello:5}", ";hello=Hello"),
("{;list}", ";list=red,green,blue"),
("{;list*}", ";list=red;list=green;list=blue"),
("{;keys}", ";keys=semi,%3B,dot,.,comma,%2C"),
("{;keys*}", ";semi=%3B;dot=.;comma=%2C"),
// Section 3.2.8. Form-Style Query Expansion.
("{?who}", "?who=fred"),
("{?half}", "?half=50%25"),
("{?x,y}", "?x=1024&y=768"),
("{?x,y,empty}", "?x=1024&y=768&empty="),
("{?x,y,undef}", "?x=1024&y=768"),
("{?var:3}", "?var=val"),
("{?list}", "?list=red,green,blue"),
("{?list*}", "?list=red&list=green&list=blue"),
("{?keys}", "?keys=semi,%3B,dot,.,comma,%2C"),
("{?keys*}", "?semi=%3B&dot=.&comma=%2C"),
// Section 3.2.9. Form-Style Query Continuation.
("{&who}", "&who=fred"),
("{&half}", "&half=50%25"),
("?fixed=yes{&x}", "?fixed=yes&x=1024"),
("{&x,y,empty}", "&x=1024&y=768&empty="),
("{&x,y,undef}", "&x=1024&y=768"),
("{&var:3}", "&var=val"),
("{&list}", "&list=red,green,blue"),
("{&list*}", "&list=red&list=green&list=blue"),
("{&keys}", "&keys=semi,%3B,dot,.,comma,%2C"),
("{&keys*}", "&semi=%3B&dot=.&comma=%2C"),
];
/// Tests for examples in RFC 6570 section 3.2.
#[test]
fn rfc6570_section3_2() {
let context = rfc6570_context();
for (template, expected) in SUCCESS_CASES {
let template = UriTemplateStr::new(template).expect("must be valid template");
let expanded = template
.expand::<UriSpec, _>(&context)
.expect("must not have variable type error");
assert_eq_display!(expanded, expected, "template={template:?}");
assert_eq!(expanded.to_string(), *expected, "template={template:?}");
}
}
#[test]
fn prefix_modifier_for_percent_encoded_content() {
let mut context = SimpleContext::new();
context.insert("abcdef", "%61%62%63%64%65%66");
// `%CE`, `%CE%B1`, `%B1`, `%CE`, `%CE%B2`, `%B2`.
context.insert("invalid1", "%CE%CE%B1%B1%CE%CE%B2%B2");
// Each `%ff` is considered as an independent "character".
context.insert("invalid2", "%ff%ff%ff%ff%ff%ff");
// `&[(template, expected)]`.
const CASES: &[(&str, &str)] = &[
("{abcdef:4}", "%2561%25"),
("{+abcdef:4}", "%61%62%63%64"),
("{invalid1:2}", "%25C"),
("{invalid1:4}", "%25CE%25"),
("{+invalid1:2}", "%CE%CE%B1"),
("{+invalid1:4}", "%CE%CE%B1%B1%CE"),
("{invalid2:2}", "%25f"),
("{invalid2:4}", "%25ff%25"),
("{+invalid2:2}", "%ff%ff"),
("{+invalid2:4}", "%ff%ff%ff%ff"),
];
for (template, expected) in CASES {
let template = UriTemplateStr::new(template).expect("must be valid template");
let expanded = template
.expand::<UriSpec, _>(&context)
.expect("must not have variable type error");
assert_eq_display!(expanded, *expected, "template={template:?}");
assert_eq!(expanded.to_string(), *expected, "template={template:?}");
let expanded_dynamic = template
.expand_dynamic_to_string::<UriSpec, _>(&mut context.clone())
.expect("must not have variable type error");
assert_eq!(
expanded_dynamic, *expected,
"dynamic, template={template:?}"
);
}
}
#[test]
fn incomplete_percent_encode() {
let mut context = SimpleContext::new();
context.insert("incomplete1", "%ce%b1%");
context.insert("incomplete2", "%ce%b1%c");
context.insert("incomplete3", "%ce%b1%ce");
// `&[(template, expected)]`.
const CASES: &[(&str, &str)] = &[
("{incomplete1:1}", "%25"),
("{incomplete1:2}", "%25c"),
("{incomplete1:3}", "%25ce"),
("{incomplete1:4}", "%25ce%25"),
("{+incomplete1:1}", "%ce%b1"),
("{+incomplete1:2}", "%ce%b1%25"),
("{+incomplete2:1}", "%ce%b1"),
("{+incomplete2:2}", "%ce%b1%25"),
("{+incomplete2:3}", "%ce%b1%25c"),
("{+incomplete3:1}", "%ce%b1"),
("{+incomplete3:2}", "%ce%b1%ce"),
("{+incomplete3:3}", "%ce%b1%ce"),
];
for (template, expected) in CASES {
let template = UriTemplateStr::new(template).expect("must be valid template");
let expanded = template
.expand::<UriSpec, _>(&context)
.expect("must not have variable type error");
assert_eq_display!(expanded, *expected, "template={template:?}");
assert_eq!(expanded.to_string(), *expected, "template={template:?}");
let expanded_dynamic = template
.expand_dynamic_to_string::<UriSpec, _>(&mut context.clone())
.expect("must not have variable type error");
assert_eq!(
expanded_dynamic, *expected,
"dynamic, template={template:?}"
);
}
}
#[test]
fn fragmented_write() {
use core::fmt;
#[derive(Clone)]
enum Foo {
Incomplete1,
Incomplete2,
Incomplete3,
}
impl fmt::Display for Foo {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use core::fmt::Write;
f.write_char('%')?;
f.write_char('c')?;
f.write_char('e')?;
f.write_char('%')?;
f.write_char('b')?;
f.write_char('1')?;
f.write_char('%')?;
match self {
Foo::Incomplete1 => {}
Foo::Incomplete2 => {
f.write_char('c')?;
}
Foo::Incomplete3 => {
f.write_char('c')?;
f.write_char('e')?;
}
}
Ok(())
}
}
#[derive(Clone)]
struct MyContext {
incomplete1: Foo,
incomplete2: Foo,
incomplete3: Foo,
}
impl Context for MyContext {
fn visit<V: Visitor>(&self, visitor: V) -> V::Result {
let name = visitor.var_name().as_str();
match name {
"incomplete1" => visitor.visit_string(&self.incomplete1),
"incomplete2" => visitor.visit_string(&self.incomplete2),
"incomplete3" => visitor.visit_string(&self.incomplete3),
_ => visitor.visit_undefined(),
}
}
}
let context = MyContext {
incomplete1: Foo::Incomplete1,
incomplete2: Foo::Incomplete2,
incomplete3: Foo::Incomplete3,
};
// `&[(template, expected)]`.
const CASES: &[(&str, &str)] = &[
("{incomplete1:1}", "%25"),
("{incomplete1:2}", "%25c"),
("{incomplete1:3}", "%25ce"),
("{incomplete1:4}", "%25ce%25"),
("{+incomplete1:1}", "%ce%b1"),
("{+incomplete1:2}", "%ce%b1%25"),
("{+incomplete2:1}", "%ce%b1"),
("{+incomplete2:2}", "%ce%b1%25"),
("{+incomplete2:3}", "%ce%b1%25c"),
("{+incomplete3:1}", "%ce%b1"),
("{+incomplete3:2}", "%ce%b1%ce"),
("{+incomplete3:3}", "%ce%b1%ce"),
];
for (template, expected) in CASES {
let template = UriTemplateStr::new(template).expect("must be valid template");
let expanded = template
.expand::<UriSpec, _>(&context)
.expect("must not have variable type error");
assert_eq_display!(expanded, *expected, "template={template:?}");
assert_eq!(expanded.to_string(), *expected, "template={template:?}");
let expanded_dynamic = template
.expand_dynamic_to_string::<UriSpec, _>(&mut context.clone())
.expect("must not have variable type error");
assert_eq!(
expanded_dynamic, *expected,
"dynamic, template={template:?}"
);
}
}
#[test]
fn github_issue_39() {
#[derive(Default)]
struct MyContext {
on_expansion_start_called: Cell<bool>,
on_expansion_end_called: Cell<bool>,
}
impl DynamicContext for MyContext {
fn visit_dynamic<V: Visitor>(&mut self, visitor: V) -> V::Result {
visitor.visit_undefined()
}
fn on_expansion_start(&mut self) {
self.on_expansion_start_called.set(true);
}
fn on_expansion_end(&mut self) {
self.on_expansion_end_called.set(true);
}
}
let mut dyctx = MyContext::default();
let template = UriTemplateStr::new("hello/{world}").expect("valid template string");
let s = template
.expand_dynamic_to_string::<UriSpec, _>(&mut dyctx)
.expect("must not have variable type error");
assert_eq!(s, "hello/");
assert!(dyctx.on_expansion_start_called.get());
assert!(dyctx.on_expansion_end_called.get());
}

212
vendor/iri-string/tests/utils/mod.rs vendored Normal file
View File

@@ -0,0 +1,212 @@
//! Utilities.
#![allow(dead_code)]
use core::fmt;
use RawKind::*;
/// Raw kind (exclusive).
#[derive(Clone, Copy, PartialEq, Eq)]
enum RawKind {
/// Invalid string.
Invalid,
/// IRI.
Iri,
/// Absolute IRI.
IriAbsolute,
/// Relative IRI.
IriRelative,
/// URI.
Uri,
/// Absolute URI.
UriAbsolute,
/// Relative URI.
UriRelative,
}
impl RawKind {
fn spec_is(self, spec: Spec) -> bool {
match spec {
Spec::Uri => matches!(self, Self::Uri | Self::UriAbsolute | Self::UriRelative),
Spec::Iri => self != Self::Invalid,
}
}
fn kind_is(self, kind: Kind) -> bool {
match kind {
Kind::Absolute => matches!(self, Self::UriAbsolute | Self::IriAbsolute),
Kind::Normal => matches!(
self,
Self::UriAbsolute | Self::Uri | Self::IriAbsolute | Self::Iri
),
Kind::Reference => self != Self::Invalid,
Kind::Relative => matches!(self, Self::UriRelative | Self::IriRelative),
}
}
fn is(self, spec: Spec, kind: Kind) -> bool {
self.spec_is(spec) && self.kind_is(kind)
}
}
/// Strings.
/// ```
/// # use iri_string::types::IriReferenceStr;
/// // `<` and `>` cannot directly appear in an IRI reference.
/// assert!(IriReferenceStr::new("<not allowed>").is_err());
/// // Broken percent encoding cannot appear in an IRI reference.
/// assert!(IriReferenceStr::new("%").is_err());
/// assert!(IriReferenceStr::new("%GG").is_err());
/// ```
const STRINGS: &[(RawKind, &str)] = &[
(UriAbsolute, "https://user:pass@example.com:8080"),
(UriAbsolute, "https://example.com/"),
(UriAbsolute, "https://example.com/foo?bar=baz"),
(Uri, "https://example.com/foo?bar=baz#qux"),
(UriAbsolute, "foo:bar"),
(UriAbsolute, "foo:"),
(UriAbsolute, "foo:/"),
(UriAbsolute, "foo://"),
(UriAbsolute, "foo:///"),
(UriAbsolute, "foo:////"),
(UriAbsolute, "foo://///"),
(UriRelative, "foo"),
(UriRelative, "foo/bar"),
(UriRelative, "foo//bar"),
(UriRelative, "/"),
(UriRelative, "/foo"),
(UriRelative, "/foo/bar"),
(UriRelative, "//foo/bar"),
(UriRelative, "/foo//bar"),
(UriRelative, "?"),
(UriRelative, "???"),
(UriRelative, "?foo"),
(UriRelative, "#"),
(UriRelative, "#foo"),
(Invalid, "##"),
(Invalid, "fragment#cannot#have#hash#char"),
// `<` cannot appear in an IRI reference.
(Invalid, "<"),
// `>` cannot appear in an IRI reference.
(Invalid, ">"),
// `<` and `>` cannot appear in an IRI reference.
(Invalid, "lt<and-gt>not-allowed"),
// Incomplete percent encoding.
(Invalid, "%"),
(Invalid, "%0"),
(Invalid, "%f"),
(Invalid, "%F"),
// Invalid percent encoding.
(Invalid, "%0g"),
(Invalid, "%0G"),
(Invalid, "%GG"),
(Invalid, "%G0"),
];
/// Spec.
#[derive(Clone, Copy, PartialEq, Eq)]
pub enum Spec {
/// URI.
Uri,
/// IRI and URI.
Iri,
}
/// Kind.
#[derive(Clone, Copy, PartialEq, Eq)]
pub enum Kind {
/// Absolute IRI / URI.
Absolute,
/// IRI / URI.
Normal,
/// IRI / URI reference.
Reference,
/// Relative IRI / URI reference.
Relative,
}
pub fn positive(spec: Spec, kind: Kind) -> impl Iterator<Item = &'static str> {
STRINGS
.iter()
.filter(move |(raw_kind, _)| raw_kind.is(spec, kind))
.map(|(_, s)| *s)
}
pub fn negative(spec: Spec, kind: Kind) -> impl Iterator<Item = &'static str> {
STRINGS
.iter()
.filter(move |(raw_kind, _)| !raw_kind.is(spec, kind))
.map(|(_, s)| *s)
}
/// Returns true if the two equals after they are converted to strings.
pub(crate) fn eq_display_str<T>(d: &T, s: &str) -> bool
where
T: ?Sized + fmt::Display,
{
use core::fmt::Write as _;
/// Dummy writer to compare the formatted object to the given string.
struct CmpWriter<'a>(&'a str);
impl fmt::Write for CmpWriter<'_> {
fn write_str(&mut self, s: &str) -> fmt::Result {
if self.0.len() < s.len() {
return Err(fmt::Error);
}
let (prefix, rest) = self.0.split_at(s.len());
self.0 = rest;
if prefix == s {
Ok(())
} else {
Err(fmt::Error)
}
}
}
let mut writer = CmpWriter(s);
let succeeded = write!(writer, "{}", d).is_ok();
succeeded && writer.0.is_empty()
}
#[allow(unused_macros)]
macro_rules! assert_eq_display {
($left:expr, $right:expr $(,)?) => {{
match (&$left, &$right) {
(left, right) => {
assert!(
utils::eq_display_str(left, right.as_ref()),
"`eq_str_display(left, right)`\n left: `{left}`,\n right: `{right}`",
);
#[cfg(feature = "alloc")]
{
let left = left.to_string();
let right = right.to_string();
assert_eq!(left, right);
}
}
}
}};
($left:expr, $right:expr, $($args:tt)*) => {{
match (&$left, &$right) {
(left, right) => {
assert!(
utils::eq_display_str(left, right.as_ref()),
"{}",
format_args!(
"{}: {}",
format_args!(
"`eq_str_display(left, right)`\n left: `{left}`,\n right: `{right}`",
),
format_args!($($args)*)
)
);
#[cfg(feature = "alloc")]
{
let left = left.to_string();
let right = right.to_string();
assert_eq!(left, right, $($args)*);
}
}
}
}};
}