Vendor dependencies

This commit is contained in:
2026-08-01 16:11:49 +03:00
parent 7f139a0241
commit 6b5e7f0f8b
29706 changed files with 9575646 additions and 0 deletions
+1
View File
@@ -0,0 +1 @@
{"$comment":"This file only protects against accidental modifications. It is not a security mechanism and does not protect against malicious changes.","files":{".cargo_vcs_info.json":"e8226884ef29d9d29490a621d35c2d8842b7917f61dfc9c4ad2853ad19797479","Cargo.lock":"1950661c3f529d11223e10ab8a32125f31bfabc449b88cd669fd445cb9f70f29","Cargo.toml":"0197e2dac96a2907f88a2bf202602ceb229ccb1171d434c6396e927517d23af9","Cargo.toml.orig":"b8710076359569fc879131eef4f67753f802250aa5b8bb1857c8a25e52cf8822","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"86dd7f026f916daf7511e39951ad8ea8cf55a8db67ae64060dacf829761c18f3","interface/mod.rs":"726c9138b06f9eefccfa0608ccdf657e0fe4a1c0f771f56a6d9cef612ede58ed","interface/tree_builder.rs":"1a1aee13103be919027c772137859d7b143694898b41fa8c61cee73235293dbd","lib.rs":"4043ce4ca34ec2ca62ac1a078df937197802f2d025c22be5041d91023033847c","serialize.rs":"871f613fdc4e9e8508cf12bf74087d4c39a5e19c5701324961e7e13491c0b7ac","util/buffer_queue.rs":"5d0bcdd8b2d0e4aa7c7c881c44ae66258e384a00f3faeadfc4dc7086a1a63f97","util/smallcharset.rs":"97ee1244744875deba3664c4c6eb638ddd69c68c4699899dbac4df26c3efcac8"},"package":"6c3294c4d74d0742910f8c7b466f44dda9eb2d5742c1e430138df290a1e8451c"}
+6
View File
@@ -0,0 +1,6 @@
{
"git": {
"sha1": "34059007a855d284608f9447ed74c0c209bc0ad8"
},
"path_in_vcs": "markup5ever"
}
+292
View File
@@ -0,0 +1,292 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "bitflags"
version = "2.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3"
[[package]]
name = "cfg-if"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
[[package]]
name = "fastrand"
version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
[[package]]
name = "futf"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df420e2e84819663797d1ec6544b13c5be84629e7bb00dc960d6917db2987843"
dependencies = [
"mac",
"new_debug_unreachable",
]
[[package]]
name = "libc"
version = "0.2.177"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976"
[[package]]
name = "lock_api"
version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965"
dependencies = [
"scopeguard",
]
[[package]]
name = "log"
version = "0.4.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432"
[[package]]
name = "mac"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4"
[[package]]
name = "markup5ever"
version = "0.36.1"
dependencies = [
"log",
"tendril",
"web_atoms",
]
[[package]]
name = "new_debug_unreachable"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086"
[[package]]
name = "parking_lot"
version = "0.12.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a"
dependencies = [
"lock_api",
"parking_lot_core",
]
[[package]]
name = "parking_lot_core"
version = "0.9.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1"
dependencies = [
"cfg-if",
"libc",
"redox_syscall",
"smallvec",
"windows-link",
]
[[package]]
name = "phf"
version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c1562dc717473dbaa4c1f85a36410e03c047b2e7df7f45ee938fbef64ae7fadf"
dependencies = [
"phf_shared",
"serde",
]
[[package]]
name = "phf_codegen"
version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49aa7f9d80421bca176ca8dbfebe668cc7a2684708594ec9f3c0db0805d5d6e1"
dependencies = [
"phf_generator",
"phf_shared",
]
[[package]]
name = "phf_generator"
version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "135ace3a761e564ec88c03a77317a7c6b80bb7f7135ef2544dbe054243b89737"
dependencies = [
"fastrand",
"phf_shared",
]
[[package]]
name = "phf_shared"
version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e57fef6bc5981e38c2ce2d63bfa546861309f875b8a75f092d1d54ae2d64f266"
dependencies = [
"siphasher",
]
[[package]]
name = "precomputed-hash"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c"
[[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.42"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f"
dependencies = [
"proc-macro2",
]
[[package]]
name = "redox_syscall"
version = "0.5.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d"
dependencies = [
"bitflags",
]
[[package]]
name = "scopeguard"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[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 = "siphasher"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d"
[[package]]
name = "smallvec"
version = "1.15.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
[[package]]
name = "string_cache"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a18596f8c785a729f2819c0f6a7eae6ebeebdfffbfe4214ae6b087f690e31901"
dependencies = [
"new_debug_unreachable",
"parking_lot",
"phf_shared",
"precomputed-hash",
"serde",
]
[[package]]
name = "string_cache_codegen"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "585635e46db231059f76c5849798146164652513eb9e8ab2685939dd90f29b69"
dependencies = [
"phf_generator",
"phf_shared",
"proc-macro2",
"quote",
]
[[package]]
name = "syn"
version = "2.0.110"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a99801b5bd34ede4cf3fc688c5919368fea4e4814a4664359503e6015b280aea"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "tendril"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d24a120c5fc464a3458240ee02c299ebcb9d67b5249c8848b09d639dca8d7bb0"
dependencies = [
"futf",
"mac",
"utf-8",
]
[[package]]
name = "unicode-ident"
version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5"
[[package]]
name = "utf-8"
version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9"
[[package]]
name = "web_atoms"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "acd0c322f146d0f8aad130ce6c187953889359584497dac6561204c8e17bb43d"
dependencies = [
"phf",
"phf_codegen",
"string_cache",
"string_cache_codegen",
]
[[package]]
name = "windows-link"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
+45
View File
@@ -0,0 +1,45 @@
# 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 = "2021"
rust-version = "1.71.0"
name = "markup5ever"
version = "0.36.1"
authors = ["The html5ever Project Developers"]
build = false
autolib = false
autobins = false
autoexamples = false
autotests = false
autobenches = false
description = "Common code for xml5ever and html5ever"
documentation = "https://docs.rs/markup5ever"
readme = false
categories = [
"parser-implementations",
"web-programming",
]
license = "MIT OR Apache-2.0"
repository = "https://github.com/servo/html5ever"
[lib]
name = "markup5ever"
path = "lib.rs"
[dependencies.log]
version = "0.4"
[dependencies.tendril]
version = "0.4.3"
[dependencies.web_atoms]
version = "0.2"
+19
View File
@@ -0,0 +1,19 @@
[package]
name = "markup5ever"
description = "Common code for xml5ever and html5ever"
documentation = "https://docs.rs/markup5ever"
categories = [ "parser-implementations", "web-programming" ]
version.workspace = true
license.workspace = true
authors.workspace = true
repository.workspace = true
edition.workspace = true
rust-version.workspace = true
[lib]
path = "lib.rs"
[dependencies]
web_atoms = { workspace = true }
tendril = { workspace = true }
log = { workspace = true }
+201
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
View File
@@ -0,0 +1,25 @@
Copyright (c) 2014 The html5ever Project 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.
+421
View File
@@ -0,0 +1,421 @@
// Copyright 2014-2017 The html5ever Project Developers. See the
// COPYRIGHT file at the top-level directory of this distribution.
//
// 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.
//! Types for tag and attribute names, and tree-builder functionality.
use std::cell::Ref;
use std::fmt;
use tendril::StrTendril;
use web_atoms::{LocalName, Namespace, Prefix};
pub use self::tree_builder::{create_element, AppendNode, AppendText, ElementFlags, NodeOrText};
pub use self::tree_builder::{ElemName, Tracer, TreeSink};
pub use self::tree_builder::{LimitedQuirks, NoQuirks, Quirks, QuirksMode};
/// An [expanded name], containing the tag and the namespace.
///
/// [expanded name]: https://www.w3.org/TR/REC-xml-names/#dt-expname
#[derive(Copy, Clone, Eq, Hash, PartialEq)]
pub struct ExpandedName<'a> {
pub ns: &'a Namespace,
pub local: &'a LocalName,
}
impl ElemName for ExpandedName<'_> {
#[inline(always)]
fn ns(&self) -> &Namespace {
self.ns
}
#[inline(always)]
fn local_name(&self) -> &LocalName {
self.local
}
}
impl<'a> ElemName for Ref<'a, ExpandedName<'a>> {
#[inline(always)]
fn ns(&self) -> &Namespace {
self.ns
}
#[inline(always)]
fn local_name(&self) -> &LocalName {
self.local
}
}
impl fmt::Debug for ExpandedName<'_> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if self.ns.is_empty() {
write!(f, "{}", self.local)
} else {
write!(f, "{{{}}}:{}", self.ns, self.local)
}
}
}
#[must_use]
#[derive(Debug, PartialEq)]
pub enum TokenizerResult<Handle> {
Done,
Script(Handle),
}
/// Helper to quickly create an expanded name.
///
/// Can be used with no namespace as `expanded_name!("", "some_name")`
/// or with a namespace as `expanded_name!(ns "some_name")`. In the
/// latter case, `ns` is one of the symbols which the [`ns!`][ns]
/// macro accepts; note the lack of a comma between the `ns` and
/// `"some_name"`.
///
/// [ns]: macro.ns.html
///
/// # Examples
///
/// ```
/// # #[macro_use] extern crate markup5ever;
///
/// # fn main() {
/// use markup5ever::ExpandedName;
///
/// assert_eq!(
/// expanded_name!("", "div"),
/// ExpandedName {
/// ns: &ns!(),
/// local: &local_name!("div")
/// }
/// );
///
/// assert_eq!(
/// expanded_name!(html "div"),
/// ExpandedName {
/// ns: &ns!(html),
/// local: &local_name!("div")
/// }
/// );
/// # }
#[macro_export]
macro_rules! expanded_name {
("", $local: tt) => {
$crate::interface::ExpandedName {
ns: &ns!(),
local: &local_name!($local),
}
};
($ns: ident $local: tt) => {
$crate::interface::ExpandedName {
ns: &ns!($ns),
local: &local_name!($local),
}
};
}
pub mod tree_builder;
/// A fully qualified name (with a namespace), used to depict names of tags and attributes.
///
/// Namespaces can be used to differentiate between similar XML fragments. For example:
///
/// ```text
/// // HTML
/// <table>
/// <tr>
/// <td>Apples</td>
/// <td>Bananas</td>
/// </tr>
/// </table>
///
/// // Furniture XML
/// <table>
/// <name>African Coffee Table</name>
/// <width>80</width>
/// <length>120</length>
/// </table>
/// ```
///
/// Without XML namespaces, we can't use those two fragments in the same document
/// at the same time. However if we declare a namespace we could instead say:
///
/// ```text
///
/// // Furniture XML
/// <furn:table xmlns:furn="https://furniture.rs">
/// <furn:name>African Coffee Table</furn:name>
/// <furn:width>80</furn:width>
/// <furn:length>120</furn:length>
/// </furn:table>
/// ```
///
/// and bind the prefix `furn` to a different namespace.
///
/// For this reason we parse names that contain a colon in the following way:
///
/// ```text
/// <furn:table>
/// | |
/// | +- local name
/// |
/// prefix (when resolved gives namespace_url `https://furniture.rs`)
/// ```
///
/// NOTE: `Prefix`, `LocalName` and `Prefix` all implement `Deref<str>`.
///
#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone)]
pub struct QualName {
/// The prefix of qualified (e.g. `furn` in `<furn:table>` above).
/// Optional (since some namespaces can be empty or inferred), and
/// only useful for namespace resolution (since different prefix
/// can still resolve to same namespace)
///
/// ```
///
/// # fn main() {
/// use markup5ever::{QualName, Namespace, LocalName, Prefix};
///
/// let qual = QualName::new(
/// Some(Prefix::from("furn")),
/// Namespace::from("https://furniture.rs"),
/// LocalName::from("table"),
/// );
///
/// assert_eq!("furn", &qual.prefix.unwrap());
///
/// # }
/// ```
pub prefix: Option<Prefix>,
/// The namespace after resolution (e.g. `https://furniture.rs` in example above).
///
/// ```
/// # use markup5ever::{QualName, Namespace, LocalName, Prefix};
///
/// # fn main() {
/// # let qual = QualName::new(
/// # Some(Prefix::from("furn")),
/// # Namespace::from("https://furniture.rs"),
/// # LocalName::from("table"),
/// # );
///
/// assert_eq!("https://furniture.rs", &qual.ns);
/// # }
/// ```
///
/// When matching namespaces used by HTML we can use `ns!` macro.
/// Although keep in mind that ns! macro only works with namespaces
/// that are present in HTML spec (like `html`, `xmlns`, `svg`, etc.).
///
/// ```
/// #[macro_use] extern crate markup5ever;
///
/// # use markup5ever::{QualName, Namespace, LocalName, Prefix};
///
/// let html_table = QualName::new(
/// None,
/// ns!(html),
/// LocalName::from("table"),
/// );
///
/// assert!(
/// match html_table.ns {
/// ns!(html) => true,
/// _ => false,
/// }
/// );
///
/// ```
pub ns: Namespace,
/// The local name (e.g. `table` in `<furn:table>` above).
///
/// ```
/// # use markup5ever::{QualName, Namespace, LocalName, Prefix};
///
/// # fn main() {
/// # let qual = QualName::new(
/// # Some(Prefix::from("furn")),
/// # Namespace::from("https://furniture.rs"),
/// # LocalName::from("table"),
/// # );
///
/// assert_eq!("table", &qual.local);
/// # }
/// ```
/// When matching local name we can also use the `local_name!` macro:
///
/// ```
/// #[macro_use] extern crate markup5ever;
///
/// # use markup5ever::{QualName, Namespace, LocalName, Prefix};
///
/// # let qual = QualName::new(
/// # Some(Prefix::from("furn")),
/// # Namespace::from("https://furniture.rs"),
/// # LocalName::from("table"),
/// # );
///
/// // Initialize qual to furniture example
///
/// assert!(
/// match qual.local {
/// local_name!("table") => true,
/// _ => false,
/// }
/// );
///
/// ```
pub local: LocalName,
}
impl ElemName for Ref<'_, QualName> {
#[inline(always)]
fn ns(&self) -> &Namespace {
&self.ns
}
#[inline(always)]
fn local_name(&self) -> &LocalName {
&self.local
}
}
impl ElemName for &QualName {
#[inline(always)]
fn ns(&self) -> &Namespace {
&self.ns
}
#[inline(always)]
fn local_name(&self) -> &LocalName {
&self.local
}
}
impl QualName {
/// Basic constructor function.
///
/// First let's try it for the following example where `QualName`
/// is defined as:
/// ```text
/// <furn:table> <!-- namespace url is https://furniture.rs -->
/// ```
///
/// Given this definition, we can define `QualName` using strings.
///
/// ```
/// use markup5ever::{QualName, Namespace, LocalName, Prefix};
///
/// # fn main() {
/// let qual_name = QualName::new(
/// Some(Prefix::from("furn")),
/// Namespace::from("https://furniture.rs"),
/// LocalName::from("table"),
/// );
/// # }
/// ```
///
/// If we were instead to construct this element instead:
///
/// ```text
///
/// <table>
/// ^^^^^---- no prefix and thus default html namespace
///
/// ```
///
/// Or could define it using macros, like so:
///
/// ```
/// #[macro_use] extern crate markup5ever;
/// use markup5ever::{QualName, Namespace, LocalName, Prefix};
///
/// # fn main() {
/// let qual_name = QualName::new(
/// None,
/// ns!(html),
/// local_name!("table")
/// );
/// # }
/// ```
///
/// Let's analyse the above example.
/// Since we have no prefix its value is None. Second we have html namespace.
/// In html5ever html namespaces are supported out of the box,
/// we can write `ns!(html)` instead of typing `Namespace::from("http://www.w3.org/1999/xhtml")`.
/// Local name is also one of the HTML elements local names, so can
/// use `local_name!("table")` macro.
///
#[inline]
pub fn new(prefix: Option<Prefix>, ns: Namespace, local: LocalName) -> QualName {
QualName { prefix, ns, local }
}
/// Take a reference of `self` as an `ExpandedName`, dropping the unresolved prefix.
///
/// In XML and HTML prefixes are only used to extract the relevant namespace URI.
/// Expanded name only contains resolved namespace and tag name, which are only
/// relevant parts of an XML or HTML tag and attribute name respectively.
///
/// In lieu of our XML Namespace example
///
/// ```text
/// <furn:table> <!-- namespace url is https://furniture.rs -->
/// ```
/// For it the expanded name would become roughly equivalent to:
///
/// ```text
/// ExpandedName {
/// ns: "https://furniture.rs",
/// local: "table",
/// }
/// ```
///
#[inline]
pub fn expanded(&self) -> ExpandedName<'_> {
ExpandedName {
ns: &self.ns,
local: &self.local,
}
}
}
/// A tag attribute, e.g. `class="test"` in `<div class="test" ...>`.
///
/// The namespace on the attribute name is almost always ns!("").
/// The tokenizer creates all attributes this way, but the tree
/// builder will adjust certain attribute names inside foreign
/// content (MathML, SVG).
#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Debug)]
pub struct Attribute {
/// The name of the attribute (e.g. the `class` in `<div class="test">`)
pub name: QualName,
/// The value of the attribute (e.g. the `"test"` in `<div class="test">`)
pub value: StrTendril,
}
#[cfg(test)]
mod tests {
use web_atoms::{ns, Namespace};
#[test]
fn ns_macro() {
assert_eq!(ns!(), Namespace::from(""));
assert_eq!(ns!(html), Namespace::from("http://www.w3.org/1999/xhtml"));
assert_eq!(
ns!(xml),
Namespace::from("http://www.w3.org/XML/1998/namespace")
);
assert_eq!(ns!(xmlns), Namespace::from("http://www.w3.org/2000/xmlns/"));
assert_eq!(ns!(xlink), Namespace::from("http://www.w3.org/1999/xlink"));
assert_eq!(ns!(svg), Namespace::from("http://www.w3.org/2000/svg"));
assert_eq!(
ns!(mathml),
Namespace::from("http://www.w3.org/1998/Math/MathML")
);
}
}
@@ -0,0 +1,272 @@
// Copyright 2014-2017 The html5ever Project Developers. See the
// COPYRIGHT file at the top-level directory of this distribution.
//
// 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.
//! This module contains functionality for managing the DOM, including adding/removing nodes.
//!
//! It can be used by a parser to create the DOM graph structure in memory.
use crate::interface::{Attribute, ExpandedName, QualName};
use std::borrow::Cow;
use std::fmt::Debug;
use tendril::StrTendril;
use web_atoms::{LocalName, Namespace};
pub use self::NodeOrText::{AppendNode, AppendText};
pub use self::QuirksMode::{LimitedQuirks, NoQuirks, Quirks};
/// Something which can be inserted into the DOM.
///
/// Adjacent sibling text nodes are merged into a single node, so
/// the sink may not want to allocate a `Handle` for each.
pub enum NodeOrText<Handle> {
AppendNode(Handle),
AppendText(StrTendril),
}
/// A document's quirks mode, for compatibility with old browsers. See [quirks mode on wikipedia]
/// for more information.
///
/// [quirks mode on wikipedia]: https://en.wikipedia.org/wiki/Quirks_mode
#[derive(PartialEq, Eq, Copy, Clone, Hash, Debug)]
pub enum QuirksMode {
/// Full quirks mode
Quirks,
/// Almost standards mode
LimitedQuirks,
/// Standards mode
NoQuirks,
}
/// Special properties of an element, useful for tagging elements with this information.
#[derive(Default)]
#[non_exhaustive]
pub struct ElementFlags {
/// A document fragment should be created, associated with the element,
/// and returned in TreeSink::get_template_contents.
///
/// See [template-contents in the whatwg spec][whatwg template-contents].
///
/// [whatwg template-contents]: https://html.spec.whatwg.org/multipage/#template-contents
pub template: bool,
/// This boolean should be recorded with the element and returned
/// in TreeSink::is_mathml_annotation_xml_integration_point
///
/// See [html-integration-point in the whatwg spec][whatwg integration-point].
///
/// [whatwg integration-point]: https://html.spec.whatwg.org/multipage/#html-integration-point
pub mathml_annotation_xml_integration_point: bool,
}
/// A constructor for an element.
///
/// # Examples
///
/// Create an element like `<div class="test-class-name"></div>`:
pub fn create_element<Sink>(sink: &Sink, name: QualName, attrs: Vec<Attribute>) -> Sink::Handle
where
Sink: TreeSink,
{
let mut flags = ElementFlags::default();
match name.expanded() {
expanded_name!(html "template") => flags.template = true,
expanded_name!(mathml "annotation-xml") => {
flags.mathml_annotation_xml_integration_point = attrs.iter().any(|attr| {
attr.name.expanded() == expanded_name!("", "encoding")
&& (attr.value.eq_ignore_ascii_case("text/html")
|| attr.value.eq_ignore_ascii_case("application/xhtml+xml"))
})
},
_ => {},
}
sink.create_element(name, attrs, flags)
}
/// An abstraction over any type that can represent an element's local name and namespace.
pub trait ElemName: Debug {
fn ns(&self) -> &Namespace;
fn local_name(&self) -> &LocalName;
#[inline(always)]
fn expanded(&self) -> ExpandedName<'_> {
ExpandedName {
ns: self.ns(),
local: self.local_name(),
}
}
}
/// Methods a parser can use to create the DOM. The DOM provider implements this trait.
///
/// Having this as a trait potentially allows multiple implementations of the DOM to be used with
/// the same parser.
pub trait TreeSink {
/// `Handle` is a reference to a DOM node. The tree builder requires
/// that a `Handle` implements `Clone` to get another reference to
/// the same node.
type Handle: Clone;
/// The overall result of parsing.
///
/// This should default to Self, but default associated types are not stable yet.
/// [rust-lang/rust#29661](https://github.com/rust-lang/rust/issues/29661)
type Output;
//
type ElemName<'a>: ElemName
where
Self: 'a;
/// Consume this sink and return the overall result of parsing.
///
/// TODO:This should default to `fn finish(self) -> Self::Output { self }`,
/// but default associated types are not stable yet.
/// [rust-lang/rust#29661](https://github.com/rust-lang/rust/issues/29661)
fn finish(self) -> Self::Output;
/// Signal a parse error.
fn parse_error(&self, msg: Cow<'static, str>);
/// Get a handle to the `Document` node.
fn get_document(&self) -> Self::Handle;
/// What is the name of this element?
///
/// Should never be called on a non-element node;
/// feel free to `panic!`.
fn elem_name<'a>(&'a self, target: &'a Self::Handle) -> Self::ElemName<'a>;
/// Create an element.
///
/// When creating a template element (`name.ns.expanded() == expanded_name!(html "template")`),
/// an associated document fragment called the "template contents" should
/// also be created. Later calls to self.get_template_contents() with that
/// given element return it.
/// See [the template element in the whatwg spec][whatwg template].
///
/// [whatwg template]: https://html.spec.whatwg.org/multipage/#the-template-element
fn create_element(
&self,
name: QualName,
attrs: Vec<Attribute>,
flags: ElementFlags,
) -> Self::Handle;
/// Create a comment node.
fn create_comment(&self, text: StrTendril) -> Self::Handle;
/// Create a Processing Instruction node.
fn create_pi(&self, target: StrTendril, data: StrTendril) -> Self::Handle;
/// Append a node as the last child of the given node. If this would
/// produce adjacent sibling text nodes, it should concatenate the text
/// instead.
///
/// The child node will not already have a parent.
fn append(&self, parent: &Self::Handle, child: NodeOrText<Self::Handle>);
/// When the insertion point is decided by the existence of a parent node of the
/// element, we consider both possibilities and send the element which will be used
/// if a parent node exists, along with the element to be used if there isn't one.
fn append_based_on_parent_node(
&self,
element: &Self::Handle,
prev_element: &Self::Handle,
child: NodeOrText<Self::Handle>,
);
/// Append a `DOCTYPE` element to the `Document` node.
fn append_doctype_to_document(
&self,
name: StrTendril,
public_id: StrTendril,
system_id: StrTendril,
);
/// Mark a HTML `<script>` as "already started".
fn mark_script_already_started(&self, _node: &Self::Handle) {}
/// Indicate that a node was popped off the stack of open elements.
fn pop(&self, _node: &Self::Handle) {}
/// Get a handle to a template's template contents. The tree builder
/// promises this will never be called with something else than
/// a template element.
fn get_template_contents(&self, target: &Self::Handle) -> Self::Handle;
/// Do two handles refer to the same node?
fn same_node(&self, x: &Self::Handle, y: &Self::Handle) -> bool;
/// Set the document's quirks mode.
fn set_quirks_mode(&self, mode: QuirksMode);
/// Append a node as the sibling immediately before the given node.
///
/// The tree builder promises that `sibling` is not a text node. However its
/// old previous sibling, which would become the new node's previous sibling,
/// could be a text node. If the new node is also a text node, the two should
/// be merged, as in the behavior of `append`.
///
/// NB: `new_node` may have an old parent, from which it should be removed.
fn append_before_sibling(&self, sibling: &Self::Handle, new_node: NodeOrText<Self::Handle>);
/// Add each attribute to the given element, if no attribute with that name
/// already exists. The tree builder promises this will never be called
/// with something else than an element.
fn add_attrs_if_missing(&self, target: &Self::Handle, attrs: Vec<Attribute>);
/// Associate the given form-associatable element with the form element
fn associate_with_form(
&self,
_target: &Self::Handle,
_form: &Self::Handle,
_nodes: (&Self::Handle, Option<&Self::Handle>),
) {
}
/// Detach the given node from its parent.
fn remove_from_parent(&self, target: &Self::Handle);
/// Remove all the children from node and append them to new_parent.
fn reparent_children(&self, node: &Self::Handle, new_parent: &Self::Handle);
/// Returns true if the adjusted current node is an HTML integration point
/// and the token is a start tag.
fn is_mathml_annotation_xml_integration_point(&self, _handle: &Self::Handle) -> bool {
false
}
/// Called whenever the line number changes.
fn set_current_line(&self, _line_number: u64) {}
fn allow_declarative_shadow_roots(&self, _intended_parent: &Self::Handle) -> bool {
true
}
/// Attempt to attach a declarative shadow root at the given location.
///
/// Returns a boolean indicating whether the operation succeeded or not.
fn attach_declarative_shadow(
&self,
_location: &Self::Handle,
_template: &Self::Handle,
_attrs: &[Attribute],
) -> bool {
false
}
}
/// Trace hooks for a garbage-collected DOM.
pub trait Tracer {
type Handle;
/// Upon a call to `trace_handles`, the tree builder will call this method
/// for each handle in its internal state.
fn trace_handle(&self, node: &Self::Handle);
}
+55
View File
@@ -0,0 +1,55 @@
// Copyright 2014-2017 The html5ever Project Developers. See the
// COPYRIGHT file at the top-level directory of this distribution.
//
// 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.
pub use tendril;
#[macro_use]
extern crate web_atoms;
/// Create a [`SmallCharSet`], with each space-separated number stored in the set.
///
/// # Examples
///
/// ```
/// # #[macro_use] extern crate markup5ever;
/// # fn main() {
/// let set = small_char_set!(12 54 42);
/// assert_eq!(set.bits,
/// 0b00000000_01000000_00000100_00000000_00000000_00000000_00010000_00000000);
/// # }
/// ```
///
/// [`SmallCharSet`]: struct.SmallCharSet.html
#[macro_export]
macro_rules! small_char_set ( ($($e:expr)+) => (
$ crate ::SmallCharSet {
bits: $( (1 << ($e as usize)) )|+
}
));
pub use web_atoms::{
local_name, namespace_prefix, namespace_url, ns, LocalName, LocalNameStaticSet, Namespace,
NamespaceStaticSet, Prefix, PrefixStaticSet,
};
pub mod data {
pub use web_atoms::C1_REPLACEMENTS;
pub use web_atoms::NAMED_ENTITIES;
}
#[macro_use]
pub mod interface;
pub mod serialize;
mod util {
pub mod buffer_queue;
pub mod smallcharset;
}
pub use interface::{Attribute, ExpandedName, QualName, TokenizerResult};
pub use util::smallcharset::SmallCharSet;
pub use util::*;
+77
View File
@@ -0,0 +1,77 @@
// Copyright 2014-2017 The html5ever Project Developers. See the
// COPYRIGHT file at the top-level directory of this distribution.
//
// 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.
//! Traits for serializing elements.
//!
//! The serializer expects the data to be xml-like (with a name,
//! and optional children, attrs, text, comments, doctypes, and [processing instructions]). It uses
//! the visitor pattern, where the serializer and the serializable objects are decoupled and
//! implement their own traits.
//!
//! [processing instructions]: https://en.wikipedia.org/wiki/Processing_Instruction
use crate::QualName;
use std::io;
//§ serializing-html-fragments
/// Used as a parameter to `serialize`, telling it if we want to skip the parent.
#[derive(Clone, PartialEq)]
pub enum TraversalScope {
/// Include the parent node when serializing.
IncludeNode,
/// Only serialize the children of the node, treating any provided qualified name as the
/// parent while serializing.
///
/// This is used in the implementation of [`html5ever::serialize::serialize`]
///
/// [`html5ever::serialize::serialize`]: ../../html5ever/serialize/fn.serialize.html
ChildrenOnly(Option<QualName>),
}
/// Types that can be serialized (according to the xml-like scheme in `Serializer`) implement this
/// trait.
pub trait Serialize {
/// Take the serializer and call its methods to serialize this type. The type will dictate
/// which methods are called and with what parameters.
fn serialize<S>(&self, serializer: &mut S, traversal_scope: TraversalScope) -> io::Result<()>
where
S: Serializer;
}
/// Types that are capable of serializing implement this trait
pub trait Serializer {
/// Serialize the start of an element, for example `<div class="test">`.
fn start_elem<'a, AttrIter>(&mut self, name: QualName, attrs: AttrIter) -> io::Result<()>
where
AttrIter: Iterator<Item = AttrRef<'a>>;
/// Serialize the end of an element, for example `</div>`.
fn end_elem(&mut self, name: QualName) -> io::Result<()>;
/// Serialize a plain text node.
fn write_text(&mut self, text: &str) -> io::Result<()>;
/// Serialize a comment node, for example `<!-- comment -->`.
fn write_comment(&mut self, text: &str) -> io::Result<()>;
/// Serialize a doctype node, for example `<!doctype html>`.
fn write_doctype(&mut self, name: &str) -> io::Result<()>;
/// Serialize a processing instruction node, for example
/// `<?xml-stylesheet type="text/xsl" href="style.xsl"?>`.
fn write_processing_instruction(&mut self, target: &str, data: &str) -> io::Result<()>;
}
/// A type alias for an attribute name and value (e.g. the `class="test"` in `<div class="test">`
/// is represented as `(<QualName of type class>, "test")`.
///
/// This is used in [`Serializer::start_elem`] where the value being serialized must supply an
/// iterator over the attributes for the current element
///
/// [`Serializer::start_elem`]: trait.Serializer.html#tymethod.start_elem
pub type AttrRef<'a> = (&'a QualName, &'a str);
+333
View File
@@ -0,0 +1,333 @@
// Copyright 2014-2017 The html5ever Project Developers. See the
// COPYRIGHT file at the top-level directory of this distribution.
//
// 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.
//! The `BufferQueue` struct and helper types.
//!
//! This type is designed for the efficient parsing of string data, especially where many
//! significant characters are from the ascii range 0-63. This includes, for example, important
//! characters in xml/html parsing.
//!
//! Good and predictable performance is achieved by avoiding allocation where possible (a.k.a. zero
//! copy).
//!
//! [`BufferQueue`]: struct.BufferQueue.html
use std::{
cell::{RefCell, RefMut},
collections::VecDeque,
mem,
};
use tendril::StrTendril;
pub use self::SetResult::{FromSet, NotFromSet};
use crate::util::smallcharset::SmallCharSet;
/// Result from [`pop_except_from`] containing either a character from a [`SmallCharSet`], or a
/// string buffer of characters not from the set.
///
/// [`pop_except_from`]: struct.BufferQueue.html#method.pop_except_from
/// [`SmallCharSet`]: ../struct.SmallCharSet.html
#[derive(PartialEq, Eq, Debug)]
pub enum SetResult {
/// A character from the `SmallCharSet`.
FromSet(char),
/// A string buffer containing no characters from the `SmallCharSet`.
NotFromSet(StrTendril),
}
/// A queue of owned string buffers, which supports incrementally consuming characters.
///
/// Internally it uses [`VecDeque`] and has the same complexity properties.
///
/// [`VecDeque`]: https://doc.rust-lang.org/std/collections/struct.VecDeque.html
#[derive(Clone, Debug)]
pub struct BufferQueue {
/// Buffers to process.
buffers: RefCell<VecDeque<StrTendril>>,
}
impl Default for BufferQueue {
/// Create an empty BufferQueue.
#[inline]
fn default() -> Self {
Self {
buffers: RefCell::new(VecDeque::with_capacity(16)),
}
}
}
impl BufferQueue {
/// Returns whether the queue is empty.
#[inline]
pub fn is_empty(&self) -> bool {
self.buffers.borrow().is_empty()
}
/// Get the buffer at the beginning of the queue.
#[inline]
pub fn pop_front(&self) -> Option<StrTendril> {
self.buffers.borrow_mut().pop_front()
}
/// Add a buffer to the beginning of the queue.
///
/// If the buffer is empty, it will be skipped.
pub fn push_front(&self, buf: StrTendril) {
if buf.len32() == 0 {
return;
}
self.buffers.borrow_mut().push_front(buf);
}
/// Add a buffer to the end of the queue.
///
/// If the buffer is empty, it will be skipped.
pub fn push_back(&self, buf: StrTendril) {
if buf.len32() == 0 {
return;
}
self.buffers.borrow_mut().push_back(buf);
}
/// Look at the next available character without removing it, if the queue is not empty.
pub fn peek(&self) -> Option<char> {
debug_assert!(
!self.buffers.borrow().iter().any(|el| el.len32() == 0),
"invariant \"all buffers in the queue are non-empty\" failed"
);
self.buffers
.borrow()
.front()
.map(|b| b.chars().next().unwrap())
}
/// Pops and returns either a single character from the given set, or
/// a buffer of characters none of which are in the set.
///
/// # Examples
///
/// ```
/// # #[macro_use] extern crate markup5ever;
/// # #[macro_use] extern crate tendril;
/// # fn main() {
/// use markup5ever::buffer_queue::{BufferQueue, SetResult};
///
/// let mut queue = BufferQueue::default();
/// queue.push_back(format_tendril!(r#"<some_tag attr="text">SomeText</some_tag>"#));
/// let set = small_char_set!(b'<' b'>' b' ' b'=' b'"' b'/');
/// let tag = format_tendril!("some_tag");
/// let attr = format_tendril!("attr");
/// let attr_val = format_tendril!("text");
/// assert_eq!(queue.pop_except_from(set), Some(SetResult::FromSet('<')));
/// assert_eq!(queue.pop_except_from(set), Some(SetResult::NotFromSet(tag)));
/// assert_eq!(queue.pop_except_from(set), Some(SetResult::FromSet(' ')));
/// assert_eq!(queue.pop_except_from(set), Some(SetResult::NotFromSet(attr)));
/// assert_eq!(queue.pop_except_from(set), Some(SetResult::FromSet('=')));
/// assert_eq!(queue.pop_except_from(set), Some(SetResult::FromSet('"')));
/// assert_eq!(queue.pop_except_from(set), Some(SetResult::NotFromSet(attr_val)));
/// // ...
/// # }
/// ```
pub fn pop_except_from(&self, set: SmallCharSet) -> Option<SetResult> {
let (result, now_empty) = match self.buffers.borrow_mut().front_mut() {
None => (None, false),
Some(buf) => {
let n = set.nonmember_prefix_len(buf);
if n > 0 {
let out;
unsafe {
out = buf.unsafe_subtendril(0, n);
buf.unsafe_pop_front(n);
}
(Some(NotFromSet(out)), buf.is_empty())
} else {
let c = buf.pop_front_char().expect("empty buffer in queue");
(Some(FromSet(c)), buf.is_empty())
}
},
};
// Unborrow self for this part.
if now_empty {
self.buffers.borrow_mut().pop_front();
}
result
}
/// Consume bytes matching the pattern, using a custom comparison function `eq`.
///
/// Returns `Some(true)` if there is a match, `Some(false)` if there is no match, or `None` if
/// it wasn't possible to know (more data is needed).
///
/// The custom comparison function is used elsewhere to compare ascii-case-insensitively.
///
/// # Examples
///
/// ```
/// # extern crate markup5ever;
/// # #[macro_use] extern crate tendril;
/// # fn main() {
/// use markup5ever::buffer_queue::BufferQueue;
///
/// let mut queue = BufferQueue::default();
/// queue.push_back(format_tendril!("testtext"));
/// let test_str = "test";
/// assert_eq!(queue.eat("test", |&a, &b| a == b), Some(true));
/// assert_eq!(queue.eat("text", |&a, &b| a == b), Some(true));
/// assert!(queue.is_empty());
/// # }
/// ```
pub fn eat<F: Fn(&u8, &u8) -> bool>(&self, pat: &str, eq: F) -> Option<bool> {
let mut buffers_exhausted = 0;
let mut consumed_from_last = 0;
self.buffers.borrow().front()?;
for pattern_byte in pat.bytes() {
if buffers_exhausted >= self.buffers.borrow().len() {
return None;
}
let buf = &self.buffers.borrow()[buffers_exhausted];
if !eq(&buf.as_bytes()[consumed_from_last], &pattern_byte) {
return Some(false);
}
consumed_from_last += 1;
if consumed_from_last >= buf.len() {
buffers_exhausted += 1;
consumed_from_last = 0;
}
}
// We have a match. Commit changes to the BufferQueue.
for _ in 0..buffers_exhausted {
self.buffers.borrow_mut().pop_front();
}
match self.buffers.borrow_mut().front_mut() {
None => assert_eq!(consumed_from_last, 0),
Some(ref mut buf) => buf.pop_front(consumed_from_last as u32),
}
Some(true)
}
/// Get the next character if one is available, removing it from the queue.
///
/// This function manages the buffers, removing them as they become empty.
pub fn next(&self) -> Option<char> {
let (result, now_empty) = match self.buffers.borrow_mut().front_mut() {
None => (None, false),
Some(buf) => {
let c = buf.pop_front_char().expect("empty buffer in queue");
(Some(c), buf.is_empty())
},
};
if now_empty {
self.buffers.borrow_mut().pop_front();
}
result
}
pub fn replace_with(&self, other: BufferQueue) {
let _ = mem::replace(&mut *self.buffers.borrow_mut(), other.buffers.take());
}
pub fn swap_with(&self, other: &BufferQueue) {
mem::swap(
&mut *self.buffers.borrow_mut(),
&mut *other.buffers.borrow_mut(),
);
}
/// Return a mutable reference to the first tendril in the queue.
pub fn peek_front_chunk_mut(&self) -> Option<RefMut<'_, StrTendril>> {
let buffers = self.buffers.borrow_mut();
if buffers.is_empty() {
return None;
}
let front_buffer = RefMut::map(buffers, |buffers| {
buffers.front_mut().expect("there is at least one buffer")
});
Some(front_buffer)
}
}
#[cfg(test)]
#[allow(non_snake_case)]
mod test {
use tendril::SliceExt;
use super::BufferQueue;
use super::SetResult::{FromSet, NotFromSet};
#[test]
fn smoke_test() {
let bq = BufferQueue::default();
assert_eq!(bq.peek(), None);
assert_eq!(bq.next(), None);
bq.push_back("abc".to_tendril());
assert_eq!(bq.peek(), Some('a'));
assert_eq!(bq.next(), Some('a'));
assert_eq!(bq.peek(), Some('b'));
assert_eq!(bq.peek(), Some('b'));
assert_eq!(bq.next(), Some('b'));
assert_eq!(bq.peek(), Some('c'));
assert_eq!(bq.next(), Some('c'));
assert_eq!(bq.peek(), None);
assert_eq!(bq.next(), None);
}
#[test]
fn can_unconsume() {
let bq = BufferQueue::default();
bq.push_back("abc".to_tendril());
assert_eq!(bq.next(), Some('a'));
bq.push_front("xy".to_tendril());
assert_eq!(bq.next(), Some('x'));
assert_eq!(bq.next(), Some('y'));
assert_eq!(bq.next(), Some('b'));
assert_eq!(bq.next(), Some('c'));
assert_eq!(bq.next(), None);
}
#[test]
fn can_pop_except_set() {
let bq = BufferQueue::default();
bq.push_back("abc&def".to_tendril());
let pop = || bq.pop_except_from(small_char_set!('&'));
assert_eq!(pop(), Some(NotFromSet("abc".to_tendril())));
assert_eq!(pop(), Some(FromSet('&')));
assert_eq!(pop(), Some(NotFromSet("def".to_tendril())));
assert_eq!(pop(), None);
}
#[test]
fn can_eat() {
// This is not very comprehensive. We rely on the tokenizer
// integration tests for more thorough testing with many
// different input buffer splits.
let bq = BufferQueue::default();
bq.push_back("a".to_tendril());
bq.push_back("bc".to_tendril());
assert_eq!(bq.eat("abcd", u8::eq_ignore_ascii_case), None);
assert_eq!(bq.eat("ax", u8::eq_ignore_ascii_case), Some(false));
assert_eq!(bq.eat("ab", u8::eq_ignore_ascii_case), Some(true));
assert_eq!(bq.next(), Some('c'));
assert_eq!(bq.next(), None);
}
}
+88
View File
@@ -0,0 +1,88 @@
// Copyright 2014-2017 The html5ever Project Developers. See the
// COPYRIGHT file at the top-level directory of this distribution.
//
// 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.
//! This module contains a single struct [`SmallCharSet`]. See its documentation for details.
//!
//! [`SmallCharSet`]: struct.SmallCharSet.html
/// Represents a set of "small characters", those with Unicode scalar
/// values less than 64.
///
/// This is stored as a bitmap, with 1 bit for each value.
#[derive(Debug, Eq, PartialEq, Clone, Copy, Hash)]
pub struct SmallCharSet {
pub bits: u64,
}
impl SmallCharSet {
/// Checks whether a character (u8 value below 64) is stored in the SmallCharSet.
///
/// # Examples
///
/// ```ignore
/// # use markup5ever::SmallCharSet;
/// let set = SmallCharSet {
/// bits: 0b00000000_01000000_00000100_00000000_00000000_00000000_00010000_00000000
/// };
/// assert!(set.contains(64));
/// assert!(set.contains(b'6')); // `b'6'` is the same as 64u8
/// ```
#[inline]
fn contains(&self, n: u8) -> bool {
0 != (self.bits & (1 << (n as usize)))
}
/// Count the number of bytes of characters at the beginning of `buf` which are not in the set.
///
/// This functionality is used in [`BufferQueue::pop_except_from`].
///
/// # Examples
///
/// ```
/// # #[macro_use] extern crate markup5ever;
/// # fn main() {
/// let set = small_char_set!(48 49 50); // '0' '1' '2'
/// // `test` is 4 chars, 😁 is 4 chars, then we meet a character in the set
/// let test_str = "test😁01232afd";
/// assert_eq!(set.nonmember_prefix_len(test_str), 8);
/// # }
/// ```
///
/// [`BufferQueue::pop_except_from`]: buffer_queue/struct.BufferQueue.html#method.pop_except_from
pub fn nonmember_prefix_len(&self, buf: &str) -> u32 {
let mut n = 0;
for b in buf.bytes() {
if b >= 64 || !self.contains(b) {
n += 1;
} else {
break;
}
}
n
}
}
#[cfg(test)]
mod test {
#[test]
fn nonmember_prefix() {
for &c in ['&', '\0'].iter() {
for x in 0..48u32 {
for y in 0..48u32 {
let mut s = "x".repeat(x as usize);
s.push(c);
s.push_str(&"x".repeat(y as usize));
let set = small_char_set!('&' '\0');
assert_eq!(x, set.nonmember_prefix_len(&s));
}
}
}
}
}