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

1
vendor/url/.cargo-checksum.json vendored Normal file
View File

@@ -0,0 +1 @@
{"files":{".cargo_vcs_info.json":"79d39e19f84f35830fef9637c62027c992e1107f6d850abce545f8277bd99aed","Cargo.lock":"f219c87b25034c1046eeffa7e75288646b719131dd49cf27e1efe64141c65ba7","Cargo.toml":"7003e5562b5ab8454b9d78376303151af79011f5dd684c1891e0f46bc9112016","Cargo.toml.orig":"71ca556493d5a7be3df490bee1cfe11da6174f5d1bd6e346efd76e682b26696b","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"b38f11f6096706e6de553dabe2a7ed142d59b6fa8c97e290c67496154745cdd5","README.md":"141354cfa79fe3a7e739834bf6e001ff1293169e1809cf9eb6d5c8af0b7a191f","src/host.rs":"24bba45b14cb843ea0fe5c857284d458097a13a84847f5c6a15a14c9e1334335","src/lib.rs":"51f47a93654528f3b8cb92c94fc26ad25775860d84197fff0c3124a687f8c5e3","src/origin.rs":"e8ffaf759aa78c92574c6ae49f76999a751064621c399059b3d4eb819cb0d821","src/parser.rs":"2dd4210e1773fc5d3fdc15ce506ecff30dbf8eb358a79bbbc2835cc30d513c3a","src/path_segments.rs":"28c8b6ffa880c7da76dcf43af8dcae2c46cd74389472e6e43efdf2973026a462","src/quirks.rs":"c3c8380f9dbecf65924272e904cad8876bda0c62e0e48a3c6b1344fac258102f","src/slicing.rs":"b59bf2fa4cbfd31619b860766268d4e99b46537d3b9702b73f6d0a979a7de24a","tests/expected_failures.txt":"dd016d320be3fe6ad3810c22302f8eabd755d9effc937b2193dbf222855c4b7f","tests/setters_tests.json":"a3a4cbd7b798bc2c4d9656dc50be7397a5a5ed1f0b52daa1da1ad654d38c1dcd","tests/unit.rs":"7eb96c1194fba03fd7f469c44cbbe28699dae14399be72e4c5c675a90fe08dc7","tests/urltestdata.json":"58d67bea710d5f46324fe6841df5fd82090fe4ec2d882bc0fc7c1784d4771884","tests/wpt.rs":"2d083ab72ad8cc9b116dd164e63440dd120f07977ac306377633cfe214f34a62"},"package":"ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed"}

6
vendor/url/.cargo_vcs_info.json vendored Normal file
View File

@@ -0,0 +1,6 @@
{
"git": {
"sha1": "d6ea13c5f8e7e6e627f6390161b3e185bda5e5ce"
},
"path_in_vcs": "url"
}

411
vendor/url/Cargo.lock generated vendored Normal file
View File

@@ -0,0 +1,411 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
[[package]]
name = "bencher"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7dfdb4953a096c551ce9ace855a604d702e6e62d77fac690575ae347571717f5"
[[package]]
name = "bumpalo"
version = "3.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43"
[[package]]
name = "cc"
version = "1.2.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37521ac7aabe3d13122dc382493e20c9416f299d2ccd5b3a5340a2570cdeb0f3"
dependencies = [
"find-msvc-tools",
"shlex",
]
[[package]]
name = "cfg-if"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
[[package]]
name = "find-msvc-tools"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "52051878f80a721bb68ebfbc930e07b65ba72f2da88968ea5c06fd6ca3d3a127"
[[package]]
name = "form_urlencoded"
version = "1.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf"
dependencies = [
"percent-encoding",
]
[[package]]
name = "idna"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de"
dependencies = [
"idna_adapter",
"smallvec",
"utf8_iter",
]
[[package]]
name = "idna_adapter"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "279259b0ac81c89d11c290495fdcfa96ea3643b7df311c138b6fe8ca5237f0f8"
dependencies = [
"idna_mapping",
"unicode-bidi",
"unicode-normalization",
]
[[package]]
name = "idna_mapping"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "11c13906586a4b339310541a274dd927aff6fcbb5b8e3af90634c4b31681c792"
dependencies = [
"unicode-joining-type",
]
[[package]]
name = "itoa"
version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
[[package]]
name = "js-sys"
version = "0.3.82"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b011eec8cc36da2aab2d5cff675ec18454fad408585853910a202391cf9f8e65"
dependencies = [
"once_cell",
"wasm-bindgen",
]
[[package]]
name = "memchr"
version = "2.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273"
[[package]]
name = "minicov"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f27fe9f1cc3c22e1687f9446c2083c4c5fc7f0bcf1c7a86bdbded14985895b4b"
dependencies = [
"cc",
"walkdir",
]
[[package]]
name = "once_cell"
version = "1.21.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
[[package]]
name = "percent-encoding"
version = "2.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220"
[[package]]
name = "proc-macro2"
version = "1.0.103"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.41"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ce25767e7b499d1b604768e7cde645d14cc8584231ea6b295e9c9eb22c02e1d1"
dependencies = [
"proc-macro2",
]
[[package]]
name = "rustversion"
version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
[[package]]
name = "ryu"
version = "1.0.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
[[package]]
name = "same-file"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
dependencies = [
"winapi-util",
]
[[package]]
name = "serde"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
dependencies = [
"serde_core",
]
[[package]]
name = "serde_core"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "serde_json"
version = "1.0.145"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c"
dependencies = [
"itoa",
"memchr",
"ryu",
"serde",
"serde_core",
]
[[package]]
name = "shlex"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
[[package]]
name = "smallvec"
version = "1.15.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
[[package]]
name = "syn"
version = "2.0.100"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "tinyvec"
version = "1.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bfa5fdc3bce6191a1dbc8c02d5c8bffcf557bafa17c124c5264a458f1b0613fa"
dependencies = [
"tinyvec_macros",
]
[[package]]
name = "tinyvec_macros"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
[[package]]
name = "unicode-bidi"
version = "0.3.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5"
[[package]]
name = "unicode-ident"
version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5"
[[package]]
name = "unicode-joining-type"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d8d00a78170970967fdb83f9d49b92f959ab2bb829186b113e4f4604ad98e180"
[[package]]
name = "unicode-normalization"
version = "0.1.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5fd4f6878c9cb28d874b009da9e8d183b5abc80117c40bbd187a1fde336be6e8"
dependencies = [
"tinyvec",
]
[[package]]
name = "url"
version = "2.5.8"
dependencies = [
"bencher",
"form_urlencoded",
"idna",
"percent-encoding",
"serde",
"serde_derive",
"serde_json",
"wasm-bindgen-test",
]
[[package]]
name = "utf8_iter"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
[[package]]
name = "walkdir"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b"
dependencies = [
"same-file",
"winapi-util",
]
[[package]]
name = "wasm-bindgen"
version = "0.2.105"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da95793dfc411fbbd93f5be7715b0578ec61fe87cb1a42b12eb625caa5c5ea60"
dependencies = [
"cfg-if",
"once_cell",
"rustversion",
"wasm-bindgen-macro",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-futures"
version = "0.4.55"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "551f88106c6d5e7ccc7cd9a16f312dd3b5d36ea8b4954304657d5dfba115d4a0"
dependencies = [
"cfg-if",
"js-sys",
"once_cell",
"wasm-bindgen",
"web-sys",
]
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.105"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "04264334509e04a7bf8690f2384ef5265f05143a4bff3889ab7a3269adab59c2"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
]
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.105"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "420bc339d9f322e562942d52e115d57e950d12d88983a14c79b86859ee6c7ebc"
dependencies = [
"bumpalo",
"proc-macro2",
"quote",
"syn",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.105"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "76f218a38c84bcb33c25ec7059b07847d465ce0e0a76b995e134a45adcb6af76"
dependencies = [
"unicode-ident",
]
[[package]]
name = "wasm-bindgen-test"
version = "0.3.55"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bfc379bfb624eb59050b509c13e77b4eb53150c350db69628141abce842f2373"
dependencies = [
"js-sys",
"minicov",
"wasm-bindgen",
"wasm-bindgen-futures",
"wasm-bindgen-test-macro",
]
[[package]]
name = "wasm-bindgen-test-macro"
version = "0.3.55"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "085b2df989e1e6f9620c1311df6c996e83fe16f57792b272ce1e024ac16a90f1"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "web-sys"
version = "0.3.82"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3a1f95c0d03a47f4ae1f7a64643a6bb97465d9b740f0fa8f90ea33915c99a9a1"
dependencies = [
"js-sys",
"wasm-bindgen",
]
[[package]]
name = "winapi-util"
version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
dependencies = [
"windows-sys",
]
[[package]]
name = "windows-link"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
[[package]]
name = "windows-sys"
version = "0.61.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
dependencies = [
"windows-link",
]

122
vendor/url/Cargo.toml vendored Normal file
View File

@@ -0,0 +1,122 @@
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
#
# When uploading crates to the registry Cargo will automatically
# "normalize" Cargo.toml files for maximal compatibility
# with all versions of Cargo and also rewrite `path` dependencies
# to registry (e.g., crates.io) dependencies.
#
# If you are reading this file be aware that the original Cargo.toml
# will likely look very different (and much more reasonable).
# See Cargo.toml.orig for the original contents.
[package]
edition = "2018"
rust-version = "1.63"
name = "url"
version = "2.5.8"
authors = ["The rust-url developers"]
build = false
include = [
"src/**/*",
"LICENSE-*",
"README.md",
"tests/**",
]
autolib = false
autobins = false
autoexamples = false
autotests = false
autobenches = false
description = "URL library for Rust, based on the WHATWG URL Standard"
documentation = "https://docs.rs/url"
readme = "README.md"
keywords = [
"url",
"parser",
]
categories = [
"parser-implementations",
"web-programming",
"encoding",
"no-std",
]
license = "MIT OR Apache-2.0"
repository = "https://github.com/servo/rust-url"
[package.metadata.docs.rs]
features = ["serde"]
rustdoc-args = ["--generate-link-to-definition"]
[package.metadata.playground]
features = ["serde"]
[features]
debugger_visualizer = []
default = ["std"]
expose_internals = []
serde = [
"dep:serde",
"dep:serde_derive",
]
std = [
"idna/std",
"percent-encoding/std",
"form_urlencoded/std",
"serde?/std",
]
[lib]
name = "url"
path = "src/lib.rs"
[[test]]
name = "unit"
path = "tests/unit.rs"
[[test]]
name = "url_wpt"
path = "tests/wpt.rs"
harness = false
[dependencies.form_urlencoded]
version = "1.2.2"
features = ["alloc"]
default-features = false
[dependencies.idna]
version = "1.1.0"
features = [
"alloc",
"compiled_data",
]
default-features = false
[dependencies.percent-encoding]
version = "2.3.2"
features = ["alloc"]
default-features = false
[dependencies.serde]
version = "1.0"
optional = true
default-features = false
[dependencies.serde_derive]
version = "1.0"
optional = true
default-features = false
[dev-dependencies.bencher]
version = "0.1"
[dev-dependencies.serde]
version = "1.0"
[dev-dependencies.serde_derive]
version = "1.0"
[dev-dependencies.serde_json]
version = "1.0"
[target.'cfg(all(target_arch = "wasm32", target_os = "unknown"))'.dev-dependencies.wasm-bindgen-test]
version = "0.3"

201
vendor/url/LICENSE-APACHE vendored Normal file
View File

@@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

25
vendor/url/LICENSE-MIT vendored Normal file
View File

@@ -0,0 +1,25 @@
Copyright (c) 2013-2025 The rust-url developers
Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without
limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice
shall be included in all copies or substantial portions
of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

18
vendor/url/README.md vendored Normal file
View File

@@ -0,0 +1,18 @@
rust-url
========
[![Build status](https://github.com/servo/rust-url/workflows/CI/badge.svg)](https://github.com/servo/rust-url/actions?query=workflow%3ACI)
[![Coverage](https://codecov.io/gh/servo/rust-url/branch/master/graph/badge.svg)](https://codecov.io/gh/servo/rust-url)
[![Chat](https://img.shields.io/badge/chat-%23rust--url:mozilla.org-%2346BC99?logo=Matrix)](https://matrix.to/#/#rust-url:mozilla.org)
[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE-MIT)
[![License: Apache 2.0](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](LICENSE-APACHE)
URL library for Rust, based on the [URL Standard](https://url.spec.whatwg.org/).
[Documentation](https://docs.rs/url)
Please see [UPGRADING.md](https://github.com/servo/rust-url/blob/main/UPGRADING.md) if you are upgrading from a previous version.
## Alternative Unicode back ends
`url` depends on the `idna` crate. By default, `idna` uses [ICU4X](https://github.com/unicode-org/icu4x/) as its Unicode back end. If you wish to opt for different tradeoffs between correctness, run-time performance, binary size, compile time, and MSRV, please see the [README of the latest version of the `idna_adapter` crate](https://docs.rs/crate/idna_adapter/latest) for how to opt into a different Unicode back end.

512
vendor/url/src/host.rs vendored Normal file
View File

@@ -0,0 +1,512 @@
// Copyright 2013-2016 The rust-url developers.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use crate::net::{Ipv4Addr, Ipv6Addr};
use alloc::borrow::Cow;
use alloc::borrow::ToOwned;
use alloc::string::String;
use alloc::vec::Vec;
use core::cmp;
use core::fmt::{self, Formatter};
use percent_encoding::{percent_decode, utf8_percent_encode, CONTROLS};
#[cfg(feature = "serde")]
use serde_derive::{Deserialize, Serialize};
use crate::parser::{ParseError, ParseResult};
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub(crate) enum HostInternal {
None,
Domain,
Ipv4(Ipv4Addr),
Ipv6(Ipv6Addr),
}
impl From<Host<Cow<'_, str>>> for HostInternal {
fn from(host: Host<Cow<'_, str>>) -> Self {
match host {
Host::Domain(ref s) if s.is_empty() => Self::None,
Host::Domain(_) => Self::Domain,
Host::Ipv4(address) => Self::Ipv4(address),
Host::Ipv6(address) => Self::Ipv6(address),
}
}
}
/// The host name of an URL.
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[derive(Clone, Debug, Eq, Ord, PartialOrd, Hash)]
pub enum Host<S = String> {
/// A DNS domain name, as '.' dot-separated labels.
/// Non-ASCII labels are encoded in punycode per IDNA if this is the host of
/// a special URL, or percent encoded for non-special URLs. Hosts for
/// non-special URLs are also called opaque hosts.
Domain(S),
/// An IPv4 address.
/// `Url::host_str` returns the serialization of this address,
/// as four decimal integers separated by `.` dots.
Ipv4(Ipv4Addr),
/// An IPv6 address.
/// `Url::host_str` returns the serialization of that address between `[` and `]` brackets,
/// in the format per [RFC 5952 *A Recommendation
/// for IPv6 Address Text Representation*](https://tools.ietf.org/html/rfc5952):
/// lowercase hexadecimal with maximal `::` compression.
Ipv6(Ipv6Addr),
}
impl Host<&str> {
/// Return a copy of `self` that owns an allocated `String` but does not borrow an `&Url`.
pub fn to_owned(&self) -> Host<String> {
match *self {
Host::Domain(domain) => Host::Domain(domain.to_owned()),
Host::Ipv4(address) => Host::Ipv4(address),
Host::Ipv6(address) => Host::Ipv6(address),
}
}
}
impl Host<String> {
/// Parse a host: either an IPv6 address in [] square brackets, or a domain.
///
/// <https://url.spec.whatwg.org/#host-parsing>
pub fn parse(input: &str) -> Result<Self, ParseError> {
Host::<Cow<str>>::parse_cow(input.into()).map(|i| i.into_owned())
}
/// <https://url.spec.whatwg.org/#concept-opaque-host-parser>
pub fn parse_opaque(input: &str) -> Result<Self, ParseError> {
Host::<Cow<str>>::parse_opaque_cow(input.into()).map(|i| i.into_owned())
}
}
impl<'a> Host<Cow<'a, str>> {
pub(crate) fn parse_cow(input: Cow<'a, str>) -> Result<Self, ParseError> {
if input.starts_with('[') {
if !input.ends_with(']') {
return Err(ParseError::InvalidIpv6Address);
}
return parse_ipv6addr(&input[1..input.len() - 1]).map(Host::Ipv6);
}
let domain: Cow<'_, [u8]> = percent_decode(input.as_bytes()).into();
let domain: Cow<'a, [u8]> = match domain {
Cow::Owned(v) => Cow::Owned(v),
// if borrowed then we can use the original cow
Cow::Borrowed(_) => match input {
Cow::Borrowed(input) => Cow::Borrowed(input.as_bytes()),
Cow::Owned(input) => Cow::Owned(input.into_bytes()),
},
};
let domain = idna::domain_to_ascii_from_cow(domain, idna::AsciiDenyList::URL)?;
if domain.is_empty() {
return Err(ParseError::EmptyHost);
}
if ends_in_a_number(&domain) {
let address = parse_ipv4addr(&domain)?;
Ok(Host::Ipv4(address))
} else {
Ok(Host::Domain(domain))
}
}
pub(crate) fn parse_opaque_cow(input: Cow<'a, str>) -> Result<Self, ParseError> {
if input.starts_with('[') {
if !input.ends_with(']') {
return Err(ParseError::InvalidIpv6Address);
}
return parse_ipv6addr(&input[1..input.len() - 1]).map(Host::Ipv6);
}
let is_invalid_host_char = |c| {
matches!(
c,
'\0' | '\t'
| '\n'
| '\r'
| ' '
| '#'
| '/'
| ':'
| '<'
| '>'
| '?'
| '@'
| '['
| '\\'
| ']'
| '^'
| '|'
)
};
if input.find(is_invalid_host_char).is_some() {
return Err(ParseError::InvalidDomainCharacter);
}
// Call utf8_percent_encode and use the result.
// Note: This returns Cow::Borrowed for single-item results (either from input
// or from the static encoding table), and Cow::Owned for multi-item results.
// We cannot distinguish between "borrowed from input" vs "borrowed from static table"
// based on the Cow variant alone.
Ok(Host::Domain(
match utf8_percent_encode(&input, CONTROLS).into() {
Cow::Owned(v) => Cow::Owned(v),
// If we're borrowing, we need to check if it's the same as the input
Cow::Borrowed(v) => {
if v == &*input {
input // No encoding happened, reuse original
} else {
Cow::Owned(v.to_owned()) // Borrowed from static table, need to own it
}
}
},
))
}
pub(crate) fn into_owned(self) -> Host<String> {
match self {
Host::Domain(s) => Host::Domain(s.into_owned()),
Host::Ipv4(ip) => Host::Ipv4(ip),
Host::Ipv6(ip) => Host::Ipv6(ip),
}
}
}
impl<S: AsRef<str>> fmt::Display for Host<S> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match *self {
Self::Domain(ref domain) => domain.as_ref().fmt(f),
Self::Ipv4(ref addr) => addr.fmt(f),
Self::Ipv6(ref addr) => {
f.write_str("[")?;
write_ipv6(addr, f)?;
f.write_str("]")
}
}
}
}
impl<S, T> PartialEq<Host<T>> for Host<S>
where
S: PartialEq<T>,
{
fn eq(&self, other: &Host<T>) -> bool {
match (self, other) {
(Self::Domain(a), Host::Domain(b)) => a == b,
(Self::Ipv4(a), Host::Ipv4(b)) => a == b,
(Self::Ipv6(a), Host::Ipv6(b)) => a == b,
(_, _) => false,
}
}
}
fn write_ipv6(addr: &Ipv6Addr, f: &mut Formatter<'_>) -> fmt::Result {
let segments = addr.segments();
let (compress_start, compress_end) = longest_zero_sequence(&segments);
let mut i = 0;
while i < 8 {
if i == compress_start {
f.write_str(":")?;
if i == 0 {
f.write_str(":")?;
}
if compress_end < 8 {
i = compress_end;
} else {
break;
}
}
write!(f, "{:x}", segments[i as usize])?;
if i < 7 {
f.write_str(":")?;
}
i += 1;
}
Ok(())
}
// https://url.spec.whatwg.org/#concept-ipv6-serializer step 2 and 3
fn longest_zero_sequence(pieces: &[u16; 8]) -> (isize, isize) {
let mut longest = -1;
let mut longest_length = -1;
let mut start = -1;
macro_rules! finish_sequence(
($end: expr) => {
if start >= 0 {
let length = $end - start;
if length > longest_length {
longest = start;
longest_length = length;
}
}
};
);
for i in 0..8 {
if pieces[i as usize] == 0 {
if start < 0 {
start = i;
}
} else {
finish_sequence!(i);
start = -1;
}
}
finish_sequence!(8);
// https://url.spec.whatwg.org/#concept-ipv6-serializer
// step 3: ignore lone zeroes
if longest_length < 2 {
(-1, -2)
} else {
(longest, longest + longest_length)
}
}
/// <https://url.spec.whatwg.org/#ends-in-a-number-checker>
fn ends_in_a_number(input: &str) -> bool {
let mut parts = input.rsplit('.');
let last = parts.next().unwrap();
let last = if last.is_empty() {
if let Some(last) = parts.next() {
last
} else {
return false;
}
} else {
last
};
if !last.is_empty() && last.as_bytes().iter().all(|c| c.is_ascii_digit()) {
return true;
}
parse_ipv4number(last).is_ok()
}
/// <https://url.spec.whatwg.org/#ipv4-number-parser>
/// Ok(None) means the input is a valid number, but it overflows a `u32`.
fn parse_ipv4number(mut input: &str) -> Result<Option<u32>, ()> {
if input.is_empty() {
return Err(());
}
let mut r = 10;
if input.starts_with("0x") || input.starts_with("0X") {
input = &input[2..];
r = 16;
} else if input.len() >= 2 && input.starts_with('0') {
input = &input[1..];
r = 8;
}
if input.is_empty() {
return Ok(Some(0));
}
let valid_number = match r {
8 => input.as_bytes().iter().all(|c| (b'0'..=b'7').contains(c)),
10 => input.as_bytes().iter().all(|c| c.is_ascii_digit()),
16 => input.as_bytes().iter().all(|c| c.is_ascii_hexdigit()),
_ => false,
};
if !valid_number {
return Err(());
}
match u32::from_str_radix(input, r) {
Ok(num) => Ok(Some(num)),
Err(_) => Ok(None), // The only possible error kind here is an integer overflow.
// The validity of the chars in the input is checked above.
}
}
/// <https://url.spec.whatwg.org/#concept-ipv4-parser>
fn parse_ipv4addr(input: &str) -> ParseResult<Ipv4Addr> {
let mut parts: Vec<&str> = input.split('.').collect();
if parts.last() == Some(&"") {
parts.pop();
}
if parts.len() > 4 {
return Err(ParseError::InvalidIpv4Address);
}
let mut numbers: Vec<u32> = Vec::new();
for part in parts {
match parse_ipv4number(part) {
Ok(Some(n)) => numbers.push(n),
Ok(None) => return Err(ParseError::InvalidIpv4Address), // u32 overflow
Err(()) => return Err(ParseError::InvalidIpv4Address),
};
}
let mut ipv4 = numbers.pop().expect("a non-empty list of numbers");
// Equivalent to: ipv4 >= 256 ** (4 numbers.len())
if ipv4 > u32::MAX >> (8 * numbers.len() as u32) {
return Err(ParseError::InvalidIpv4Address);
}
if numbers.iter().any(|x| *x > 255) {
return Err(ParseError::InvalidIpv4Address);
}
for (counter, n) in numbers.iter().enumerate() {
ipv4 += n << (8 * (3 - counter as u32))
}
Ok(Ipv4Addr::from(ipv4))
}
/// <https://url.spec.whatwg.org/#concept-ipv6-parser>
fn parse_ipv6addr(input: &str) -> ParseResult<Ipv6Addr> {
let input = input.as_bytes();
let len = input.len();
let mut is_ip_v4 = false;
let mut pieces = [0, 0, 0, 0, 0, 0, 0, 0];
let mut piece_pointer = 0;
let mut compress_pointer = None;
let mut i = 0;
if len < 2 {
return Err(ParseError::InvalidIpv6Address);
}
if input[0] == b':' {
if input[1] != b':' {
return Err(ParseError::InvalidIpv6Address);
}
i = 2;
piece_pointer = 1;
compress_pointer = Some(1);
}
while i < len {
if piece_pointer == 8 {
return Err(ParseError::InvalidIpv6Address);
}
if input[i] == b':' {
if compress_pointer.is_some() {
return Err(ParseError::InvalidIpv6Address);
}
i += 1;
piece_pointer += 1;
compress_pointer = Some(piece_pointer);
continue;
}
let start = i;
let end = cmp::min(len, start + 4);
let mut value = 0u16;
while i < end {
match (input[i] as char).to_digit(16) {
Some(digit) => {
value = value * 0x10 + digit as u16;
i += 1;
}
None => break,
}
}
if i < len {
match input[i] {
b'.' => {
if i == start {
return Err(ParseError::InvalidIpv6Address);
}
i = start;
if piece_pointer > 6 {
return Err(ParseError::InvalidIpv6Address);
}
is_ip_v4 = true;
}
b':' => {
i += 1;
if i == len {
return Err(ParseError::InvalidIpv6Address);
}
}
_ => return Err(ParseError::InvalidIpv6Address),
}
}
if is_ip_v4 {
break;
}
pieces[piece_pointer] = value;
piece_pointer += 1;
}
if is_ip_v4 {
if piece_pointer > 6 {
return Err(ParseError::InvalidIpv6Address);
}
let mut numbers_seen = 0;
while i < len {
if numbers_seen > 0 {
if numbers_seen < 4 && (i < len && input[i] == b'.') {
i += 1
} else {
return Err(ParseError::InvalidIpv6Address);
}
}
let mut ipv4_piece = None;
while i < len {
let digit = match input[i] {
c @ b'0'..=b'9' => c - b'0',
_ => break,
};
match ipv4_piece {
None => ipv4_piece = Some(digit as u16),
Some(0) => return Err(ParseError::InvalidIpv6Address), // No leading zero
Some(ref mut v) => {
*v = *v * 10 + digit as u16;
if *v > 255 {
return Err(ParseError::InvalidIpv6Address);
}
}
}
i += 1;
}
pieces[piece_pointer] = if let Some(v) = ipv4_piece {
pieces[piece_pointer] * 0x100 + v
} else {
return Err(ParseError::InvalidIpv6Address);
};
numbers_seen += 1;
if numbers_seen == 2 || numbers_seen == 4 {
piece_pointer += 1;
}
}
if numbers_seen != 4 {
return Err(ParseError::InvalidIpv6Address);
}
}
if i < len {
return Err(ParseError::InvalidIpv6Address);
}
match compress_pointer {
Some(compress_pointer) => {
let mut swaps = piece_pointer - compress_pointer;
piece_pointer = 7;
while swaps > 0 {
pieces.swap(piece_pointer, compress_pointer + swaps - 1);
swaps -= 1;
piece_pointer -= 1;
}
}
_ => {
if piece_pointer != 8 {
return Err(ParseError::InvalidIpv6Address);
}
}
}
Ok(Ipv6Addr::new(
pieces[0], pieces[1], pieces[2], pieces[3], pieces[4], pieces[5], pieces[6], pieces[7],
))
}

3231
vendor/url/src/lib.rs vendored Normal file

File diff suppressed because it is too large Load Diff

115
vendor/url/src/origin.rs vendored Normal file
View File

@@ -0,0 +1,115 @@
// Copyright 2016 The rust-url developers.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use crate::host::Host;
use crate::parser::default_port;
use crate::Url;
use alloc::borrow::ToOwned;
use alloc::format;
use alloc::string::String;
use core::sync::atomic::{AtomicUsize, Ordering};
pub fn url_origin(url: &Url) -> Origin {
let scheme = url.scheme();
match scheme {
"blob" => {
let result = Url::parse(url.path());
match result {
Ok(ref url) => url_origin(url),
Err(_) => Origin::new_opaque(),
}
}
"ftp" | "http" | "https" | "ws" | "wss" => Origin::Tuple(
scheme.to_owned(),
url.host().unwrap().to_owned(),
url.port_or_known_default().unwrap(),
),
// TODO: Figure out what to do if the scheme is a file
"file" => Origin::new_opaque(),
_ => Origin::new_opaque(),
}
}
/// The origin of an URL
///
/// Two URLs with the same origin are considered
/// to originate from the same entity and can therefore trust
/// each other.
///
/// The origin is determined based on the scheme as follows:
///
/// - If the scheme is "blob" the origin is the origin of the
/// URL contained in the path component. If parsing fails,
/// it is an opaque origin.
/// - If the scheme is "ftp", "http", "https", "ws", or "wss",
/// then the origin is a tuple of the scheme, host, and port.
/// - If the scheme is anything else, the origin is opaque, meaning
/// the URL does not have the same origin as any other URL.
///
/// For more information see <https://url.spec.whatwg.org/#origin>
#[derive(PartialEq, Eq, Hash, Clone, Debug)]
pub enum Origin {
/// A globally unique identifier
Opaque(OpaqueOrigin),
/// Consists of the URL's scheme, host and port
Tuple(String, Host<String>, u16),
}
impl Origin {
/// Creates a new opaque origin that is only equal to itself.
pub fn new_opaque() -> Self {
static COUNTER: AtomicUsize = AtomicUsize::new(0);
Self::Opaque(OpaqueOrigin(COUNTER.fetch_add(1, Ordering::SeqCst)))
}
/// Return whether this origin is a (scheme, host, port) tuple
/// (as opposed to an opaque origin).
pub fn is_tuple(&self) -> bool {
matches!(*self, Self::Tuple(..))
}
/// <https://html.spec.whatwg.org/multipage/#ascii-serialisation-of-an-origin>
pub fn ascii_serialization(&self) -> String {
match *self {
Self::Opaque(_) => "null".to_owned(),
Self::Tuple(ref scheme, ref host, port) => {
if default_port(scheme) == Some(port) {
format!("{scheme}://{host}")
} else {
format!("{scheme}://{host}:{port}")
}
}
}
}
/// <https://html.spec.whatwg.org/multipage/#unicode-serialisation-of-an-origin>
pub fn unicode_serialization(&self) -> String {
match *self {
Self::Opaque(_) => "null".to_owned(),
Self::Tuple(ref scheme, ref host, port) => {
let host = match *host {
Host::Domain(ref domain) => {
let (domain, _errors) = idna::domain_to_unicode(domain);
Host::Domain(domain)
}
_ => host.clone(),
};
if default_port(scheme) == Some(port) {
format!("{scheme}://{host}")
} else {
format!("{scheme}://{host}:{port}")
}
}
}
}
}
/// Opaque identifier for URLs that have file or other schemes
#[derive(Eq, PartialEq, Hash, Clone, Debug)]
pub struct OpaqueOrigin(usize);

1836
vendor/url/src/parser.rs vendored Normal file

File diff suppressed because it is too large Load Diff

267
vendor/url/src/path_segments.rs vendored Normal file
View File

@@ -0,0 +1,267 @@
// Copyright 2016 The rust-url developers.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use crate::parser::{self, to_u32, SchemeType};
use crate::Url;
use alloc::string::String;
use core::str;
/// Exposes methods to manipulate the path of an URL that is not cannot-be-base.
///
/// The path always starts with a `/` slash, and is made of slash-separated segments.
/// There is always at least one segment (which may be the empty string).
///
/// Examples:
///
/// ```rust
/// use url::Url;
///
/// # #[cfg(feature = "std")]
/// # use std::error::Error;
/// # #[cfg(not(feature = "std"))]
/// # use core::error::Error;
///
/// # fn run() -> Result<(), Box<dyn Error>> {
/// let mut url = Url::parse("mailto:me@example.com")?;
/// assert!(url.path_segments_mut().is_err());
///
/// let mut url = Url::parse("http://example.net/foo/index.html")?;
/// url.path_segments_mut().map_err(|_| "cannot be base")?
/// .pop().push("img").push("2/100%.png");
/// assert_eq!(url.as_str(), "http://example.net/foo/img/2%2F100%25.png");
/// # Ok(())
/// # }
/// # run().unwrap();
/// ```
#[derive(Debug)]
pub struct PathSegmentsMut<'a> {
url: &'a mut Url,
after_first_slash: usize,
after_path: String,
old_after_path_position: u32,
}
// Not re-exported outside the crate
pub fn new(url: &mut Url) -> PathSegmentsMut<'_> {
let after_path = url.take_after_path();
let old_after_path_position = to_u32(url.serialization.len()).unwrap();
// Special urls always have a non empty path
if SchemeType::from(url.scheme()).is_special() {
debug_assert!(url.byte_at(url.path_start) == b'/');
} else {
debug_assert!(
url.serialization.len() == url.path_start as usize
|| url.byte_at(url.path_start) == b'/'
);
}
PathSegmentsMut {
after_first_slash: url.path_start as usize + "/".len(),
url,
old_after_path_position,
after_path,
}
}
impl Drop for PathSegmentsMut<'_> {
fn drop(&mut self) {
self.url
.restore_after_path(self.old_after_path_position, &self.after_path)
}
}
impl PathSegmentsMut<'_> {
/// Remove all segments in the path, leaving the minimal `url.path() == "/"`.
///
/// Returns `&mut Self` so that method calls can be chained.
///
/// Example:
///
/// ```rust
/// use url::Url;
///
/// # #[cfg(feature = "std")]
/// # use std::error::Error;
/// # #[cfg(not(feature = "std"))]
/// # use core::error::Error;
///
/// # fn run() -> Result<(), Box<dyn Error>> {
/// let mut url = Url::parse("https://github.com/servo/rust-url/")?;
/// url.path_segments_mut().map_err(|_| "cannot be base")?
/// .clear().push("logout");
/// assert_eq!(url.as_str(), "https://github.com/logout");
/// # Ok(())
/// # }
/// # run().unwrap();
/// ```
pub fn clear(&mut self) -> &mut Self {
self.url.serialization.truncate(self.after_first_slash);
self
}
/// Remove the last segment of this URLs path if it is empty,
/// except if these was only one segment to begin with.
///
/// In other words, remove one path trailing slash, if any,
/// unless it is also the initial slash (so this does nothing if `url.path() == "/")`.
///
/// Returns `&mut Self` so that method calls can be chained.
///
/// Example:
///
/// ```rust
/// use url::Url;
///
/// # #[cfg(feature = "std")]
/// # use std::error::Error;
/// # #[cfg(not(feature = "std"))]
/// # use core::error::Error;
///
/// # fn run() -> Result<(), Box<dyn Error>> {
/// let mut url = Url::parse("https://github.com/servo/rust-url/")?;
/// url.path_segments_mut().map_err(|_| "cannot be base")?
/// .push("pulls");
/// assert_eq!(url.as_str(), "https://github.com/servo/rust-url//pulls");
///
/// let mut url = Url::parse("https://github.com/servo/rust-url/")?;
/// url.path_segments_mut().map_err(|_| "cannot be base")?
/// .pop_if_empty().push("pulls");
/// assert_eq!(url.as_str(), "https://github.com/servo/rust-url/pulls");
/// # Ok(())
/// # }
/// # run().unwrap();
/// ```
pub fn pop_if_empty(&mut self) -> &mut Self {
if self.after_first_slash >= self.url.serialization.len() {
return self;
}
if self.url.serialization[self.after_first_slash..].ends_with('/') {
self.url.serialization.pop();
}
self
}
/// Remove the last segment of this URLs path.
///
/// If the path only has one segment, make it empty such that `url.path() == "/"`.
///
/// Returns `&mut Self` so that method calls can be chained.
pub fn pop(&mut self) -> &mut Self {
if self.after_first_slash >= self.url.serialization.len() {
return self;
}
let last_slash = self.url.serialization[self.after_first_slash..]
.rfind('/')
.unwrap_or(0);
self.url
.serialization
.truncate(self.after_first_slash + last_slash);
self
}
/// Append the given segment at the end of this URLs path.
///
/// See the documentation for `.extend()`.
///
/// Returns `&mut Self` so that method calls can be chained.
pub fn push(&mut self, segment: &str) -> &mut Self {
self.extend(Some(segment))
}
/// Append each segment from the given iterator at the end of this URLs path.
///
/// Each segment is percent-encoded like in `Url::parse` or `Url::join`,
/// except that `%` and `/` characters are also encoded (to `%25` and `%2F`).
/// This is unlike `Url::parse` where `%` is left as-is in case some of the input
/// is already percent-encoded, and `/` denotes a path segment separator.)
///
/// Note that, in addition to slashes between new segments,
/// this always adds a slash between the existing path and the new segments
/// *except* if the existing path is `"/"`.
/// If the previous last segment was empty (if the path had a trailing slash)
/// the path after `.extend()` will contain two consecutive slashes.
/// If that is undesired, call `.pop_if_empty()` first.
///
/// To obtain a behavior similar to `Url::join`, call `.pop()` unconditionally first.
///
/// Returns `&mut Self` so that method calls can be chained.
///
/// Example:
///
/// ```rust
/// use url::Url;
///
/// # #[cfg(feature = "std")]
/// # use std::error::Error;
/// # #[cfg(not(feature = "std"))]
/// # use core::error::Error;
///
/// # fn run() -> Result<(), Box<dyn Error>> {
/// let mut url = Url::parse("https://github.com/")?;
/// let org = "servo";
/// let repo = "rust-url";
/// let issue_number = "188";
/// url.path_segments_mut().map_err(|_| "cannot be base")?
/// .extend(&[org, repo, "issues", issue_number]);
/// assert_eq!(url.as_str(), "https://github.com/servo/rust-url/issues/188");
/// # Ok(())
/// # }
/// # run().unwrap();
/// ```
///
/// In order to make sure that parsing the serialization of an URL gives the same URL,
/// a segment is ignored if it is `"."` or `".."`:
///
/// ```rust
/// use url::Url;
///
/// # #[cfg(feature = "std")]
/// # use std::error::Error;
/// # #[cfg(not(feature = "std"))]
/// # use core::error::Error;
///
/// # fn run() -> Result<(), Box<dyn Error>> {
/// let mut url = Url::parse("https://github.com/servo")?;
/// url.path_segments_mut().map_err(|_| "cannot be base")?
/// .extend(&["..", "rust-url", ".", "pulls"]);
/// assert_eq!(url.as_str(), "https://github.com/servo/rust-url/pulls");
/// # Ok(())
/// # }
/// # run().unwrap();
/// ```
pub fn extend<I>(&mut self, segments: I) -> &mut Self
where
I: IntoIterator,
I::Item: AsRef<str>,
{
let scheme_type = SchemeType::from(self.url.scheme());
let path_start = self.url.path_start as usize;
self.url.mutate(|parser| {
parser.context = parser::Context::PathSegmentSetter;
for segment in segments {
let segment = segment.as_ref();
if matches!(segment, "." | "..") {
continue;
}
if parser.serialization.len() > path_start + 1
// Non special url's path might still be empty
|| parser.serialization.len() == path_start
{
parser.serialization.push('/');
}
let mut has_host = true; // FIXME account for this?
parser.parse_path(
scheme_type,
&mut has_host,
path_start,
parser::Input::new_no_trim(segment),
);
}
});
self
}
}

336
vendor/url/src/quirks.rs vendored Normal file
View File

@@ -0,0 +1,336 @@
// Copyright 2016 The rust-url developers.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! Getters and setters for URL components implemented per <https://url.spec.whatwg.org/#api>
//!
//! Unless you need to be interoperable with web browsers,
//! you probably want to use `Url` method instead.
use crate::parser::{default_port, Context, Input, Parser, SchemeType};
use crate::{Host, ParseError, Position, Url};
use alloc::string::String;
use alloc::string::ToString;
/// Internal components / offsets of a URL.
///
/// https://user@pass:example.com:1234/foo/bar?baz#quux
/// | | | | ^^^^| | |
/// | | | | | | | `----- fragment_start
/// | | | | | | `--------- query_start
/// | | | | | `----------------- path_start
/// | | | | `--------------------- port
/// | | | `----------------------- host_end
/// | | `---------------------------------- host_start
/// | `--------------------------------------- username_end
/// `---------------------------------------------- scheme_end
#[derive(Copy, Clone)]
#[cfg(feature = "expose_internals")]
pub struct InternalComponents {
pub scheme_end: u32,
pub username_end: u32,
pub host_start: u32,
pub host_end: u32,
pub port: Option<u16>,
pub path_start: u32,
pub query_start: Option<u32>,
pub fragment_start: Option<u32>,
}
/// Internal component / parsed offsets of the URL.
///
/// This can be useful for implementing efficient serialization
/// for the URL.
#[cfg(feature = "expose_internals")]
pub fn internal_components(url: &Url) -> InternalComponents {
InternalComponents {
scheme_end: url.scheme_end,
username_end: url.username_end,
host_start: url.host_start,
host_end: url.host_end,
port: url.port,
path_start: url.path_start,
query_start: url.query_start,
fragment_start: url.fragment_start,
}
}
/// <https://url.spec.whatwg.org/#dom-url-domaintoascii>
pub fn domain_to_ascii(domain: &str) -> String {
match Host::parse(domain) {
Ok(Host::Domain(domain)) => domain,
_ => String::new(),
}
}
/// <https://url.spec.whatwg.org/#dom-url-domaintounicode>
pub fn domain_to_unicode(domain: &str) -> String {
match Host::parse(domain) {
Ok(Host::Domain(ref domain)) => {
let (unicode, _errors) = idna::domain_to_unicode(domain);
unicode
}
_ => String::new(),
}
}
/// Getter for <https://url.spec.whatwg.org/#dom-url-href>
pub fn href(url: &Url) -> &str {
url.as_str()
}
/// Setter for <https://url.spec.whatwg.org/#dom-url-href>
pub fn set_href(url: &mut Url, value: &str) -> Result<(), ParseError> {
*url = Url::parse(value)?;
Ok(())
}
/// Getter for <https://url.spec.whatwg.org/#dom-url-origin>
pub fn origin(url: &Url) -> String {
url.origin().ascii_serialization()
}
/// Getter for <https://url.spec.whatwg.org/#dom-url-protocol>
#[inline]
pub fn protocol(url: &Url) -> &str {
&url.as_str()[..url.scheme().len() + ":".len()]
}
/// Setter for <https://url.spec.whatwg.org/#dom-url-protocol>
#[allow(clippy::result_unit_err)]
pub fn set_protocol(url: &mut Url, mut new_protocol: &str) -> Result<(), ()> {
// The scheme state in the spec ignores everything after the first `:`,
// but `set_scheme` errors if there is more.
if let Some(position) = new_protocol.find(':') {
new_protocol = &new_protocol[..position];
}
url.set_scheme(new_protocol)
}
/// Getter for <https://url.spec.whatwg.org/#dom-url-username>
#[inline]
pub fn username(url: &Url) -> &str {
url.username()
}
/// Setter for <https://url.spec.whatwg.org/#dom-url-username>
#[allow(clippy::result_unit_err)]
pub fn set_username(url: &mut Url, new_username: &str) -> Result<(), ()> {
url.set_username(new_username)
}
/// Getter for <https://url.spec.whatwg.org/#dom-url-password>
#[inline]
pub fn password(url: &Url) -> &str {
url.password().unwrap_or("")
}
/// Setter for <https://url.spec.whatwg.org/#dom-url-password>
#[allow(clippy::result_unit_err)]
pub fn set_password(url: &mut Url, new_password: &str) -> Result<(), ()> {
url.set_password(if new_password.is_empty() {
None
} else {
Some(new_password)
})
}
/// Getter for <https://url.spec.whatwg.org/#dom-url-host>
#[inline]
pub fn host(url: &Url) -> &str {
&url[Position::BeforeHost..Position::AfterPort]
}
/// Setter for <https://url.spec.whatwg.org/#dom-url-host>
#[allow(clippy::result_unit_err)]
pub fn set_host(url: &mut Url, new_host: &str) -> Result<(), ()> {
// If context objects urls cannot-be-a-base-URL flag is set, then return.
if url.cannot_be_a_base() {
return Err(());
}
// Host parsing rules are strict,
// We don't want to trim the input
let input = Input::new_no_trim(new_host);
let host;
let opt_port;
{
let scheme = url.scheme();
let scheme_type = SchemeType::from(scheme);
if scheme_type == SchemeType::File && new_host.is_empty() {
url.set_host_internal(Host::Domain("".into()), None);
return Ok(());
}
if let Ok((h, remaining)) = Parser::parse_host(input, scheme_type) {
host = h;
opt_port = if let Some(remaining) = remaining.split_prefix(':') {
if remaining.is_empty() {
None
} else {
Parser::parse_port(remaining, || default_port(scheme), Context::Setter)
.ok()
.map(|(port, _remaining)| port)
}
} else {
None
};
} else {
return Err(());
}
}
// Make sure we won't set an empty host to a url with a username or a port
if host == Host::Domain("".to_string())
&& (!username(url).is_empty() || matches!(opt_port, Some(Some(_))) || url.port().is_some())
{
return Err(());
}
url.set_host_internal(host, opt_port);
Ok(())
}
/// Getter for <https://url.spec.whatwg.org/#dom-url-hostname>
#[inline]
pub fn hostname(url: &Url) -> &str {
url.host_str().unwrap_or("")
}
/// Setter for <https://url.spec.whatwg.org/#dom-url-hostname>
#[allow(clippy::result_unit_err)]
pub fn set_hostname(url: &mut Url, new_hostname: &str) -> Result<(), ()> {
if url.cannot_be_a_base() {
return Err(());
}
// Host parsing rules are strict we don't want to trim the input
let input = Input::new_no_trim(new_hostname);
let scheme_type = SchemeType::from(url.scheme());
if scheme_type == SchemeType::File && new_hostname.is_empty() {
url.set_host_internal(Host::Domain("".into()), None);
return Ok(());
}
if let Ok((host, remaining)) = Parser::parse_host(input, scheme_type) {
if remaining.starts_with(':') {
return Err(());
};
if let Host::Domain(h) = &host {
if h.is_empty() {
// Empty host on special not file url
if SchemeType::from(url.scheme()) == SchemeType::SpecialNotFile
// Port with an empty host
||!port(url).is_empty()
// Empty host that includes credentials
|| !url.username().is_empty()
|| !url.password().unwrap_or("").is_empty()
{
return Err(());
}
}
}
url.set_host_internal(host, None);
Ok(())
} else {
Err(())
}
}
/// Getter for <https://url.spec.whatwg.org/#dom-url-port>
#[inline]
pub fn port(url: &Url) -> &str {
&url[Position::BeforePort..Position::AfterPort]
}
/// Setter for <https://url.spec.whatwg.org/#dom-url-port>
#[allow(clippy::result_unit_err)]
pub fn set_port(url: &mut Url, new_port: &str) -> Result<(), ()> {
let result;
{
// has_host implies !cannot_be_a_base
let scheme = url.scheme();
if !url.has_host() || url.host() == Some(Host::Domain("")) || scheme == "file" {
return Err(());
}
result = Parser::parse_port(
Input::new_no_trim(new_port),
|| default_port(scheme),
Context::Setter,
)
}
if let Ok((new_port, _remaining)) = result {
url.set_port_internal(new_port);
Ok(())
} else {
Err(())
}
}
/// Getter for <https://url.spec.whatwg.org/#dom-url-pathname>
#[inline]
pub fn pathname(url: &Url) -> &str {
url.path()
}
/// Setter for <https://url.spec.whatwg.org/#dom-url-pathname>
pub fn set_pathname(url: &mut Url, new_pathname: &str) {
if url.cannot_be_a_base() {
return;
}
if new_pathname.starts_with('/')
|| (SchemeType::from(url.scheme()).is_special()
// \ is a segment delimiter for 'special' URLs"
&& new_pathname.starts_with('\\'))
{
url.set_path(new_pathname)
} else if SchemeType::from(url.scheme()).is_special()
|| !new_pathname.is_empty()
|| !url.has_host()
{
let mut path_to_set = String::from("/");
path_to_set.push_str(new_pathname);
url.set_path(&path_to_set)
} else {
url.set_path(new_pathname)
}
}
/// Getter for <https://url.spec.whatwg.org/#dom-url-search>
pub fn search(url: &Url) -> &str {
trim(&url[Position::AfterPath..Position::AfterQuery])
}
/// Setter for <https://url.spec.whatwg.org/#dom-url-search>
pub fn set_search(url: &mut Url, new_search: &str) {
url.set_query(match new_search {
"" => None,
_ if new_search.starts_with('?') => Some(&new_search[1..]),
_ => Some(new_search),
})
}
/// Getter for <https://url.spec.whatwg.org/#dom-url-hash>
pub fn hash(url: &Url) -> &str {
trim(&url[Position::AfterQuery..])
}
/// Setter for <https://url.spec.whatwg.org/#dom-url-hash>
pub fn set_hash(url: &mut Url, new_hash: &str) {
url.set_fragment(match new_hash {
// If the given value is the empty string,
// then set context objects urls fragment to null and return.
"" => None,
// Let input be the given value with a single leading U+0023 (#) removed, if any.
_ if new_hash.starts_with('#') => Some(&new_hash[1..]),
_ => Some(new_hash),
})
}
fn trim(s: &str) -> &str {
if s.len() == 1 {
""
} else {
s
}
}

218
vendor/url/src/slicing.rs vendored Normal file
View File

@@ -0,0 +1,218 @@
// Copyright 2016 The rust-url developers.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use core::ops::{Index, Range, RangeFrom, RangeFull, RangeTo};
use crate::Url;
impl Index<RangeFull> for Url {
type Output = str;
fn index(&self, _: RangeFull) -> &str {
&self.serialization
}
}
impl Index<RangeFrom<Position>> for Url {
type Output = str;
fn index(&self, range: RangeFrom<Position>) -> &str {
&self.serialization[self.index(range.start)..]
}
}
impl Index<RangeTo<Position>> for Url {
type Output = str;
fn index(&self, range: RangeTo<Position>) -> &str {
&self.serialization[..self.index(range.end)]
}
}
impl Index<Range<Position>> for Url {
type Output = str;
fn index(&self, range: Range<Position>) -> &str {
&self.serialization[self.index(range.start)..self.index(range.end)]
}
}
// Counts how many base-10 digits are required to represent n in the given base
fn count_digits(n: u16) -> usize {
match n {
0..=9 => 1,
10..=99 => 2,
100..=999 => 3,
1000..=9999 => 4,
10000..=65535 => 5,
}
}
#[test]
fn test_count_digits() {
assert_eq!(count_digits(0), 1);
assert_eq!(count_digits(1), 1);
assert_eq!(count_digits(9), 1);
assert_eq!(count_digits(10), 2);
assert_eq!(count_digits(99), 2);
assert_eq!(count_digits(100), 3);
assert_eq!(count_digits(9999), 4);
assert_eq!(count_digits(65535), 5);
}
/// Indicates a position within a URL based on its components.
///
/// A range of positions can be used for slicing `Url`:
///
/// ```rust
/// # use url::{Url, Position};
/// # fn something(some_url: Url) {
/// let serialization: &str = &some_url[..];
/// let serialization_without_fragment: &str = &some_url[..Position::AfterQuery];
/// let authority: &str = &some_url[Position::BeforeUsername..Position::AfterPort];
/// let data_url_payload: &str = &some_url[Position::BeforePath..Position::AfterQuery];
/// let scheme_relative: &str = &some_url[Position::BeforeUsername..];
/// # }
/// ```
///
/// In a pseudo-grammar (where `[`…`]?` makes a sub-sequence optional),
/// URL components and delimiters that separate them are:
///
/// ```notrust
/// url =
/// scheme ":"
/// [ "//" [ username [ ":" password ]? "@" ]? host [ ":" port ]? ]?
/// path [ "?" query ]? [ "#" fragment ]?
/// ```
///
/// When a given component is not present,
/// its "before" and "after" position are the same
/// (so that `&some_url[BeforeFoo..AfterFoo]` is the empty string)
/// and component ordering is preserved
/// (so that a missing query "is between" a path and a fragment).
///
/// The end of a component and the start of the next are either the same or separate
/// by a delimiter.
/// (Note that the initial `/` of a path is considered part of the path here, not a delimiter.)
/// For example, `&url[..BeforeFragment]` would include a `#` delimiter (if present in `url`),
/// so `&url[..AfterQuery]` might be desired instead.
///
/// `BeforeScheme` and `AfterFragment` are always the start and end of the entire URL,
/// so `&url[BeforeScheme..X]` is the same as `&url[..X]`
/// and `&url[X..AfterFragment]` is the same as `&url[X..]`.
#[derive(Copy, Clone, Debug)]
pub enum Position {
BeforeScheme,
AfterScheme,
BeforeUsername,
AfterUsername,
BeforePassword,
AfterPassword,
BeforeHost,
AfterHost,
BeforePort,
AfterPort,
BeforePath,
AfterPath,
BeforeQuery,
AfterQuery,
BeforeFragment,
AfterFragment,
}
impl Url {
#[inline]
fn index(&self, position: Position) -> usize {
match position {
Position::BeforeScheme => 0,
Position::AfterScheme => self.scheme_end as usize,
Position::BeforeUsername => {
if self.has_authority() {
self.scheme_end as usize + "://".len()
} else {
debug_assert!(self.byte_at(self.scheme_end) == b':');
debug_assert!(self.scheme_end + ":".len() as u32 == self.username_end);
self.scheme_end as usize + ":".len()
}
}
Position::AfterUsername => self.username_end as usize,
Position::BeforePassword => {
if self.has_authority() && self.byte_at(self.username_end) == b':' {
self.username_end as usize + ":".len()
} else {
debug_assert!(self.username_end == self.host_start);
self.username_end as usize
}
}
Position::AfterPassword => {
if self.has_authority() && self.byte_at(self.username_end) == b':' {
debug_assert!(self.byte_at(self.host_start - "@".len() as u32) == b'@');
self.host_start as usize - "@".len()
} else {
debug_assert!(self.username_end == self.host_start);
self.host_start as usize
}
}
Position::BeforeHost => self.host_start as usize,
Position::AfterHost => self.host_end as usize,
Position::BeforePort => {
if self.port.is_some() {
debug_assert!(self.byte_at(self.host_end) == b':');
self.host_end as usize + ":".len()
} else {
self.host_end as usize
}
}
Position::AfterPort => {
if let Some(port) = self.port {
debug_assert!(self.byte_at(self.host_end) == b':');
self.host_end as usize + ":".len() + count_digits(port)
} else {
self.host_end as usize
}
}
Position::BeforePath => self.path_start as usize,
Position::AfterPath => match (self.query_start, self.fragment_start) {
(Some(q), _) => q as usize,
(None, Some(f)) => f as usize,
(None, None) => self.serialization.len(),
},
Position::BeforeQuery => match (self.query_start, self.fragment_start) {
(Some(q), _) => {
debug_assert!(self.byte_at(q) == b'?');
q as usize + "?".len()
}
(None, Some(f)) => f as usize,
(None, None) => self.serialization.len(),
},
Position::AfterQuery => match self.fragment_start {
None => self.serialization.len(),
Some(f) => f as usize,
},
Position::BeforeFragment => match self.fragment_start {
Some(f) => {
debug_assert!(self.byte_at(f) == b'#');
f as usize + "#".len()
}
None => self.serialization.len(),
},
Position::AfterFragment => self.serialization.len(),
}
}
}

47
vendor/url/tests/expected_failures.txt vendored Normal file
View File

@@ -0,0 +1,47 @@
</> against <file://h/C:/a/b>
<file:\\\\//>
<file:\\\\\\\\>
<file:\\\\\\\\?fox>
<file:\\\\\\\\#guppy>
<file://spider///>
<file:\\\\localhost//>
<file://\\/localhost//cat>
<file://localhost//a//../..//>
</////mouse> against <file:///elephant>
<\\/localhost//pig> against <file://lion/>
<//localhost//pig> against <file://lion/>
</..//localhost//pig> against <file://lion/>
<C|> against <file://host/dir/file>
<C|> against <file://host/D:/dir1/dir2/file>
<C|#> against <file://host/dir/file>
<C|?> against <file://host/dir/file>
<C|/> against <file://host/dir/file>
<C|\n/> against <file://host/dir/file>
<C|\\> against <file://host/dir/file>
</c:/foo/bar> against <file://host/path>
<file://example.net/C:/>
<file://1.2.3.4/C:/>
<file://[1::8]/C:/>
<C|/> against <file://host/>
</C:/> against <file://host/>
<file:C:/> against <file://host/>
<file:/C:/> against <file://host/>
<file://localhost//a//../..//foo>
<file://localhost////foo>
<file:////foo>
<file:////one/two> against <file:///>
<////one/two> against <file:///>
<file:///.//> against <file:////>
<file:.//p>
<file:/.//p>
<non-spec:/.//p> set hostname to <h>
<non-spec:/.//p> set hostname to <>
<foo:///some/path> set pathname to <>
<file:///var/log/system.log> set href to <http://0300.168.0xF0>
<file://monkey/> set pathname to <\\\\>
<file:///unicorn> set pathname to <//\\/>
<file:///unicorn> set pathname to <//monkey/..//>
<non-spec:/> set pathname to </.//p>
<non-spec:/> set pathname to </..//p>
<non-spec:/> set pathname to <//p>
<non-spec:/.//> set pathname to <p>

2381
vendor/url/tests/setters_tests.json vendored Normal file

File diff suppressed because it is too large Load Diff

1394
vendor/url/tests/unit.rs vendored Normal file

File diff suppressed because it is too large Load Diff

9519
vendor/url/tests/urltestdata.json vendored Normal file

File diff suppressed because it is too large Load Diff

504
vendor/url/tests/wpt.rs vendored Normal file
View File

@@ -0,0 +1,504 @@
// Copyright 2013-2014 The rust-url developers.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! Data-driven tests imported from web-platform-tests
use serde_json::Value;
use std::collections::HashMap;
use std::fmt::Write;
#[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
use std::sync::Mutex;
use url::Url;
// https://rustwasm.github.io/wasm-bindgen/wasm-bindgen-test/usage.html
#[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
use wasm_bindgen_test::{console_log, wasm_bindgen_test, wasm_bindgen_test_configure};
#[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
wasm_bindgen_test_configure!(run_in_browser);
// wpt has its own test driver, but we shoe-horn this into wasm_bindgen_test
// which will discard stdout and stderr. So, we make println! go to
// console.log(), so we see failures that do not result in panics.
#[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
static PRINT_BUF: Mutex<Option<String>> = Mutex::new(None);
#[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
macro_rules! print {
($($arg:tt)*) => {
let v = format!($($arg)*);
{
let mut buf = PRINT_BUF.lock().unwrap();
if let Some(buf) = buf.as_mut() {
buf.push_str(&v);
} else {
*buf = Some(v);
}
}
};
}
#[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
macro_rules! println {
() => {
let buf = PRINT_BUF.lock().unwrap().take();
match buf {
Some(buf) => console_log!("{buf}"),
None => console_log!(""),
}
};
($($arg:tt)*) => {
let buf = PRINT_BUF.lock().unwrap().take();
match buf {
Some(buf) => {
let v = format!($($arg)*);
console_log!("{buf}{v}");
},
None => console_log!($($arg)*),
}
}
}
#[derive(Debug, serde_derive::Deserialize)]
struct UrlTest {
input: String,
base: Option<String>,
#[serde(flatten)]
result: UrlTestResult,
}
#[derive(Debug, serde_derive::Deserialize)]
#[serde(untagged)]
#[allow(clippy::large_enum_variant)]
enum UrlTestResult {
Ok(UrlTestOk),
Fail(UrlTestFail),
}
#[derive(Debug, serde_derive::Deserialize)]
struct UrlTestOk {
href: String,
protocol: String,
username: String,
password: String,
host: String,
hostname: String,
port: String,
pathname: String,
search: String,
hash: String,
}
#[derive(Debug, serde_derive::Deserialize)]
struct UrlTestFail {
failure: bool,
}
#[derive(Debug, serde_derive::Deserialize)]
struct SetterTest {
href: String,
new_value: String,
expected: SetterTestExpected,
}
#[derive(Debug, serde_derive::Deserialize)]
struct SetterTestExpected {
href: Option<String>,
protocol: Option<String>,
username: Option<String>,
password: Option<String>,
host: Option<String>,
hostname: Option<String>,
port: Option<String>,
pathname: Option<String>,
search: Option<String>,
hash: Option<String>,
}
#[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), wasm_bindgen_test)]
fn main() {
let mut filter = None;
let mut args = std::env::args().skip(1);
while filter.is_none() {
if let Some(arg) = args.next() {
if arg == "--test-threads" {
args.next();
continue;
}
filter = Some(arg);
} else {
break;
}
}
let mut expected_failures = include_str!("expected_failures.txt")
.lines()
.collect::<Vec<_>>();
let mut errors = vec![];
// Copied from https://github.com/web-platform-tests/wpt/blob/master/url/
let url_json: Vec<Value> = serde_json::from_str(include_str!("urltestdata.json"))
.expect("JSON parse error in urltestdata.json");
let url_tests = url_json
.into_iter()
.filter(|val| val.is_object())
.map(|val| serde_json::from_value::<UrlTest>(val).expect("parsing failed"))
.collect::<Vec<_>>();
let setter_json: HashMap<String, Value> =
serde_json::from_str(include_str!("setters_tests.json"))
.expect("JSON parse error in setters_tests.json");
let setter_tests = setter_json
.into_iter()
.filter(|(k, _)| k != "comment")
.map(|(k, v)| {
let test = serde_json::from_value::<Vec<SetterTest>>(v).expect("parsing failed");
(k, test)
})
.collect::<HashMap<_, _>>();
for url_test in url_tests {
let mut name = format!("<{}>", url_test.input.escape_default());
if let Some(base) = &url_test.base {
write!(&mut name, " against <{}>", base.escape_default()).unwrap();
}
if should_skip(&name, filter.as_deref()) {
continue;
}
print!("{name} ... ");
let res = run_url_test(url_test);
report(name, res, &mut errors, &mut expected_failures);
}
for (kind, tests) in setter_tests {
for test in tests {
let name = format!(
"<{}> set {} to <{}>",
test.href.escape_default(),
kind,
test.new_value.escape_default()
);
if should_skip(&name, filter.as_deref()) {
continue;
}
print!("{name} ... ");
let res = run_setter_test(&kind, test);
report(name, res, &mut errors, &mut expected_failures);
}
}
println!();
println!("====================");
println!();
if !errors.is_empty() {
println!("errors:");
println!();
for (name, err) in errors {
println!(" name: {name}");
println!(" err: {err}");
println!();
}
std::process::exit(1);
} else {
println!("all tests passed");
}
if !expected_failures.is_empty() && filter.is_none() {
println!();
println!("====================");
println!();
println!("tests were expected to fail but did not run:");
println!();
for name in expected_failures {
println!(" {name}");
}
println!();
println!("if these tests were removed, update expected_failures.txt");
println!();
std::process::exit(1);
}
}
fn should_skip(name: &str, filter: Option<&str>) -> bool {
match filter {
Some(filter) => !name.contains(filter),
None => false,
}
}
fn report(
name: String,
res: Result<(), String>,
errors: &mut Vec<(String, String)>,
expected_failures: &mut Vec<&str>,
) {
let expected_failure = expected_failures.contains(&&*name);
expected_failures.retain(|&s| s != &*name);
match res {
Ok(()) => {
if expected_failure {
println!("🟠 (unexpected success)");
errors.push((name, "unexpected success".to_string()));
} else {
println!("");
}
}
Err(err) => {
if expected_failure {
println!("✅ (expected fail)");
} else {
println!("");
errors.push((name, err));
}
}
}
}
fn run_url_test(
UrlTest {
base,
input,
result,
}: UrlTest,
) -> Result<(), String> {
let base = match base {
Some(base) => {
let base = Url::parse(&base).map_err(|e| format!("errored while parsing base: {e}"))?;
Some(base)
}
None => None,
};
let res = Url::options()
.base_url(base.as_ref())
.parse(&input)
.map_err(|e| format!("errored while parsing input: {e}"));
match result {
UrlTestResult::Ok(ok) => check_url_ok(res, ok),
UrlTestResult::Fail(fail) => {
assert!(fail.failure);
if res.is_ok() {
return Err("expected failure, but parsed successfully".to_string());
}
Ok(())
}
}
}
fn check_url_ok(res: Result<Url, String>, ok: UrlTestOk) -> Result<(), String> {
let url = match res {
Ok(url) => url,
Err(err) => {
return Err(format!("expected success, but errored: {err:?}"));
}
};
let href = url::quirks::href(&url);
if href != ok.href {
return Err(format!("expected href {:?}, but got {:?}", ok.href, href));
}
let protocol = url::quirks::protocol(&url);
if protocol != ok.protocol {
return Err(format!(
"expected protocol {:?}, but got {:?}",
ok.protocol, protocol
));
}
let username = url::quirks::username(&url);
if username != ok.username {
return Err(format!(
"expected username {:?}, but got {:?}",
ok.username, username
));
}
let password = url::quirks::password(&url);
if password != ok.password {
return Err(format!(
"expected password {:?}, but got {:?}",
ok.password, password
));
}
let host = url::quirks::host(&url);
if host != ok.host {
return Err(format!("expected host {:?}, but got {:?}", ok.host, host));
}
let hostname = url::quirks::hostname(&url);
if hostname != ok.hostname {
return Err(format!(
"expected hostname {:?}, but got {:?}",
ok.hostname, hostname
));
}
let port = url::quirks::port(&url);
if port != ok.port {
return Err(format!("expected port {:?}, but got {:?}", ok.port, port));
}
let pathname = url::quirks::pathname(&url);
if pathname != ok.pathname {
return Err(format!(
"expected pathname {:?}, but got {:?}",
ok.pathname, pathname
));
}
let search = url::quirks::search(&url);
if search != ok.search {
return Err(format!(
"expected search {:?}, but got {:?}",
ok.search, search
));
}
let hash = url::quirks::hash(&url);
if hash != ok.hash {
return Err(format!("expected hash {:?}, but got {:?}", ok.hash, hash));
}
Ok(())
}
fn run_setter_test(
kind: &str,
SetterTest {
href,
new_value,
expected,
}: SetterTest,
) -> Result<(), String> {
let mut url = Url::parse(&href).map_err(|e| format!("errored while parsing href: {e}"))?;
match kind {
"protocol" => {
url::quirks::set_protocol(&mut url, &new_value).ok();
}
"username" => {
url::quirks::set_username(&mut url, &new_value).ok();
}
"password" => {
url::quirks::set_password(&mut url, &new_value).ok();
}
"host" => {
url::quirks::set_host(&mut url, &new_value).ok();
}
"hostname" => {
url::quirks::set_hostname(&mut url, &new_value).ok();
}
"port" => {
url::quirks::set_port(&mut url, &new_value).ok();
}
"pathname" => url::quirks::set_pathname(&mut url, &new_value),
"search" => url::quirks::set_search(&mut url, &new_value),
"hash" => url::quirks::set_hash(&mut url, &new_value),
_ => {
return Err(format!("unknown setter kind: {kind:?}"));
}
}
if let Some(expected_href) = expected.href {
let href = url::quirks::href(&url);
if href != expected_href {
return Err(format!("expected href {expected_href:?}, but got {href:?}"));
}
}
if let Some(expected_protocol) = expected.protocol {
let protocol = url::quirks::protocol(&url);
if protocol != expected_protocol {
return Err(format!(
"expected protocol {expected_protocol:?}, but got {protocol:?}"
));
}
}
if let Some(expected_username) = expected.username {
let username = url::quirks::username(&url);
if username != expected_username {
return Err(format!(
"expected username {expected_username:?}, but got {username:?}"
));
}
}
if let Some(expected_password) = expected.password {
let password = url::quirks::password(&url);
if password != expected_password {
return Err(format!(
"expected password {expected_password:?}, but got {password:?}"
));
}
}
if let Some(expected_host) = expected.host {
let host = url::quirks::host(&url);
if host != expected_host {
return Err(format!("expected host {expected_host:?}, but got {host:?}"));
}
}
if let Some(expected_hostname) = expected.hostname {
let hostname = url::quirks::hostname(&url);
if hostname != expected_hostname {
return Err(format!(
"expected hostname {expected_hostname:?}, but got {hostname:?}"
));
}
}
if let Some(expected_port) = expected.port {
let port = url::quirks::port(&url);
if port != expected_port {
return Err(format!("expected port {expected_port:?}, but got {port:?}"));
}
}
if let Some(expected_pathname) = expected.pathname {
let pathname = url::quirks::pathname(&url);
if pathname != expected_pathname {
return Err(format!(
"expected pathname {expected_pathname:?}, but got {pathname:?}"
));
}
}
if let Some(expected_search) = expected.search {
let search = url::quirks::search(&url);
if search != expected_search {
return Err(format!(
"expected search {expected_search:?}, but got {search:?}"
));
}
}
if let Some(expected_hash) = expected.hash {
let hash = url::quirks::hash(&url);
if hash != expected_hash {
return Err(format!("expected hash {expected_hash:?}, but got {hash:?}"));
}
}
Ok(())
}