mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-23 21:01:08 +00:00
Bug 1672317 - [l10nfilesource] part2: Vendor in l10nregistry-rs and fluent-fallback. r=dminor
Depends on D103184 Differential Revision: https://phabricator.services.mozilla.com/D102102
This commit is contained in:
parent
bcff9a7e2d
commit
e0ee86ae33
@ -2,6 +2,11 @@
|
||||
# It was generated by `mach vendor rust`.
|
||||
# Please do not edit.
|
||||
|
||||
[source."https://github.com/zbraniecki/l10nregistry-rs"]
|
||||
git = "https://github.com/zbraniecki/l10nregistry-rs"
|
||||
replace-with = "vendored-sources"
|
||||
rev = "92d8fbfbbbdffa2047ce01a935a389eb11031f69"
|
||||
|
||||
[source."https://github.com/shravanrn/nix/"]
|
||||
git = "https://github.com/shravanrn/nix/"
|
||||
replace-with = "vendored-sources"
|
||||
|
111
Cargo.lock
generated
111
Cargo.lock
generated
@ -7,10 +7,6 @@ name = "Inflector"
|
||||
version = "0.11.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"regex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "adler"
|
||||
@ -95,6 +91,17 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-trait"
|
||||
version = "0.1.42"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8d3a45e77e34375a7923b1e8febb049bb011f064714a8e17a1a616fef01da13d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "atomic"
|
||||
version = "0.4.6"
|
||||
@ -583,6 +590,12 @@ dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "chunky-vec"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bb7bdea464ae038f09197b82430b921c53619fc8d2bcaf7b151013b3ca008017"
|
||||
|
||||
[[package]]
|
||||
name = "clang-sys"
|
||||
version = "1.2.0"
|
||||
@ -1427,9 +1440,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "fluent-bundle"
|
||||
version = "0.15.0"
|
||||
version = "0.15.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b589dfaa7e69ddf497be48cd0d184d7ff6e2cbb8186d1bb01c26d5cf5449a17"
|
||||
checksum = "8acf044eeb4872d9dbf2667541fbf461f5965c57e343878ad0fb24b5793fa007"
|
||||
dependencies = [
|
||||
"fluent-langneg",
|
||||
"fluent-syntax",
|
||||
@ -1441,13 +1454,30 @@ dependencies = [
|
||||
"unic-langid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fluent-fallback"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "768feaf8a77beababd5cf3bb1154597b7161eb5a555013a3ac594fe3ed8b18ee"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"chunky-vec",
|
||||
"fluent-bundle",
|
||||
"futures 0.3.15",
|
||||
"once_cell",
|
||||
"unic-langid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fluent-ffi"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"fluent",
|
||||
"fluent-fallback",
|
||||
"fluent-pseudo",
|
||||
"futures 0.3.15",
|
||||
"intl-memoizer",
|
||||
"l10nregistry",
|
||||
"nsstring",
|
||||
"thin-vec",
|
||||
"unic-langid",
|
||||
@ -1595,6 +1625,7 @@ checksum = "0e7e43a803dae2fa37c1f6a8fe121e1f7bf9548b4dfc0522a42f34145dadfc27"
|
||||
dependencies = [
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"futures-executor",
|
||||
"futures-io",
|
||||
"futures-sink",
|
||||
"futures-task",
|
||||
@ -1627,12 +1658,36 @@ dependencies = [
|
||||
"num_cpus",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-executor"
|
||||
version = "0.3.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "badaa6a909fac9e7236d0620a2f57f7664640c56575b71a7552fbd68deafab79"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-task",
|
||||
"futures-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-io"
|
||||
version = "0.3.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "acc499defb3b348f8d8f3f66415835a9131856ff7714bf10dadfc4ec4bdb29a1"
|
||||
|
||||
[[package]]
|
||||
name = "futures-macro"
|
||||
version = "0.3.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a4c40298486cdf52cc00cd6d6987892ba502c7656a16a4192a9992b1ccedd121"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"proc-macro-hack",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-sink"
|
||||
version = "0.3.15"
|
||||
@ -1652,11 +1707,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "feb5c238d27e2bf94ffdfd27b2c29e3df4a68c4193bb6427384259e2bf191967"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"futures-io",
|
||||
"futures-macro",
|
||||
"futures-sink",
|
||||
"futures-task",
|
||||
"memchr",
|
||||
"pin-project-lite 0.2.6",
|
||||
"pin-utils",
|
||||
"proc-macro-hack",
|
||||
"proc-macro-nested",
|
||||
"slab",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1928,6 +1990,7 @@ dependencies = [
|
||||
"cubeb-sys",
|
||||
"encoding_glue",
|
||||
"fluent",
|
||||
"fluent-fallback",
|
||||
"fluent-ffi",
|
||||
"fluent-langneg",
|
||||
"fluent-langneg-ffi",
|
||||
@ -1939,6 +2002,7 @@ dependencies = [
|
||||
"http_sfv",
|
||||
"jsrust_shared",
|
||||
"kvstore",
|
||||
"l10nregistry",
|
||||
"l10nregistry-ffi",
|
||||
"lmdb-rkv-sys",
|
||||
"log",
|
||||
@ -2573,6 +2637,21 @@ dependencies = [
|
||||
"xpcom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "l10nregistry"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/zbraniecki/l10nregistry-rs?rev=92d8fbfbbbdffa2047ce01a935a389eb11031f69#92d8fbfbbbdffa2047ce01a935a389eb11031f69"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"fluent-bundle",
|
||||
"fluent-fallback",
|
||||
"futures 0.3.15",
|
||||
"pin-project-lite 0.2.6",
|
||||
"replace_with",
|
||||
"rustc-hash",
|
||||
"unic-langid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "l10nregistry-ffi"
|
||||
version = "0.1.0"
|
||||
@ -3503,9 +3582,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ouroboros"
|
||||
version = "0.8.0"
|
||||
version = "0.9.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "069fb33e127cabdc8ad6a287eed9719b85c612d36199777f6dc41ad91f7be41a"
|
||||
checksum = "c8234affc3c31a8b744cc236fd3dc7443f57c6370cbb7d61f41f9c7dcc6c2530"
|
||||
dependencies = [
|
||||
"ouroboros_macro",
|
||||
"stable_deref_trait",
|
||||
@ -3513,9 +3592,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ouroboros_macro"
|
||||
version = "0.8.0"
|
||||
version = "0.9.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ad938cc920f299d6dce91e43d3ce316e785f4aa4bc4243555634dc2967098fc6"
|
||||
checksum = "3633332cd8c0b6a865e2e0e705fad9cde25fe458cd0c693629b58a7b15e4d852"
|
||||
dependencies = [
|
||||
"Inflector",
|
||||
"proc-macro-error",
|
||||
@ -3824,6 +3903,12 @@ version = "0.5.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-nested"
|
||||
version = "0.1.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bc881b2c22681370c6a780e47af9840ef841837bc98118431d4e1868bd0c1086"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.27"
|
||||
@ -4130,6 +4215,12 @@ dependencies = [
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "replace_with"
|
||||
version = "0.1.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e3a8614ee435691de62bcffcf4a66d91b3594bf1428a5722e79103249a095690"
|
||||
|
||||
[[package]]
|
||||
name = "ringbuf"
|
||||
version = "0.2.5"
|
||||
|
@ -5,9 +5,12 @@ authors = ["Zibi Braniecki <zibi@braniecki.net>"]
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
fluent = { version = "0.15.0", features = ["fluent-pseudo"] }
|
||||
fluent = { version = "0.15", features = ["fluent-pseudo"] }
|
||||
fluent-pseudo = "0.2.3"
|
||||
intl-memoizer = "0.5"
|
||||
unic-langid = "0.9"
|
||||
nsstring = { path = "../../../../xpcom/rust/nsstring" }
|
||||
thin-vec = { version = "0.2.1", features = ["gecko-ffi"] }
|
||||
l10nregistry = { git = "https://github.com/zbraniecki/l10nregistry-rs", rev = "92d8fbfbbbdffa2047ce01a935a389eb11031f69" }
|
||||
fluent-fallback = "0.5"
|
||||
futures = "0.3"
|
||||
|
1
third_party/rust/async-trait/.cargo-checksum.json
vendored
Normal file
1
third_party/rust/async-trait/.cargo-checksum.json
vendored
Normal file
@ -0,0 +1 @@
|
||||
{"files":{"Cargo.toml":"4c50bdb04cb7b2c2eccf4f6f43a27bd833e3481eaaeaf44ce31e0e4842749f98","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3","README.md":"7cdf53d90efa27ae464b138755eb88a7f912ff95cad3ea3a1970ee4891af8aa2","src/args.rs":"6eed5497db91752b3aae597943c39e769f60406b37055304e69e4699f1f87b15","src/expand.rs":"da89e09db97138bc7488fde2aea105329635a4467267d7cfe5d80d4550948e2a","src/lib.rs":"fee50ccfc315bb468f4207cebef12ec7285cef78c5c09c9f09d5a32b1876f4af","src/lifetime.rs":"e51bd32dba9caf32ea241d2f4fbd83c29b5c71d0308d9c18268b9e4594ec9c76","src/parse.rs":"cd9032fe2c6dcf41050b3a59b9fb98eb9700a29bbe2fa011ee2854014c1666b7","src/receiver.rs":"0a166f7423f17e0743b64a37c448dcbace89f1df71d204f94b0ed942c7d9e053","src/respan.rs":"eef5b4467f58e1df4650a2331495c5e28eff8d54a3250506fc2f6c2da3366ada","tests/compiletest.rs":"0a52a44786aea1c299c695bf948b2ed2081e4cc344e5c2cadceab4eb03d0010d","tests/executor/mod.rs":"620975b33cc2a494efb6bce230cf285a462b85e5fe12d71561b5bacd9d507b28","tests/test.rs":"78dcb0b36fa1351dd97f1d415889e1178799667ffef70daa115695ee99758e71","tests/ui/bare-trait-object.rs":"4546e8bd6682de11920fa4c768295fed61954484ef0550dfadbc5677b77f29a5","tests/ui/bare-trait-object.stderr":"b63287594e6e5183e9b5b0e701fc4dd3a9b4fb0ea93eb910be56166e5fa4cdbd","tests/ui/delimiter-span.rs":"5e11150b1448f4c5b88a08aa579a663606ccaaed7f302e9465b42fc3489f0f3e","tests/ui/delimiter-span.stderr":"a1d7ead68b8d01e6e51c0943ccd683fb5fb8c3eae2d56feb22b0ef777949b87d","tests/ui/missing-body.rs":"d06c0da8c6044e7c790b924136f167e2edc0d0d3fa01f23521f3f08ca605929b","tests/ui/missing-body.stderr":"636a03cc42933b59d73032ce6cea862e33c16efb9c7fe7f27749247998bc9f23","tests/ui/must-use.rs":"75090c7df984df0996464337f60371d198bd0caf3f9f44b10d1e131f15fd4fca","tests/ui/must-use.stderr":"e6cb190e02f0226df6444065aaca3051f7db8ae599bba18a685155c52bb799b6","tests/ui/self-span.rs":"67ddde05907d7014bfb3f2c63d427b1d72d6c4369a9108a4335dac6bee5832b2","tests/ui/self-span.stderr":"8f473640c732ce66656f7da611c8173c3f8719ad90fa42d7043bb037f43964ea","tests/ui/send-not-implemented.rs":"5f1dd26f4eb34c653048e9658fbf6b41e2930d6222869b397ca4d8d7113a2809","tests/ui/send-not-implemented.stderr":"f7ea7ed8fe196c4bfc27c028ea3e1c0253e05e45e082454dbfba7ef0655d3fa6","tests/ui/unsupported-self.rs":"f7855bc39dab1fd2f533fb2e873a27c3757dcb9fb57001e4b19f58d3dda36d01","tests/ui/unsupported-self.stderr":"2b30517b790c666ea85442afc1c701ddc235d091372e175009aa084bd57b7070"},"package":"8d3a45e77e34375a7923b1e8febb049bb011f064714a8e17a1a616fef01da13d"}
|
51
third_party/rust/async-trait/Cargo.toml
vendored
Normal file
51
third_party/rust/async-trait/Cargo.toml
vendored
Normal file
@ -0,0 +1,51 @@
|
||||
# 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 believe there's an error in this file please file an
|
||||
# issue against the rust-lang/cargo repository. If you're
|
||||
# editing this file be aware that the upstream Cargo.toml
|
||||
# will likely look very different (and much more reasonable)
|
||||
|
||||
[package]
|
||||
edition = "2018"
|
||||
name = "async-trait"
|
||||
version = "0.1.42"
|
||||
authors = ["David Tolnay <dtolnay@gmail.com>"]
|
||||
description = "Type erasure for async trait methods"
|
||||
documentation = "https://docs.rs/async-trait"
|
||||
readme = "README.md"
|
||||
license = "MIT OR Apache-2.0"
|
||||
repository = "https://github.com/dtolnay/async-trait"
|
||||
[package.metadata.docs.rs]
|
||||
targets = ["x86_64-unknown-linux-gnu"]
|
||||
|
||||
[lib]
|
||||
proc-macro = true
|
||||
[dependencies.proc-macro2]
|
||||
version = "1.0"
|
||||
|
||||
[dependencies.quote]
|
||||
version = "1.0"
|
||||
|
||||
[dependencies.syn]
|
||||
version = "1.0"
|
||||
features = ["full", "visit-mut"]
|
||||
[dev-dependencies.rustversion]
|
||||
version = "1.0"
|
||||
|
||||
[dev-dependencies.tracing]
|
||||
version = "0.1.14"
|
||||
|
||||
[dev-dependencies.tracing-attributes]
|
||||
version = "0.1.8"
|
||||
|
||||
[dev-dependencies.tracing-futures]
|
||||
version = "0.2"
|
||||
|
||||
[dev-dependencies.trybuild]
|
||||
version = "1.0.19"
|
||||
features = ["diff"]
|
201
third_party/rust/async-trait/LICENSE-APACHE
vendored
Normal file
201
third_party/rust/async-trait/LICENSE-APACHE
vendored
Normal 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.
|
23
third_party/rust/async-trait/LICENSE-MIT
vendored
Normal file
23
third_party/rust/async-trait/LICENSE-MIT
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
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.
|
262
third_party/rust/async-trait/README.md
vendored
Normal file
262
third_party/rust/async-trait/README.md
vendored
Normal file
@ -0,0 +1,262 @@
|
||||
Async trait methods
|
||||
===================
|
||||
|
||||
[<img alt="github" src="https://img.shields.io/badge/github-dtolnay/async--trait-8da0cb?style=for-the-badge&labelColor=555555&logo=github" height="20">](https://github.com/dtolnay/async-trait)
|
||||
[<img alt="crates.io" src="https://img.shields.io/crates/v/async-trait.svg?style=for-the-badge&color=fc8d62&logo=rust" height="20">](https://crates.io/crates/async-trait)
|
||||
[<img alt="docs.rs" src="https://img.shields.io/badge/docs.rs-async--trait-66c2a5?style=for-the-badge&labelColor=555555&logoColor=white&logo=data:image/svg+xml;base64,PHN2ZyByb2xlPSJpbWciIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgdmlld0JveD0iMCAwIDUxMiA1MTIiPjxwYXRoIGZpbGw9IiNmNWY1ZjUiIGQ9Ik00ODguNiAyNTAuMkwzOTIgMjE0VjEwNS41YzAtMTUtOS4zLTI4LjQtMjMuNC0zMy43bC0xMDAtMzcuNWMtOC4xLTMuMS0xNy4xLTMuMS0yNS4zIDBsLTEwMCAzNy41Yy0xNC4xIDUuMy0yMy40IDE4LjctMjMuNCAzMy43VjIxNGwtOTYuNiAzNi4yQzkuMyAyNTUuNSAwIDI2OC45IDAgMjgzLjlWMzk0YzAgMTMuNiA3LjcgMjYuMSAxOS45IDMyLjJsMTAwIDUwYzEwLjEgNS4xIDIyLjEgNS4xIDMyLjIgMGwxMDMuOS01MiAxMDMuOSA1MmMxMC4xIDUuMSAyMi4xIDUuMSAzMi4yIDBsMTAwLTUwYzEyLjItNi4xIDE5LjktMTguNiAxOS45LTMyLjJWMjgzLjljMC0xNS05LjMtMjguNC0yMy40LTMzLjd6TTM1OCAyMTQuOGwtODUgMzEuOXYtNjguMmw4NS0zN3Y3My4zek0xNTQgMTA0LjFsMTAyLTM4LjIgMTAyIDM4LjJ2LjZsLTEwMiA0MS40LTEwMi00MS40di0uNnptODQgMjkxLjFsLTg1IDQyLjV2LTc5LjFsODUtMzguOHY3NS40em0wLTExMmwtMTAyIDQxLjQtMTAyLTQxLjR2LS42bDEwMi0zOC4yIDEwMiAzOC4ydi42em0yNDAgMTEybC04NSA0Mi41di03OS4xbDg1LTM4Ljh2NzUuNHptMC0xMTJsLTEwMiA0MS40LTEwMi00MS40di0uNmwxMDItMzguMiAxMDIgMzguMnYuNnoiPjwvcGF0aD48L3N2Zz4K" height="20">](https://docs.rs/async-trait)
|
||||
[<img alt="build status" src="https://img.shields.io/github/workflow/status/dtolnay/async-trait/CI/master?style=for-the-badge" height="20">](https://github.com/dtolnay/async-trait/actions?query=branch%3Amaster)
|
||||
|
||||
The initial round of stabilizations for the async/await language feature in Rust
|
||||
1.39 did not include support for async fn in traits. Trying to include an async
|
||||
fn in a trait produces the following error:
|
||||
|
||||
```rust
|
||||
trait MyTrait {
|
||||
async fn f() {}
|
||||
}
|
||||
```
|
||||
|
||||
```console
|
||||
error[E0706]: trait fns cannot be declared `async`
|
||||
--> src/main.rs:4:5
|
||||
|
|
||||
4 | async fn f() {}
|
||||
| ^^^^^^^^^^^^^^^
|
||||
```
|
||||
|
||||
This crate provides an attribute macro to make async fn in traits work.
|
||||
|
||||
Please refer to [*why async fn in traits are hard*][hard] for a deeper analysis
|
||||
of how this implementation differs from what the compiler and language hope to
|
||||
deliver in the future.
|
||||
|
||||
[hard]: https://smallcultfollowing.com/babysteps/blog/2019/10/26/async-fn-in-traits-are-hard/
|
||||
|
||||
<br>
|
||||
|
||||
## Example
|
||||
|
||||
This example implements the core of a highly effective advertising platform
|
||||
using async fn in a trait.
|
||||
|
||||
The only thing to notice here is that we write an `#[async_trait]` macro on top
|
||||
of traits and trait impls that contain async fn, and then they work.
|
||||
|
||||
```rust
|
||||
use async_trait::async_trait;
|
||||
|
||||
#[async_trait]
|
||||
trait Advertisement {
|
||||
async fn run(&self);
|
||||
}
|
||||
|
||||
struct Modal;
|
||||
|
||||
#[async_trait]
|
||||
impl Advertisement for Modal {
|
||||
async fn run(&self) {
|
||||
self.render_fullscreen().await;
|
||||
for _ in 0..4u16 {
|
||||
remind_user_to_join_mailing_list().await;
|
||||
}
|
||||
self.hide_for_now().await;
|
||||
}
|
||||
}
|
||||
|
||||
struct AutoplayingVideo {
|
||||
media_url: String,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl Advertisement for AutoplayingVideo {
|
||||
async fn run(&self) {
|
||||
let stream = connect(&self.media_url).await;
|
||||
stream.play().await;
|
||||
|
||||
// Video probably persuaded user to join our mailing list!
|
||||
Modal.run().await;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
<br>
|
||||
|
||||
## Supported features
|
||||
|
||||
It is the intention that all features of Rust traits should work nicely with
|
||||
\#\[async_trait\], but the edge cases are numerous. *Please file an issue if you
|
||||
see unexpected borrow checker errors, type errors, or warnings.* There is no use
|
||||
of `unsafe` in the expanded code, so rest assured that if your code compiles it
|
||||
can't be that badly broken.
|
||||
|
||||
- 👍 Self by value, by reference, by mut reference, or no self;
|
||||
- 👍 Any number of arguments, any return value;
|
||||
- 👍 Generic type parameters and lifetime parameters;
|
||||
- 👍 Associated types;
|
||||
- 👍 Having async and non-async functions in the same trait;
|
||||
- 👍 Default implementations provided by the trait;
|
||||
- 👍 Elided lifetimes;
|
||||
- 👍 Dyn-capable traits.
|
||||
|
||||
<br>
|
||||
|
||||
## Explanation
|
||||
|
||||
Async fns get transformed into methods that return `Pin<Box<dyn Future + Send +
|
||||
'async_trait>>` and delegate to a private async freestanding function.
|
||||
|
||||
For example the `impl Advertisement for AutoplayingVideo` above would be
|
||||
expanded as:
|
||||
|
||||
```rust
|
||||
impl Advertisement for AutoplayingVideo {
|
||||
fn run<'async_trait>(
|
||||
&'async_trait self,
|
||||
) -> Pin<Box<dyn std::future::Future<Output = ()> + Send + 'async_trait>>
|
||||
where
|
||||
Self: Sync + 'async_trait,
|
||||
{
|
||||
async fn run(_self: &AutoplayingVideo) {
|
||||
/* the original method body */
|
||||
}
|
||||
|
||||
Box::pin(run(self))
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
<br>
|
||||
|
||||
## Non-threadsafe futures
|
||||
|
||||
Not all async traits need futures that are `dyn Future + Send`. To avoid having
|
||||
Send and Sync bounds placed on the async trait methods, invoke the async trait
|
||||
macro as `#[async_trait(?Send)]` on both the trait and the impl blocks.
|
||||
|
||||
<br>
|
||||
|
||||
## Elided lifetimes
|
||||
|
||||
Be aware that async fn syntax does not allow lifetime elision outside of `&` and
|
||||
`&mut` references. (This is true even when not using #\[async_trait\].)
|
||||
Lifetimes must be named or marked by the placeholder `'_`.
|
||||
|
||||
Fortunately the compiler is able to diagnose missing lifetimes with a good error
|
||||
message.
|
||||
|
||||
```rust
|
||||
type Elided<'a> = &'a usize;
|
||||
|
||||
#[async_trait]
|
||||
trait Test {
|
||||
async fn test(not_okay: Elided, okay: &usize) {}
|
||||
}
|
||||
```
|
||||
|
||||
```console
|
||||
error[E0726]: implicit elided lifetime not allowed here
|
||||
--> src/main.rs:9:29
|
||||
|
|
||||
9 | async fn test(not_okay: Elided, okay: &usize) {}
|
||||
| ^^^^^^- help: indicate the anonymous lifetime: `<'_>`
|
||||
```
|
||||
|
||||
The fix is to name the lifetime or use `'_`.
|
||||
|
||||
```rust
|
||||
#[async_trait]
|
||||
trait Test {
|
||||
// either
|
||||
async fn test<'e>(elided: Elided<'e>) {}
|
||||
// or
|
||||
async fn test(elided: Elided<'_>) {}
|
||||
}
|
||||
```
|
||||
|
||||
<br>
|
||||
|
||||
## Dyn traits
|
||||
|
||||
Traits with async methods can be used as trait objects as long as they meet the
|
||||
usual requirements for dyn -- no methods with type parameters, no self by value,
|
||||
no associated types, etc.
|
||||
|
||||
```rust
|
||||
#[async_trait]
|
||||
pub trait ObjectSafe {
|
||||
async fn f(&self);
|
||||
async fn g(&mut self);
|
||||
}
|
||||
|
||||
impl ObjectSafe for MyType {...}
|
||||
|
||||
let value: MyType = ...;
|
||||
let object = &value as &dyn ObjectSafe; // make trait object
|
||||
```
|
||||
|
||||
The one wrinkle is in traits that provide default implementations of async
|
||||
methods. In order for the default implementation to produce a future that is
|
||||
Send, the async\_trait macro must emit a bound of `Self: Sync` on trait methods
|
||||
that take `&self` and a bound `Self: Send` on trait methods that take `&mut
|
||||
self`. An example of the former is visible in the expanded code in the
|
||||
explanation section above.
|
||||
|
||||
If you make a trait with async methods that have default implementations,
|
||||
everything will work except that the trait cannot be used as a trait object.
|
||||
Creating a value of type `&dyn Trait` will produce an error that looks like
|
||||
this:
|
||||
|
||||
```console
|
||||
error: the trait `Test` cannot be made into an object
|
||||
--> src/main.rs:8:5
|
||||
|
|
||||
8 | async fn cannot_dyn(&self) {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
```
|
||||
|
||||
For traits that need to be object safe and need to have default implementations
|
||||
for some async methods, there are two resolutions. Either you can add Send
|
||||
and/or Sync as supertraits (Send if there are `&mut self` methods with default
|
||||
implementations, Sync if there are `&self` methods with default implementions)
|
||||
to constrain all implementors of the trait such that the default implementations
|
||||
are applicable to them:
|
||||
|
||||
```rust
|
||||
#[async_trait]
|
||||
pub trait ObjectSafe: Sync { // added supertrait
|
||||
async fn can_dyn(&self) {}
|
||||
}
|
||||
|
||||
let object = &value as &dyn ObjectSafe;
|
||||
```
|
||||
|
||||
or you can strike the problematic methods from your trait object by bounding
|
||||
them with `Self: Sized`:
|
||||
|
||||
```rust
|
||||
#[async_trait]
|
||||
pub trait ObjectSafe {
|
||||
async fn cannot_dyn(&self) where Self: Sized {}
|
||||
|
||||
// presumably other methods
|
||||
}
|
||||
|
||||
let object = &value as &dyn ObjectSafe;
|
||||
```
|
||||
|
||||
<br>
|
||||
|
||||
#### License
|
||||
|
||||
<sup>
|
||||
Licensed under either of <a href="LICENSE-APACHE">Apache License, Version
|
||||
2.0</a> or <a href="LICENSE-MIT">MIT license</a> at your option.
|
||||
</sup>
|
||||
|
||||
<br>
|
||||
|
||||
<sub>
|
||||
Unless you explicitly state otherwise, any contribution intentionally submitted
|
||||
for inclusion in this crate by you, as defined in the Apache-2.0 license, shall
|
||||
be dual licensed as above, without any additional terms or conditions.
|
||||
</sub>
|
36
third_party/rust/async-trait/src/args.rs
vendored
Normal file
36
third_party/rust/async-trait/src/args.rs
vendored
Normal file
@ -0,0 +1,36 @@
|
||||
use proc_macro2::Span;
|
||||
use syn::parse::{Error, Parse, ParseStream, Result};
|
||||
use syn::Token;
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct Args {
|
||||
pub local: bool,
|
||||
}
|
||||
|
||||
mod kw {
|
||||
syn::custom_keyword!(Send);
|
||||
}
|
||||
|
||||
impl Parse for Args {
|
||||
fn parse(input: ParseStream) -> Result<Self> {
|
||||
match try_parse(input) {
|
||||
Ok(args) if input.is_empty() => Ok(args),
|
||||
_ => Err(error()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn try_parse(input: ParseStream) -> Result<Args> {
|
||||
if input.peek(Token![?]) {
|
||||
input.parse::<Token![?]>()?;
|
||||
input.parse::<kw::Send>()?;
|
||||
Ok(Args { local: true })
|
||||
} else {
|
||||
Ok(Args { local: false })
|
||||
}
|
||||
}
|
||||
|
||||
fn error() -> Error {
|
||||
let msg = "expected #[async_trait] or #[async_trait(?Send)]";
|
||||
Error::new(Span::call_site(), msg)
|
||||
}
|
502
third_party/rust/async-trait/src/expand.rs
vendored
Normal file
502
third_party/rust/async-trait/src/expand.rs
vendored
Normal file
@ -0,0 +1,502 @@
|
||||
use crate::lifetime::{has_async_lifetime, CollectLifetimes};
|
||||
use crate::parse::Item;
|
||||
use crate::receiver::{
|
||||
has_self_in_block, has_self_in_sig, has_self_in_where_predicate, ReplaceReceiver,
|
||||
};
|
||||
use proc_macro2::{Span, TokenStream};
|
||||
use quote::{format_ident, quote, quote_spanned, ToTokens};
|
||||
use std::mem;
|
||||
use syn::punctuated::Punctuated;
|
||||
use syn::visit_mut::VisitMut;
|
||||
use syn::{
|
||||
parse_quote, Block, FnArg, GenericParam, Generics, Ident, ImplItem, Lifetime, Pat, PatIdent,
|
||||
Path, Receiver, ReturnType, Signature, Stmt, Token, TraitItem, Type, TypeParam, TypeParamBound,
|
||||
WhereClause,
|
||||
};
|
||||
|
||||
impl ToTokens for Item {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||
match self {
|
||||
Item::Trait(item) => item.to_tokens(tokens),
|
||||
Item::Impl(item) => item.to_tokens(tokens),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
enum Context<'a> {
|
||||
Trait {
|
||||
name: &'a Ident,
|
||||
generics: &'a Generics,
|
||||
supertraits: &'a Supertraits,
|
||||
},
|
||||
Impl {
|
||||
impl_generics: &'a Generics,
|
||||
receiver: &'a Type,
|
||||
as_trait: &'a Path,
|
||||
},
|
||||
}
|
||||
|
||||
impl Context<'_> {
|
||||
fn lifetimes<'a>(&'a self, used: &'a [Lifetime]) -> impl Iterator<Item = &'a GenericParam> {
|
||||
let generics = match self {
|
||||
Context::Trait { generics, .. } => generics,
|
||||
Context::Impl { impl_generics, .. } => impl_generics,
|
||||
};
|
||||
generics.params.iter().filter(move |param| {
|
||||
if let GenericParam::Lifetime(param) = param {
|
||||
used.contains(¶m.lifetime)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
type Supertraits = Punctuated<TypeParamBound, Token![+]>;
|
||||
|
||||
pub fn expand(input: &mut Item, is_local: bool) {
|
||||
match input {
|
||||
Item::Trait(input) => {
|
||||
let context = Context::Trait {
|
||||
name: &input.ident,
|
||||
generics: &input.generics,
|
||||
supertraits: &input.supertraits,
|
||||
};
|
||||
for inner in &mut input.items {
|
||||
if let TraitItem::Method(method) = inner {
|
||||
let sig = &mut method.sig;
|
||||
if sig.asyncness.is_some() {
|
||||
let block = &mut method.default;
|
||||
let mut has_self = has_self_in_sig(sig);
|
||||
if let Some(block) = block {
|
||||
has_self |= has_self_in_block(block);
|
||||
transform_block(context, sig, block, has_self, is_local);
|
||||
method
|
||||
.attrs
|
||||
.push(parse_quote!(#[allow(clippy::used_underscore_binding)]));
|
||||
}
|
||||
let has_default = method.default.is_some();
|
||||
transform_sig(context, sig, has_self, has_default, is_local);
|
||||
method.attrs.push(parse_quote!(#[must_use]));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Item::Impl(input) => {
|
||||
let mut lifetimes = CollectLifetimes::new("'impl");
|
||||
lifetimes.visit_type_mut(&mut *input.self_ty);
|
||||
lifetimes.visit_path_mut(&mut input.trait_.as_mut().unwrap().1);
|
||||
let params = &input.generics.params;
|
||||
let elided = lifetimes.elided;
|
||||
input.generics.params = parse_quote!(#(#elided,)* #params);
|
||||
|
||||
let context = Context::Impl {
|
||||
impl_generics: &input.generics,
|
||||
receiver: &input.self_ty,
|
||||
as_trait: &input.trait_.as_ref().unwrap().1,
|
||||
};
|
||||
for inner in &mut input.items {
|
||||
if let ImplItem::Method(method) = inner {
|
||||
let sig = &mut method.sig;
|
||||
if sig.asyncness.is_some() {
|
||||
let block = &mut method.block;
|
||||
let has_self = has_self_in_sig(sig) || has_self_in_block(block);
|
||||
transform_block(context, sig, block, has_self, is_local);
|
||||
transform_sig(context, sig, has_self, false, is_local);
|
||||
method
|
||||
.attrs
|
||||
.push(parse_quote!(#[allow(clippy::used_underscore_binding)]));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Input:
|
||||
// async fn f<T>(&self, x: &T) -> Ret;
|
||||
//
|
||||
// Output:
|
||||
// fn f<'life0, 'life1, 'async_trait, T>(
|
||||
// &'life0 self,
|
||||
// x: &'life1 T,
|
||||
// ) -> Pin<Box<dyn Future<Output = Ret> + Send + 'async_trait>>
|
||||
// where
|
||||
// 'life0: 'async_trait,
|
||||
// 'life1: 'async_trait,
|
||||
// T: 'async_trait,
|
||||
// Self: Sync + 'async_trait;
|
||||
fn transform_sig(
|
||||
context: Context,
|
||||
sig: &mut Signature,
|
||||
has_self: bool,
|
||||
has_default: bool,
|
||||
is_local: bool,
|
||||
) {
|
||||
sig.fn_token.span = sig.asyncness.take().unwrap().span;
|
||||
|
||||
let ret = match &sig.output {
|
||||
ReturnType::Default => quote!(()),
|
||||
ReturnType::Type(_, ret) => quote!(#ret),
|
||||
};
|
||||
|
||||
let mut lifetimes = CollectLifetimes::new("'life");
|
||||
for arg in sig.inputs.iter_mut() {
|
||||
match arg {
|
||||
FnArg::Receiver(arg) => lifetimes.visit_receiver_mut(arg),
|
||||
FnArg::Typed(arg) => lifetimes.visit_type_mut(&mut arg.ty),
|
||||
}
|
||||
}
|
||||
|
||||
let where_clause = sig
|
||||
.generics
|
||||
.where_clause
|
||||
.get_or_insert_with(|| WhereClause {
|
||||
where_token: Default::default(),
|
||||
predicates: Punctuated::new(),
|
||||
});
|
||||
for param in sig
|
||||
.generics
|
||||
.params
|
||||
.iter()
|
||||
.chain(context.lifetimes(&lifetimes.explicit))
|
||||
{
|
||||
match param {
|
||||
GenericParam::Type(param) => {
|
||||
let param = ¶m.ident;
|
||||
where_clause
|
||||
.predicates
|
||||
.push(parse_quote!(#param: 'async_trait));
|
||||
}
|
||||
GenericParam::Lifetime(param) => {
|
||||
let param = ¶m.lifetime;
|
||||
where_clause
|
||||
.predicates
|
||||
.push(parse_quote!(#param: 'async_trait));
|
||||
}
|
||||
GenericParam::Const(_) => {}
|
||||
}
|
||||
}
|
||||
for elided in lifetimes.elided {
|
||||
sig.generics.params.push(parse_quote!(#elided));
|
||||
where_clause
|
||||
.predicates
|
||||
.push(parse_quote!(#elided: 'async_trait));
|
||||
}
|
||||
sig.generics.params.push(parse_quote!('async_trait));
|
||||
if has_self {
|
||||
let bound: Ident = match sig.inputs.iter().next() {
|
||||
Some(FnArg::Receiver(Receiver {
|
||||
reference: Some(_),
|
||||
mutability: None,
|
||||
..
|
||||
})) => parse_quote!(Sync),
|
||||
Some(FnArg::Typed(arg))
|
||||
if match (arg.pat.as_ref(), arg.ty.as_ref()) {
|
||||
(Pat::Ident(pat), Type::Reference(ty)) => {
|
||||
pat.ident == "self" && ty.mutability.is_none()
|
||||
}
|
||||
_ => false,
|
||||
} =>
|
||||
{
|
||||
parse_quote!(Sync)
|
||||
}
|
||||
_ => parse_quote!(Send),
|
||||
};
|
||||
let assume_bound = match context {
|
||||
Context::Trait { supertraits, .. } => !has_default || has_bound(supertraits, &bound),
|
||||
Context::Impl { .. } => true,
|
||||
};
|
||||
where_clause.predicates.push(if assume_bound || is_local {
|
||||
parse_quote!(Self: 'async_trait)
|
||||
} else {
|
||||
parse_quote!(Self: ::core::marker::#bound + 'async_trait)
|
||||
});
|
||||
}
|
||||
|
||||
for (i, arg) in sig.inputs.iter_mut().enumerate() {
|
||||
match arg {
|
||||
FnArg::Receiver(Receiver {
|
||||
reference: Some(_), ..
|
||||
}) => {}
|
||||
FnArg::Receiver(arg) => arg.mutability = None,
|
||||
FnArg::Typed(arg) => {
|
||||
if let Pat::Ident(ident) = &mut *arg.pat {
|
||||
ident.by_ref = None;
|
||||
ident.mutability = None;
|
||||
} else {
|
||||
let positional = positional_arg(i);
|
||||
*arg.pat = parse_quote!(#positional);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let bounds = if is_local {
|
||||
quote!('async_trait)
|
||||
} else {
|
||||
quote!(::core::marker::Send + 'async_trait)
|
||||
};
|
||||
|
||||
sig.output = parse_quote! {
|
||||
-> ::core::pin::Pin<Box<
|
||||
dyn ::core::future::Future<Output = #ret> + #bounds
|
||||
>>
|
||||
};
|
||||
}
|
||||
|
||||
// Input:
|
||||
// async fn f<T>(&self, x: &T) -> Ret {
|
||||
// self + x
|
||||
// }
|
||||
//
|
||||
// Output:
|
||||
// async fn f<T, AsyncTrait>(_self: &AsyncTrait, x: &T) -> Ret {
|
||||
// _self + x
|
||||
// }
|
||||
// Box::pin(async_trait_method::<T, Self>(self, x))
|
||||
fn transform_block(
|
||||
context: Context,
|
||||
sig: &mut Signature,
|
||||
block: &mut Block,
|
||||
has_self: bool,
|
||||
is_local: bool,
|
||||
) {
|
||||
if let Some(Stmt::Item(syn::Item::Verbatim(item))) = block.stmts.first() {
|
||||
if block.stmts.len() == 1 && item.to_string() == ";" {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
let inner = format_ident!("__{}", sig.ident);
|
||||
let args = sig.inputs.iter().enumerate().map(|(i, arg)| match arg {
|
||||
FnArg::Receiver(Receiver { self_token, .. }) => quote!(#self_token),
|
||||
FnArg::Typed(arg) => {
|
||||
if let Pat::Ident(PatIdent { ident, .. }) = &*arg.pat {
|
||||
quote!(#ident)
|
||||
} else {
|
||||
positional_arg(i).into_token_stream()
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let mut standalone = sig.clone();
|
||||
standalone.ident = inner.clone();
|
||||
|
||||
let generics = match context {
|
||||
Context::Trait { generics, .. } => generics,
|
||||
Context::Impl { impl_generics, .. } => impl_generics,
|
||||
};
|
||||
|
||||
let mut outer_generics = generics.clone();
|
||||
for p in &mut outer_generics.params {
|
||||
match p {
|
||||
GenericParam::Type(t) => t.default = None,
|
||||
GenericParam::Const(c) => c.default = None,
|
||||
GenericParam::Lifetime(_) => {}
|
||||
}
|
||||
}
|
||||
if !has_self {
|
||||
if let Some(mut where_clause) = outer_generics.where_clause {
|
||||
where_clause.predicates = where_clause
|
||||
.predicates
|
||||
.into_iter()
|
||||
.filter_map(|mut pred| {
|
||||
if has_self_in_where_predicate(&mut pred) {
|
||||
None
|
||||
} else {
|
||||
Some(pred)
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
outer_generics.where_clause = Some(where_clause);
|
||||
}
|
||||
}
|
||||
|
||||
let fn_generics = mem::replace(&mut standalone.generics, outer_generics);
|
||||
standalone.generics.params.extend(fn_generics.params);
|
||||
if let Some(where_clause) = fn_generics.where_clause {
|
||||
standalone
|
||||
.generics
|
||||
.make_where_clause()
|
||||
.predicates
|
||||
.extend(where_clause.predicates);
|
||||
}
|
||||
|
||||
if has_async_lifetime(&mut standalone, block) {
|
||||
standalone.generics.params.push(parse_quote!('async_trait));
|
||||
}
|
||||
|
||||
let mut types = standalone
|
||||
.generics
|
||||
.type_params()
|
||||
.map(|param| param.ident.clone())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let mut self_bound = None::<TypeParamBound>;
|
||||
match standalone.inputs.iter_mut().next() {
|
||||
Some(
|
||||
arg @ FnArg::Receiver(Receiver {
|
||||
reference: Some(_), ..
|
||||
}),
|
||||
) => {
|
||||
let (lifetime, mutability, self_token) = match arg {
|
||||
FnArg::Receiver(Receiver {
|
||||
reference: Some((_, lifetime)),
|
||||
mutability,
|
||||
self_token,
|
||||
..
|
||||
}) => (lifetime, mutability, self_token),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
let under_self = Ident::new("_self", self_token.span);
|
||||
match context {
|
||||
Context::Trait { .. } => {
|
||||
self_bound = Some(match mutability {
|
||||
Some(_) => parse_quote!(::core::marker::Send),
|
||||
None => parse_quote!(::core::marker::Sync),
|
||||
});
|
||||
*arg = parse_quote! {
|
||||
#under_self: &#lifetime #mutability AsyncTrait
|
||||
};
|
||||
}
|
||||
Context::Impl { receiver, .. } => {
|
||||
let mut ty = quote!(#receiver);
|
||||
if let Type::TraitObject(trait_object) = receiver {
|
||||
if trait_object.dyn_token.is_none() {
|
||||
ty = quote!(dyn #ty);
|
||||
}
|
||||
if trait_object.bounds.len() > 1 {
|
||||
ty = quote!((#ty));
|
||||
}
|
||||
}
|
||||
*arg = parse_quote! {
|
||||
#under_self: &#lifetime #mutability #ty
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
Some(arg @ FnArg::Receiver(_)) => {
|
||||
let (self_token, mutability) = match arg {
|
||||
FnArg::Receiver(Receiver {
|
||||
self_token,
|
||||
mutability,
|
||||
..
|
||||
}) => (self_token, mutability),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
let under_self = Ident::new("_self", self_token.span);
|
||||
match context {
|
||||
Context::Trait { .. } => {
|
||||
self_bound = Some(parse_quote!(::core::marker::Send));
|
||||
*arg = parse_quote! {
|
||||
#mutability #under_self: AsyncTrait
|
||||
};
|
||||
}
|
||||
Context::Impl { receiver, .. } => {
|
||||
*arg = parse_quote! {
|
||||
#mutability #under_self: #receiver
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
Some(FnArg::Typed(arg)) => {
|
||||
if let Pat::Ident(arg) = &mut *arg.pat {
|
||||
if arg.ident == "self" {
|
||||
arg.ident = Ident::new("_self", arg.ident.span());
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
if let Context::Trait { name, generics, .. } = context {
|
||||
if has_self {
|
||||
let (_, generics, _) = generics.split_for_impl();
|
||||
let mut self_param: TypeParam = parse_quote!(AsyncTrait: ?Sized + #name #generics);
|
||||
if !is_local {
|
||||
self_param.bounds.extend(self_bound);
|
||||
}
|
||||
let count = standalone
|
||||
.generics
|
||||
.params
|
||||
.iter()
|
||||
.take_while(|param| {
|
||||
if let GenericParam::Const(_) = param {
|
||||
false
|
||||
} else {
|
||||
true
|
||||
}
|
||||
})
|
||||
.count();
|
||||
standalone
|
||||
.generics
|
||||
.params
|
||||
.insert(count, GenericParam::Type(self_param));
|
||||
types.push(Ident::new("Self", Span::call_site()));
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(where_clause) = &mut standalone.generics.where_clause {
|
||||
// Work around an input bound like `where Self::Output: Send` expanding
|
||||
// to `where <AsyncTrait>::Output: Send` which is illegal syntax because
|
||||
// `where<T>` is reserved for future use... :(
|
||||
where_clause.predicates.insert(0, parse_quote!((): Sized));
|
||||
}
|
||||
|
||||
let mut replace = match context {
|
||||
Context::Trait { .. } => ReplaceReceiver::with(parse_quote!(AsyncTrait)),
|
||||
Context::Impl {
|
||||
receiver, as_trait, ..
|
||||
} => ReplaceReceiver::with_as_trait(receiver.clone(), as_trait.clone()),
|
||||
};
|
||||
replace.visit_signature_mut(&mut standalone);
|
||||
replace.visit_block_mut(block);
|
||||
|
||||
let mut generics = types;
|
||||
let consts = standalone
|
||||
.generics
|
||||
.const_params()
|
||||
.map(|param| param.ident.clone());
|
||||
generics.extend(consts);
|
||||
|
||||
let allow_non_snake_case = if sig.ident != sig.ident.to_string().to_lowercase() {
|
||||
Some(quote!(non_snake_case,))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let brace = block.brace_token;
|
||||
let box_pin = quote_spanned!(brace.span=> {
|
||||
#[allow(
|
||||
#allow_non_snake_case
|
||||
unused_parens, // https://github.com/dtolnay/async-trait/issues/118
|
||||
clippy::missing_docs_in_private_items,
|
||||
clippy::needless_lifetimes,
|
||||
clippy::ptr_arg,
|
||||
clippy::trivially_copy_pass_by_ref,
|
||||
clippy::type_repetition_in_bounds,
|
||||
clippy::used_underscore_binding,
|
||||
)]
|
||||
#standalone #block
|
||||
Box::pin(#inner::<#(#generics),*>(#(#args),*))
|
||||
});
|
||||
*block = parse_quote!(#box_pin);
|
||||
block.brace_token = brace;
|
||||
}
|
||||
|
||||
fn positional_arg(i: usize) -> Ident {
|
||||
format_ident!("__arg{}", i)
|
||||
}
|
||||
|
||||
fn has_bound(supertraits: &Supertraits, marker: &Ident) -> bool {
|
||||
for bound in supertraits {
|
||||
if let TypeParamBound::Trait(bound) = bound {
|
||||
if bound.path.is_ident(marker) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
330
third_party/rust/async-trait/src/lib.rs
vendored
Normal file
330
third_party/rust/async-trait/src/lib.rs
vendored
Normal file
@ -0,0 +1,330 @@
|
||||
//! [![github]](https://github.com/dtolnay/async-trait) [![crates-io]](https://crates.io/crates/async-trait) [![docs-rs]](https://docs.rs/async-trait)
|
||||
//!
|
||||
//! [github]: https://img.shields.io/badge/github-8da0cb?style=for-the-badge&labelColor=555555&logo=github
|
||||
//! [crates-io]: https://img.shields.io/badge/crates.io-fc8d62?style=for-the-badge&labelColor=555555&logo=rust
|
||||
//! [docs-rs]: https://img.shields.io/badge/docs.rs-66c2a5?style=for-the-badge&labelColor=555555&logoColor=white&logo=data:image/svg+xml;base64,PHN2ZyByb2xlPSJpbWciIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgdmlld0JveD0iMCAwIDUxMiA1MTIiPjxwYXRoIGZpbGw9IiNmNWY1ZjUiIGQ9Ik00ODguNiAyNTAuMkwzOTIgMjE0VjEwNS41YzAtMTUtOS4zLTI4LjQtMjMuNC0zMy43bC0xMDAtMzcuNWMtOC4xLTMuMS0xNy4xLTMuMS0yNS4zIDBsLTEwMCAzNy41Yy0xNC4xIDUuMy0yMy40IDE4LjctMjMuNCAzMy43VjIxNGwtOTYuNiAzNi4yQzkuMyAyNTUuNSAwIDI2OC45IDAgMjgzLjlWMzk0YzAgMTMuNiA3LjcgMjYuMSAxOS45IDMyLjJsMTAwIDUwYzEwLjEgNS4xIDIyLjEgNS4xIDMyLjIgMGwxMDMuOS01MiAxMDMuOSA1MmMxMC4xIDUuMSAyMi4xIDUuMSAzMi4yIDBsMTAwLTUwYzEyLjItNi4xIDE5LjktMTguNiAxOS45LTMyLjJWMjgzLjljMC0xNS05LjMtMjguNC0yMy40LTMzLjd6TTM1OCAyMTQuOGwtODUgMzEuOXYtNjguMmw4NS0zN3Y3My4zek0xNTQgMTA0LjFsMTAyLTM4LjIgMTAyIDM4LjJ2LjZsLTEwMiA0MS40LTEwMi00MS40di0uNnptODQgMjkxLjFsLTg1IDQyLjV2LTc5LjFsODUtMzguOHY3NS40em0wLTExMmwtMTAyIDQxLjQtMTAyLTQxLjR2LS42bDEwMi0zOC4yIDEwMiAzOC4ydi42em0yNDAgMTEybC04NSA0Mi41di03OS4xbDg1LTM4Ljh2NzUuNHptMC0xMTJsLTEwMiA0MS40LTEwMi00MS40di0uNmwxMDItMzguMiAxMDIgMzguMnYuNnoiPjwvcGF0aD48L3N2Zz4K
|
||||
//!
|
||||
//! <br>
|
||||
//!
|
||||
//! <h5>Type erasure for async trait methods</h5>
|
||||
//!
|
||||
//! The initial round of stabilizations for the async/await language feature in
|
||||
//! Rust 1.39 did not include support for async fn in traits. Trying to include
|
||||
//! an async fn in a trait produces the following error:
|
||||
//!
|
||||
//! ```compile_fail
|
||||
//! trait MyTrait {
|
||||
//! async fn f() {}
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! ```text
|
||||
//! error[E0706]: trait fns cannot be declared `async`
|
||||
//! --> src/main.rs:4:5
|
||||
//! |
|
||||
//! 4 | async fn f() {}
|
||||
//! | ^^^^^^^^^^^^^^^
|
||||
//! ```
|
||||
//!
|
||||
//! This crate provides an attribute macro to make async fn in traits work.
|
||||
//!
|
||||
//! Please refer to [*why async fn in traits are hard*][hard] for a deeper
|
||||
//! analysis of how this implementation differs from what the compiler and
|
||||
//! language hope to deliver in the future.
|
||||
//!
|
||||
//! [hard]: https://smallcultfollowing.com/babysteps/blog/2019/10/26/async-fn-in-traits-are-hard/
|
||||
//!
|
||||
//! <br>
|
||||
//!
|
||||
//! # Example
|
||||
//!
|
||||
//! This example implements the core of a highly effective advertising platform
|
||||
//! using async fn in a trait.
|
||||
//!
|
||||
//! The only thing to notice here is that we write an `#[async_trait]` macro on
|
||||
//! top of traits and trait impls that contain async fn, and then they work.
|
||||
//!
|
||||
//! ```
|
||||
//! use async_trait::async_trait;
|
||||
//!
|
||||
//! #[async_trait]
|
||||
//! trait Advertisement {
|
||||
//! async fn run(&self);
|
||||
//! }
|
||||
//!
|
||||
//! struct Modal;
|
||||
//!
|
||||
//! #[async_trait]
|
||||
//! impl Advertisement for Modal {
|
||||
//! async fn run(&self) {
|
||||
//! self.render_fullscreen().await;
|
||||
//! for _ in 0..4u16 {
|
||||
//! remind_user_to_join_mailing_list().await;
|
||||
//! }
|
||||
//! self.hide_for_now().await;
|
||||
//! }
|
||||
//! }
|
||||
//!
|
||||
//! struct AutoplayingVideo {
|
||||
//! media_url: String,
|
||||
//! }
|
||||
//!
|
||||
//! #[async_trait]
|
||||
//! impl Advertisement for AutoplayingVideo {
|
||||
//! async fn run(&self) {
|
||||
//! let stream = connect(&self.media_url).await;
|
||||
//! stream.play().await;
|
||||
//!
|
||||
//! // Video probably persuaded user to join our mailing list!
|
||||
//! Modal.run().await;
|
||||
//! }
|
||||
//! }
|
||||
//! #
|
||||
//! # impl Modal {
|
||||
//! # async fn render_fullscreen(&self) {}
|
||||
//! # async fn hide_for_now(&self) {}
|
||||
//! # }
|
||||
//! #
|
||||
//! # async fn remind_user_to_join_mailing_list() {}
|
||||
//! #
|
||||
//! # struct Stream;
|
||||
//! # async fn connect(_media_url: &str) -> Stream { Stream }
|
||||
//! # impl Stream {
|
||||
//! # async fn play(&self) {}
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! <br><br>
|
||||
//!
|
||||
//! # Supported features
|
||||
//!
|
||||
//! It is the intention that all features of Rust traits should work nicely with
|
||||
//! #\[async_trait\], but the edge cases are numerous. Please file an issue if
|
||||
//! you see unexpected borrow checker errors, type errors, or warnings. There is
|
||||
//! no use of `unsafe` in the expanded code, so rest assured that if your code
|
||||
//! compiles it can't be that badly broken.
|
||||
//!
|
||||
//! > ☑ Self by value, by reference, by mut reference, or no self;<br>
|
||||
//! > ☑ Any number of arguments, any return value;<br>
|
||||
//! > ☑ Generic type parameters and lifetime parameters;<br>
|
||||
//! > ☑ Associated types;<br>
|
||||
//! > ☑ Having async and non-async functions in the same trait;<br>
|
||||
//! > ☑ Default implementations provided by the trait;<br>
|
||||
//! > ☑ Elided lifetimes;<br>
|
||||
//! > ☑ Dyn-capable traits.<br>
|
||||
//!
|
||||
//! <br>
|
||||
//!
|
||||
//! # Explanation
|
||||
//!
|
||||
//! Async fns get transformed into methods that return `Pin<Box<dyn Future +
|
||||
//! Send + 'async>>` and delegate to a private async freestanding function.
|
||||
//!
|
||||
//! For example the `impl Advertisement for AutoplayingVideo` above would be
|
||||
//! expanded as:
|
||||
//!
|
||||
//! ```
|
||||
//! # const IGNORE: &str = stringify! {
|
||||
//! impl Advertisement for AutoplayingVideo {
|
||||
//! fn run<'async>(
|
||||
//! &'async self,
|
||||
//! ) -> Pin<Box<dyn core::future::Future<Output = ()> + Send + 'async>>
|
||||
//! where
|
||||
//! Self: Sync + 'async,
|
||||
//! {
|
||||
//! async fn run(_self: &AutoplayingVideo) {
|
||||
//! /* the original method body */
|
||||
//! }
|
||||
//!
|
||||
//! Box::pin(run(self))
|
||||
//! }
|
||||
//! }
|
||||
//! # };
|
||||
//! ```
|
||||
//!
|
||||
//! <br><br>
|
||||
//!
|
||||
//! # Non-threadsafe futures
|
||||
//!
|
||||
//! Not all async traits need futures that are `dyn Future + Send`. To avoid
|
||||
//! having Send and Sync bounds placed on the async trait methods, invoke the
|
||||
//! async trait macro as `#[async_trait(?Send)]` on both the trait and the impl
|
||||
//! blocks.
|
||||
//!
|
||||
//! <br>
|
||||
//!
|
||||
//! # Elided lifetimes
|
||||
//!
|
||||
//! Be aware that async fn syntax does not allow lifetime elision outside of `&`
|
||||
//! and `&mut` references. (This is true even when not using #\[async_trait\].)
|
||||
//! Lifetimes must be named or marked by the placeholder `'_`.
|
||||
//!
|
||||
//! Fortunately the compiler is able to diagnose missing lifetimes with a good
|
||||
//! error message.
|
||||
//!
|
||||
//! ```compile_fail
|
||||
//! # use async_trait::async_trait;
|
||||
//! #
|
||||
//! type Elided<'a> = &'a usize;
|
||||
//!
|
||||
//! #[async_trait]
|
||||
//! trait Test {
|
||||
//! async fn test(not_okay: Elided, okay: &usize) {}
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! ```text
|
||||
//! error[E0726]: implicit elided lifetime not allowed here
|
||||
//! --> src/main.rs:9:29
|
||||
//! |
|
||||
//! 9 | async fn test(not_okay: Elided, okay: &usize) {}
|
||||
//! | ^^^^^^- help: indicate the anonymous lifetime: `<'_>`
|
||||
//! ```
|
||||
//!
|
||||
//! The fix is to name the lifetime or use `'_`.
|
||||
//!
|
||||
//! ```
|
||||
//! # use async_trait::async_trait;
|
||||
//! #
|
||||
//! # type Elided<'a> = &'a usize;
|
||||
//! #
|
||||
//! #[async_trait]
|
||||
//! trait Test {
|
||||
//! // either
|
||||
//! async fn test<'e>(elided: Elided<'e>) {}
|
||||
//! # }
|
||||
//! # #[async_trait]
|
||||
//! # trait Test2 {
|
||||
//! // or
|
||||
//! async fn test(elided: Elided<'_>) {}
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! <br><br>
|
||||
//!
|
||||
//! # Dyn traits
|
||||
//!
|
||||
//! Traits with async methods can be used as trait objects as long as they meet
|
||||
//! the usual requirements for dyn -- no methods with type parameters, no self
|
||||
//! by value, no associated types, etc.
|
||||
//!
|
||||
//! ```
|
||||
//! # use async_trait::async_trait;
|
||||
//! #
|
||||
//! #[async_trait]
|
||||
//! pub trait ObjectSafe {
|
||||
//! async fn f(&self);
|
||||
//! async fn g(&mut self);
|
||||
//! }
|
||||
//!
|
||||
//! # const IGNORE: &str = stringify! {
|
||||
//! impl ObjectSafe for MyType {...}
|
||||
//!
|
||||
//! let value: MyType = ...;
|
||||
//! # };
|
||||
//! #
|
||||
//! # struct MyType;
|
||||
//! #
|
||||
//! # #[async_trait]
|
||||
//! # impl ObjectSafe for MyType {
|
||||
//! # async fn f(&self) {}
|
||||
//! # async fn g(&mut self) {}
|
||||
//! # }
|
||||
//! #
|
||||
//! # let value: MyType = MyType;
|
||||
//! let object = &value as &dyn ObjectSafe; // make trait object
|
||||
//! ```
|
||||
//!
|
||||
//! The one wrinkle is in traits that provide default implementations of async
|
||||
//! methods. In order for the default implementation to produce a future that is
|
||||
//! Send, the async_trait macro must emit a bound of `Self: Sync` on trait
|
||||
//! methods that take `&self` and a bound `Self: Send` on trait methods that
|
||||
//! take `&mut self`. An example of the former is visible in the expanded code
|
||||
//! in the explanation section above.
|
||||
//!
|
||||
//! If you make a trait with async methods that have default implementations,
|
||||
//! everything will work except that the trait cannot be used as a trait object.
|
||||
//! Creating a value of type `&dyn Trait` will produce an error that looks like
|
||||
//! this:
|
||||
//!
|
||||
//! ```text
|
||||
//! error: the trait `Test` cannot be made into an object
|
||||
//! --> src/main.rs:8:5
|
||||
//! |
|
||||
//! 8 | async fn cannot_dyn(&self) {}
|
||||
//! | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
//! ```
|
||||
//!
|
||||
//! For traits that need to be object safe and need to have default
|
||||
//! implementations for some async methods, there are two resolutions. Either
|
||||
//! you can add Send and/or Sync as supertraits (Send if there are `&mut self`
|
||||
//! methods with default implementations, Sync if there are `&self` methods with
|
||||
//! default implementions) to constrain all implementors of the trait such that
|
||||
//! the default implementations are applicable to them:
|
||||
//!
|
||||
//! ```
|
||||
//! # use async_trait::async_trait;
|
||||
//! #
|
||||
//! #[async_trait]
|
||||
//! pub trait ObjectSafe: Sync { // added supertrait
|
||||
//! async fn can_dyn(&self) {}
|
||||
//! }
|
||||
//! #
|
||||
//! # struct MyType;
|
||||
//! #
|
||||
//! # #[async_trait]
|
||||
//! # impl ObjectSafe for MyType {}
|
||||
//! #
|
||||
//! # let value = MyType;
|
||||
//!
|
||||
//! let object = &value as &dyn ObjectSafe;
|
||||
//! ```
|
||||
//!
|
||||
//! or you can strike the problematic methods from your trait object by
|
||||
//! bounding them with `Self: Sized`:
|
||||
//!
|
||||
//! ```
|
||||
//! # use async_trait::async_trait;
|
||||
//! #
|
||||
//! #[async_trait]
|
||||
//! pub trait ObjectSafe {
|
||||
//! async fn cannot_dyn(&self) where Self: Sized {}
|
||||
//!
|
||||
//! // presumably other methods
|
||||
//! }
|
||||
//! #
|
||||
//! # struct MyType;
|
||||
//! #
|
||||
//! # #[async_trait]
|
||||
//! # impl ObjectSafe for MyType {}
|
||||
//! #
|
||||
//! # let value = MyType;
|
||||
//!
|
||||
//! let object = &value as &dyn ObjectSafe;
|
||||
//! ```
|
||||
|
||||
#![allow(clippy::match_like_matches_macro)] // matches! requires Rust 1.42
|
||||
|
||||
extern crate proc_macro;
|
||||
|
||||
mod args;
|
||||
mod expand;
|
||||
mod lifetime;
|
||||
mod parse;
|
||||
mod receiver;
|
||||
mod respan;
|
||||
|
||||
use crate::args::Args;
|
||||
use crate::expand::expand;
|
||||
use crate::parse::Item;
|
||||
use proc_macro::TokenStream;
|
||||
use quote::quote;
|
||||
use syn::parse_macro_input;
|
||||
|
||||
#[proc_macro_attribute]
|
||||
pub fn async_trait(args: TokenStream, input: TokenStream) -> TokenStream {
|
||||
let args = parse_macro_input!(args as Args);
|
||||
let mut item = parse_macro_input!(input as Item);
|
||||
expand(&mut item, args.local);
|
||||
TokenStream::from(quote!(#item))
|
||||
}
|
80
third_party/rust/async-trait/src/lifetime.rs
vendored
Normal file
80
third_party/rust/async-trait/src/lifetime.rs
vendored
Normal file
@ -0,0 +1,80 @@
|
||||
use proc_macro2::Span;
|
||||
use syn::visit_mut::{self, VisitMut};
|
||||
use syn::{Block, GenericArgument, Item, Lifetime, Receiver, Signature, TypeReference};
|
||||
|
||||
pub fn has_async_lifetime(sig: &mut Signature, block: &mut Block) -> bool {
|
||||
let mut visitor = HasAsyncLifetime(false);
|
||||
visitor.visit_signature_mut(sig);
|
||||
visitor.visit_block_mut(block);
|
||||
visitor.0
|
||||
}
|
||||
|
||||
struct HasAsyncLifetime(bool);
|
||||
|
||||
impl VisitMut for HasAsyncLifetime {
|
||||
fn visit_lifetime_mut(&mut self, life: &mut Lifetime) {
|
||||
self.0 |= life.to_string() == "'async_trait";
|
||||
}
|
||||
|
||||
fn visit_item_mut(&mut self, _: &mut Item) {
|
||||
// Do not recurse into nested items.
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CollectLifetimes {
|
||||
pub elided: Vec<Lifetime>,
|
||||
pub explicit: Vec<Lifetime>,
|
||||
pub name: &'static str,
|
||||
}
|
||||
|
||||
impl CollectLifetimes {
|
||||
pub fn new(name: &'static str) -> Self {
|
||||
CollectLifetimes {
|
||||
elided: Vec::new(),
|
||||
explicit: Vec::new(),
|
||||
name,
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_opt_lifetime(&mut self, lifetime: &mut Option<Lifetime>) {
|
||||
match lifetime {
|
||||
None => *lifetime = Some(self.next_lifetime()),
|
||||
Some(lifetime) => self.visit_lifetime(lifetime),
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_lifetime(&mut self, lifetime: &mut Lifetime) {
|
||||
if lifetime.ident == "_" {
|
||||
*lifetime = self.next_lifetime();
|
||||
} else {
|
||||
self.explicit.push(lifetime.clone());
|
||||
}
|
||||
}
|
||||
|
||||
fn next_lifetime(&mut self) -> Lifetime {
|
||||
let name = format!("{}{}", self.name, self.elided.len());
|
||||
let life = Lifetime::new(&name, Span::call_site());
|
||||
self.elided.push(life.clone());
|
||||
life
|
||||
}
|
||||
}
|
||||
|
||||
impl VisitMut for CollectLifetimes {
|
||||
fn visit_receiver_mut(&mut self, arg: &mut Receiver) {
|
||||
if let Some((_, lifetime)) = &mut arg.reference {
|
||||
self.visit_opt_lifetime(lifetime);
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_type_reference_mut(&mut self, ty: &mut TypeReference) {
|
||||
self.visit_opt_lifetime(&mut ty.lifetime);
|
||||
visit_mut::visit_type_reference_mut(self, ty);
|
||||
}
|
||||
|
||||
fn visit_generic_argument_mut(&mut self, gen: &mut GenericArgument) {
|
||||
if let GenericArgument::Lifetime(lifetime) = gen {
|
||||
self.visit_lifetime(lifetime);
|
||||
}
|
||||
visit_mut::visit_generic_argument_mut(self, gen);
|
||||
}
|
||||
}
|
34
third_party/rust/async-trait/src/parse.rs
vendored
Normal file
34
third_party/rust/async-trait/src/parse.rs
vendored
Normal file
@ -0,0 +1,34 @@
|
||||
use proc_macro2::Span;
|
||||
use syn::parse::{Error, Parse, ParseStream, Result};
|
||||
use syn::{Attribute, ItemImpl, ItemTrait, Token};
|
||||
|
||||
pub enum Item {
|
||||
Trait(ItemTrait),
|
||||
Impl(ItemImpl),
|
||||
}
|
||||
|
||||
impl Parse for Item {
|
||||
fn parse(input: ParseStream) -> Result<Self> {
|
||||
let attrs = input.call(Attribute::parse_outer)?;
|
||||
let mut lookahead = input.lookahead1();
|
||||
if lookahead.peek(Token![unsafe]) {
|
||||
let ahead = input.fork();
|
||||
ahead.parse::<Token![unsafe]>()?;
|
||||
lookahead = ahead.lookahead1();
|
||||
}
|
||||
if lookahead.peek(Token![pub]) || lookahead.peek(Token![trait]) {
|
||||
let mut item: ItemTrait = input.parse()?;
|
||||
item.attrs = attrs;
|
||||
Ok(Item::Trait(item))
|
||||
} else if lookahead.peek(Token![impl]) {
|
||||
let mut item: ItemImpl = input.parse()?;
|
||||
if item.trait_.is_none() {
|
||||
return Err(Error::new(Span::call_site(), "expected a trait impl"));
|
||||
}
|
||||
item.attrs = attrs;
|
||||
Ok(Item::Impl(item))
|
||||
} else {
|
||||
Err(lookahead.error())
|
||||
}
|
||||
}
|
||||
}
|
321
third_party/rust/async-trait/src/receiver.rs
vendored
Normal file
321
third_party/rust/async-trait/src/receiver.rs
vendored
Normal file
@ -0,0 +1,321 @@
|
||||
use crate::respan::respan;
|
||||
use proc_macro2::{Group, Spacing, Span, TokenStream, TokenTree};
|
||||
use quote::{quote, quote_spanned};
|
||||
use std::iter::FromIterator;
|
||||
use std::mem;
|
||||
use syn::punctuated::Punctuated;
|
||||
use syn::visit_mut::{self, VisitMut};
|
||||
use syn::{
|
||||
parse_quote, Block, Error, ExprPath, ExprStruct, Ident, Item, Macro, PatPath, PatStruct,
|
||||
PatTupleStruct, Path, PathArguments, QSelf, Receiver, Signature, Token, Type, TypePath,
|
||||
WherePredicate,
|
||||
};
|
||||
|
||||
pub fn has_self_in_sig(sig: &mut Signature) -> bool {
|
||||
let mut visitor = HasSelf(false);
|
||||
visitor.visit_signature_mut(sig);
|
||||
visitor.0
|
||||
}
|
||||
|
||||
pub fn has_self_in_where_predicate(where_predicate: &mut WherePredicate) -> bool {
|
||||
let mut visitor = HasSelf(false);
|
||||
visitor.visit_where_predicate_mut(where_predicate);
|
||||
visitor.0
|
||||
}
|
||||
|
||||
pub fn has_self_in_block(block: &mut Block) -> bool {
|
||||
let mut visitor = HasSelf(false);
|
||||
visitor.visit_block_mut(block);
|
||||
visitor.0
|
||||
}
|
||||
|
||||
fn has_self_in_token_stream(tokens: TokenStream) -> bool {
|
||||
tokens.into_iter().any(|tt| match tt {
|
||||
TokenTree::Ident(ident) => ident == "Self",
|
||||
TokenTree::Group(group) => has_self_in_token_stream(group.stream()),
|
||||
_ => false,
|
||||
})
|
||||
}
|
||||
|
||||
struct HasSelf(bool);
|
||||
|
||||
impl VisitMut for HasSelf {
|
||||
fn visit_expr_path_mut(&mut self, expr: &mut ExprPath) {
|
||||
self.0 |= expr.path.segments[0].ident == "Self";
|
||||
visit_mut::visit_expr_path_mut(self, expr);
|
||||
}
|
||||
|
||||
fn visit_pat_path_mut(&mut self, pat: &mut PatPath) {
|
||||
self.0 |= pat.path.segments[0].ident == "Self";
|
||||
visit_mut::visit_pat_path_mut(self, pat);
|
||||
}
|
||||
|
||||
fn visit_type_path_mut(&mut self, ty: &mut TypePath) {
|
||||
self.0 |= ty.path.segments[0].ident == "Self";
|
||||
visit_mut::visit_type_path_mut(self, ty);
|
||||
}
|
||||
|
||||
fn visit_receiver_mut(&mut self, _arg: &mut Receiver) {
|
||||
self.0 = true;
|
||||
}
|
||||
|
||||
fn visit_item_mut(&mut self, _: &mut Item) {
|
||||
// Do not recurse into nested items.
|
||||
}
|
||||
|
||||
fn visit_macro_mut(&mut self, mac: &mut Macro) {
|
||||
if !contains_fn(mac.tokens.clone()) {
|
||||
self.0 |= has_self_in_token_stream(mac.tokens.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ReplaceReceiver {
|
||||
pub with: Type,
|
||||
pub as_trait: Option<Path>,
|
||||
}
|
||||
|
||||
impl ReplaceReceiver {
|
||||
pub fn with(ty: Type) -> Self {
|
||||
ReplaceReceiver {
|
||||
with: ty,
|
||||
as_trait: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_as_trait(ty: Type, as_trait: Path) -> Self {
|
||||
ReplaceReceiver {
|
||||
with: ty,
|
||||
as_trait: Some(as_trait),
|
||||
}
|
||||
}
|
||||
|
||||
fn self_ty(&self, span: Span) -> Type {
|
||||
respan(&self.with, span)
|
||||
}
|
||||
|
||||
fn self_to_qself_type(&self, qself: &mut Option<QSelf>, path: &mut Path) {
|
||||
let include_as_trait = true;
|
||||
self.self_to_qself(qself, path, include_as_trait);
|
||||
}
|
||||
|
||||
fn self_to_qself_expr(&self, qself: &mut Option<QSelf>, path: &mut Path) {
|
||||
let include_as_trait = false;
|
||||
self.self_to_qself(qself, path, include_as_trait);
|
||||
}
|
||||
|
||||
fn self_to_qself(&self, qself: &mut Option<QSelf>, path: &mut Path, include_as_trait: bool) {
|
||||
if path.leading_colon.is_some() {
|
||||
return;
|
||||
}
|
||||
|
||||
let first = &path.segments[0];
|
||||
if first.ident != "Self" || !first.arguments.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
if path.segments.len() == 1 {
|
||||
self.self_to_expr_path(path);
|
||||
return;
|
||||
}
|
||||
|
||||
let span = first.ident.span();
|
||||
*qself = Some(QSelf {
|
||||
lt_token: Token![<](span),
|
||||
ty: Box::new(self.self_ty(span)),
|
||||
position: 0,
|
||||
as_token: None,
|
||||
gt_token: Token![>](span),
|
||||
});
|
||||
|
||||
if include_as_trait && self.as_trait.is_some() {
|
||||
let as_trait = self.as_trait.as_ref().unwrap().clone();
|
||||
path.leading_colon = as_trait.leading_colon;
|
||||
qself.as_mut().unwrap().position = as_trait.segments.len();
|
||||
|
||||
let segments = mem::replace(&mut path.segments, as_trait.segments);
|
||||
path.segments.push_punct(Default::default());
|
||||
path.segments.extend(segments.into_pairs().skip(1));
|
||||
} else {
|
||||
path.leading_colon = Some(**path.segments.pairs().next().unwrap().punct().unwrap());
|
||||
|
||||
let segments = mem::replace(&mut path.segments, Punctuated::new());
|
||||
path.segments = segments.into_pairs().skip(1).collect();
|
||||
}
|
||||
}
|
||||
|
||||
fn self_to_expr_path(&self, path: &mut Path) {
|
||||
if path.leading_colon.is_some() {
|
||||
return;
|
||||
}
|
||||
|
||||
let first = &path.segments[0];
|
||||
if first.ident != "Self" || !first.arguments.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
if let Type::Path(self_ty) = self.self_ty(first.ident.span()) {
|
||||
let variant = mem::replace(path, self_ty.path);
|
||||
for segment in &mut path.segments {
|
||||
if let PathArguments::AngleBracketed(bracketed) = &mut segment.arguments {
|
||||
if bracketed.colon2_token.is_none() && !bracketed.args.is_empty() {
|
||||
bracketed.colon2_token = Some(Default::default());
|
||||
}
|
||||
}
|
||||
}
|
||||
if variant.segments.len() > 1 {
|
||||
path.segments.push_punct(Default::default());
|
||||
path.segments.extend(variant.segments.into_pairs().skip(1));
|
||||
}
|
||||
} else {
|
||||
let span = path.segments[0].ident.span();
|
||||
let msg = "Self type of this impl is unsupported in expression position";
|
||||
let error = Error::new(span, msg).to_compile_error();
|
||||
*path = parse_quote!(::core::marker::PhantomData::<#error>);
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_token_stream(&self, tokens: &mut TokenStream) -> bool {
|
||||
let mut out = Vec::new();
|
||||
let mut modified = false;
|
||||
let mut iter = tokens.clone().into_iter().peekable();
|
||||
while let Some(tt) = iter.next() {
|
||||
match tt {
|
||||
TokenTree::Ident(mut ident) => {
|
||||
modified |= prepend_underscore_to_self(&mut ident);
|
||||
if ident == "Self" {
|
||||
modified = true;
|
||||
if self.as_trait.is_none() {
|
||||
let ident = Ident::new("AsyncTrait", ident.span());
|
||||
out.push(TokenTree::Ident(ident));
|
||||
} else {
|
||||
let self_ty = self.self_ty(ident.span());
|
||||
match iter.peek() {
|
||||
Some(TokenTree::Punct(p))
|
||||
if p.as_char() == ':' && p.spacing() == Spacing::Joint =>
|
||||
{
|
||||
let next = iter.next().unwrap();
|
||||
match iter.peek() {
|
||||
Some(TokenTree::Punct(p)) if p.as_char() == ':' => {
|
||||
let span = ident.span();
|
||||
out.extend(quote_spanned!(span=> <#self_ty>));
|
||||
}
|
||||
_ => out.extend(quote!(#self_ty)),
|
||||
}
|
||||
out.push(next);
|
||||
}
|
||||
_ => out.extend(quote!(#self_ty)),
|
||||
}
|
||||
}
|
||||
} else {
|
||||
out.push(TokenTree::Ident(ident));
|
||||
}
|
||||
}
|
||||
TokenTree::Group(group) => {
|
||||
let mut content = group.stream();
|
||||
modified |= self.visit_token_stream(&mut content);
|
||||
let mut new = Group::new(group.delimiter(), content);
|
||||
new.set_span(group.span());
|
||||
out.push(TokenTree::Group(new));
|
||||
}
|
||||
other => out.push(other),
|
||||
}
|
||||
}
|
||||
if modified {
|
||||
*tokens = TokenStream::from_iter(out);
|
||||
}
|
||||
modified
|
||||
}
|
||||
}
|
||||
|
||||
impl VisitMut for ReplaceReceiver {
|
||||
// `Self` -> `Receiver`
|
||||
fn visit_type_mut(&mut self, ty: &mut Type) {
|
||||
if let Type::Path(node) = ty {
|
||||
if node.qself.is_none() && node.path.is_ident("Self") {
|
||||
*ty = self.self_ty(node.path.segments[0].ident.span());
|
||||
} else {
|
||||
self.visit_type_path_mut(node);
|
||||
}
|
||||
} else {
|
||||
visit_mut::visit_type_mut(self, ty);
|
||||
}
|
||||
}
|
||||
|
||||
// `Self::Assoc` -> `<Receiver>::Assoc`
|
||||
fn visit_type_path_mut(&mut self, ty: &mut TypePath) {
|
||||
if ty.qself.is_none() {
|
||||
self.self_to_qself_type(&mut ty.qself, &mut ty.path);
|
||||
}
|
||||
visit_mut::visit_type_path_mut(self, ty);
|
||||
}
|
||||
|
||||
// `Self::method` -> `<Receiver>::method`
|
||||
fn visit_expr_path_mut(&mut self, expr: &mut ExprPath) {
|
||||
if expr.qself.is_none() {
|
||||
prepend_underscore_to_self(&mut expr.path.segments[0].ident);
|
||||
self.self_to_qself_expr(&mut expr.qself, &mut expr.path);
|
||||
}
|
||||
visit_mut::visit_expr_path_mut(self, expr);
|
||||
}
|
||||
|
||||
fn visit_expr_struct_mut(&mut self, expr: &mut ExprStruct) {
|
||||
self.self_to_expr_path(&mut expr.path);
|
||||
visit_mut::visit_expr_struct_mut(self, expr);
|
||||
}
|
||||
|
||||
fn visit_pat_path_mut(&mut self, pat: &mut PatPath) {
|
||||
if pat.qself.is_none() {
|
||||
self.self_to_qself_expr(&mut pat.qself, &mut pat.path);
|
||||
}
|
||||
visit_mut::visit_pat_path_mut(self, pat);
|
||||
}
|
||||
|
||||
fn visit_pat_struct_mut(&mut self, pat: &mut PatStruct) {
|
||||
self.self_to_expr_path(&mut pat.path);
|
||||
visit_mut::visit_pat_struct_mut(self, pat);
|
||||
}
|
||||
|
||||
fn visit_pat_tuple_struct_mut(&mut self, pat: &mut PatTupleStruct) {
|
||||
self.self_to_expr_path(&mut pat.path);
|
||||
visit_mut::visit_pat_tuple_struct_mut(self, pat);
|
||||
}
|
||||
|
||||
fn visit_item_mut(&mut self, i: &mut Item) {
|
||||
match i {
|
||||
// Visit `macro_rules!` because locally defined macros can refer to `self`.
|
||||
Item::Macro(i) if i.mac.path.is_ident("macro_rules") => {
|
||||
self.visit_macro_mut(&mut i.mac)
|
||||
}
|
||||
// Otherwise, do not recurse into nested items.
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_macro_mut(&mut self, mac: &mut Macro) {
|
||||
// We can't tell in general whether `self` inside a macro invocation
|
||||
// refers to the self in the argument list or a different self
|
||||
// introduced within the macro. Heuristic: if the macro input contains
|
||||
// `fn`, then `self` is more likely to refer to something other than the
|
||||
// outer function's self argument.
|
||||
if !contains_fn(mac.tokens.clone()) {
|
||||
self.visit_token_stream(&mut mac.tokens);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn contains_fn(tokens: TokenStream) -> bool {
|
||||
tokens.into_iter().any(|tt| match tt {
|
||||
TokenTree::Ident(ident) => ident == "fn",
|
||||
TokenTree::Group(group) => contains_fn(group.stream()),
|
||||
_ => false,
|
||||
})
|
||||
}
|
||||
|
||||
fn prepend_underscore_to_self(ident: &mut Ident) -> bool {
|
||||
let modified = ident == "self";
|
||||
if modified {
|
||||
*ident = Ident::new("_self", ident.span());
|
||||
}
|
||||
modified
|
||||
}
|
22
third_party/rust/async-trait/src/respan.rs
vendored
Normal file
22
third_party/rust/async-trait/src/respan.rs
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
use proc_macro2::{Span, TokenStream};
|
||||
use quote::ToTokens;
|
||||
use syn::parse::Parse;
|
||||
|
||||
pub(crate) fn respan<T>(node: &T, span: Span) -> T
|
||||
where
|
||||
T: ToTokens + Parse,
|
||||
{
|
||||
let tokens = node.to_token_stream();
|
||||
let respanned = respan_tokens(tokens, span);
|
||||
syn::parse2(respanned).unwrap()
|
||||
}
|
||||
|
||||
fn respan_tokens(tokens: TokenStream, span: Span) -> TokenStream {
|
||||
tokens
|
||||
.into_iter()
|
||||
.map(|mut token| {
|
||||
token.set_span(span);
|
||||
token
|
||||
})
|
||||
.collect()
|
||||
}
|
6
third_party/rust/async-trait/tests/compiletest.rs
vendored
Normal file
6
third_party/rust/async-trait/tests/compiletest.rs
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
#[rustversion::attr(not(nightly), ignore)]
|
||||
#[test]
|
||||
fn ui() {
|
||||
let t = trybuild::TestCases::new();
|
||||
t.compile_fail("tests/ui/*.rs");
|
||||
}
|
35
third_party/rust/async-trait/tests/executor/mod.rs
vendored
Normal file
35
third_party/rust/async-trait/tests/executor/mod.rs
vendored
Normal file
@ -0,0 +1,35 @@
|
||||
use std::future::Future;
|
||||
use std::pin::Pin;
|
||||
use std::ptr;
|
||||
use std::task::{Context, Poll, RawWaker, RawWakerVTable, Waker};
|
||||
|
||||
// Executor for a future that resolves immediately (test only).
|
||||
pub fn block_on_simple<F: Future>(mut fut: F) -> F::Output {
|
||||
unsafe fn clone(_null: *const ()) -> RawWaker {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
unsafe fn wake(_null: *const ()) {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
unsafe fn wake_by_ref(_null: *const ()) {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
unsafe fn drop(_null: *const ()) {}
|
||||
|
||||
let data = ptr::null();
|
||||
let vtable = &RawWakerVTable::new(clone, wake, wake_by_ref, drop);
|
||||
let raw_waker = RawWaker::new(data, vtable);
|
||||
let waker = unsafe { Waker::from_raw(raw_waker) };
|
||||
let mut cx = Context::from_waker(&waker);
|
||||
|
||||
// fut does not move until it gets dropped.
|
||||
let fut = unsafe { Pin::new_unchecked(&mut fut) };
|
||||
|
||||
match fut.poll(&mut cx) {
|
||||
Poll::Ready(output) => output,
|
||||
Poll::Pending => panic!("future did not resolve immediately"),
|
||||
}
|
||||
}
|
1079
third_party/rust/async-trait/tests/test.rs
vendored
Normal file
1079
third_party/rust/async-trait/tests/test.rs
vendored
Normal file
File diff suppressed because it is too large
Load Diff
15
third_party/rust/async-trait/tests/ui/bare-trait-object.rs
vendored
Normal file
15
third_party/rust/async-trait/tests/ui/bare-trait-object.rs
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
#![deny(bare_trait_objects)]
|
||||
|
||||
use async_trait::async_trait;
|
||||
|
||||
#[async_trait]
|
||||
trait Trait {
|
||||
async fn f(&self);
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl Trait for Send + Sync {
|
||||
async fn f(&self) {}
|
||||
}
|
||||
|
||||
fn main() {}
|
11
third_party/rust/async-trait/tests/ui/bare-trait-object.stderr
vendored
Normal file
11
third_party/rust/async-trait/tests/ui/bare-trait-object.stderr
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
error: trait objects without an explicit `dyn` are deprecated
|
||||
--> $DIR/bare-trait-object.rs:11:16
|
||||
|
|
||||
11 | impl Trait for Send + Sync {
|
||||
| ^^^^^^^^^^^ help: use `dyn`: `dyn Send + Sync`
|
||||
|
|
||||
note: the lint level is defined here
|
||||
--> $DIR/bare-trait-object.rs:1:9
|
||||
|
|
||||
1 | #![deny(bare_trait_objects)]
|
||||
| ^^^^^^^^^^^^^^^^^^
|
21
third_party/rust/async-trait/tests/ui/delimiter-span.rs
vendored
Normal file
21
third_party/rust/async-trait/tests/ui/delimiter-span.rs
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
use async_trait::async_trait;
|
||||
|
||||
macro_rules! picky {
|
||||
(ident) => {};
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
trait Trait {
|
||||
async fn method();
|
||||
}
|
||||
|
||||
struct Struct;
|
||||
|
||||
#[async_trait]
|
||||
impl Trait for Struct {
|
||||
async fn method() {
|
||||
picky!({ 123 });
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
8
third_party/rust/async-trait/tests/ui/delimiter-span.stderr
vendored
Normal file
8
third_party/rust/async-trait/tests/ui/delimiter-span.stderr
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
error: no rules expected the token `{`
|
||||
--> $DIR/delimiter-span.rs:17:16
|
||||
|
|
||||
3 | macro_rules! picky {
|
||||
| ------------------ when calling this macro
|
||||
...
|
||||
17 | picky!({ 123 });
|
||||
| ^ no rules expected this token in macro call
|
15
third_party/rust/async-trait/tests/ui/missing-body.rs
vendored
Normal file
15
third_party/rust/async-trait/tests/ui/missing-body.rs
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
use async_trait::async_trait;
|
||||
|
||||
#[async_trait]
|
||||
trait Trait {
|
||||
async fn f(&self);
|
||||
}
|
||||
|
||||
struct Thing;
|
||||
|
||||
#[async_trait]
|
||||
impl Trait for Thing {
|
||||
async fn f(&self);
|
||||
}
|
||||
|
||||
fn main() {}
|
7
third_party/rust/async-trait/tests/ui/missing-body.stderr
vendored
Normal file
7
third_party/rust/async-trait/tests/ui/missing-body.stderr
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
error: associated function in `impl` without body
|
||||
--> $DIR/missing-body.rs:12:5
|
||||
|
|
||||
12 | async fn f(&self);
|
||||
| ^^^^^^^^^^^^^^^^^-
|
||||
| |
|
||||
| help: provide a definition for the function: `{ <body> }`
|
21
third_party/rust/async-trait/tests/ui/must-use.rs
vendored
Normal file
21
third_party/rust/async-trait/tests/ui/must-use.rs
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
#![deny(unused_must_use)]
|
||||
|
||||
use async_trait::async_trait;
|
||||
|
||||
#[async_trait]
|
||||
trait Interface {
|
||||
async fn f(&self);
|
||||
}
|
||||
|
||||
struct Thing;
|
||||
|
||||
#[async_trait]
|
||||
impl Interface for Thing {
|
||||
async fn f(&self) {}
|
||||
}
|
||||
|
||||
pub async fn f() {
|
||||
Thing.f();
|
||||
}
|
||||
|
||||
fn main() {}
|
11
third_party/rust/async-trait/tests/ui/must-use.stderr
vendored
Normal file
11
third_party/rust/async-trait/tests/ui/must-use.stderr
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
error: unused return value of `Interface::f` that must be used
|
||||
--> $DIR/must-use.rs:18:5
|
||||
|
|
||||
18 | Thing.f();
|
||||
| ^^^^^^^^^^
|
||||
|
|
||||
note: the lint level is defined here
|
||||
--> $DIR/must-use.rs:1:9
|
||||
|
|
||||
1 | #![deny(unused_must_use)]
|
||||
| ^^^^^^^^^^^^^^^
|
30
third_party/rust/async-trait/tests/ui/self-span.rs
vendored
Normal file
30
third_party/rust/async-trait/tests/ui/self-span.rs
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
use async_trait::async_trait;
|
||||
|
||||
pub struct S {}
|
||||
|
||||
pub enum E {
|
||||
V {},
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
pub trait Trait {
|
||||
async fn method(self);
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl Trait for S {
|
||||
async fn method(self) {
|
||||
let _: () = self;
|
||||
let _: Self = Self;
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl Trait for E {
|
||||
async fn method(self) {
|
||||
let _: () = self;
|
||||
let _: Self = Self::V;
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
30
third_party/rust/async-trait/tests/ui/self-span.stderr
vendored
Normal file
30
third_party/rust/async-trait/tests/ui/self-span.stderr
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
error[E0423]: expected value, found struct `S`
|
||||
--> $DIR/self-span.rs:18:23
|
||||
|
|
||||
3 | pub struct S {}
|
||||
| --------------- `S` defined here
|
||||
...
|
||||
18 | let _: Self = Self;
|
||||
| ^^^^ help: use struct literal syntax instead: `S {}`
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/self-span.rs:17:21
|
||||
|
|
||||
17 | let _: () = self;
|
||||
| -- ^^^^ expected `()`, found struct `S`
|
||||
| |
|
||||
| expected due to this
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/self-span.rs:25:21
|
||||
|
|
||||
25 | let _: () = self;
|
||||
| -- ^^^^ expected `()`, found enum `E`
|
||||
| |
|
||||
| expected due to this
|
||||
|
||||
error[E0533]: expected unit struct, unit variant or constant, found struct variant `Self::V`
|
||||
--> $DIR/self-span.rs:26:23
|
||||
|
|
||||
26 | let _: Self = Self::V;
|
||||
| ^^^^^^^
|
15
third_party/rust/async-trait/tests/ui/send-not-implemented.rs
vendored
Normal file
15
third_party/rust/async-trait/tests/ui/send-not-implemented.rs
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
use async_trait::async_trait;
|
||||
use std::sync::Mutex;
|
||||
|
||||
async fn f() {}
|
||||
|
||||
#[async_trait]
|
||||
trait Test {
|
||||
async fn test(&self) {
|
||||
let mutex = Mutex::new(());
|
||||
let _guard = mutex.lock().unwrap();
|
||||
f().await;
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
22
third_party/rust/async-trait/tests/ui/send-not-implemented.stderr
vendored
Normal file
22
third_party/rust/async-trait/tests/ui/send-not-implemented.stderr
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
error: future cannot be sent between threads safely
|
||||
--> $DIR/send-not-implemented.rs:8:26
|
||||
|
|
||||
8 | async fn test(&self) {
|
||||
| __________________________^
|
||||
9 | | let mutex = Mutex::new(());
|
||||
10 | | let _guard = mutex.lock().unwrap();
|
||||
11 | | f().await;
|
||||
12 | | }
|
||||
| |_____^ future returned by `__test` is not `Send`
|
||||
|
|
||||
= help: within `impl Future`, the trait `Send` is not implemented for `MutexGuard<'_, ()>`
|
||||
note: future is not `Send` as this value is used across an await
|
||||
--> $DIR/send-not-implemented.rs:11:9
|
||||
|
|
||||
10 | let _guard = mutex.lock().unwrap();
|
||||
| ------ has type `MutexGuard<'_, ()>` which is not `Send`
|
||||
11 | f().await;
|
||||
| ^^^^^^^^^ await occurs here, with `_guard` maybe used later
|
||||
12 | }
|
||||
| - `_guard` is later dropped here
|
||||
= note: required for the cast to the object type `dyn Future<Output = ()> + Send`
|
15
third_party/rust/async-trait/tests/ui/unsupported-self.rs
vendored
Normal file
15
third_party/rust/async-trait/tests/ui/unsupported-self.rs
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
use async_trait::async_trait;
|
||||
|
||||
#[async_trait]
|
||||
pub trait Trait {
|
||||
async fn method();
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl Trait for &'static str {
|
||||
async fn method() {
|
||||
let _ = Self;
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
5
third_party/rust/async-trait/tests/ui/unsupported-self.stderr
vendored
Normal file
5
third_party/rust/async-trait/tests/ui/unsupported-self.stderr
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
error: Self type of this impl is unsupported in expression position
|
||||
--> $DIR/unsupported-self.rs:11:17
|
||||
|
|
||||
11 | let _ = Self;
|
||||
| ^^^^
|
1
third_party/rust/chunky-vec/.cargo-checksum.json
vendored
Normal file
1
third_party/rust/chunky-vec/.cargo-checksum.json
vendored
Normal file
@ -0,0 +1 @@
|
||||
{"files":{"Cargo.toml":"3ab88cdacffa2756abe4460dda1ef403b304e79f814a2ec71b9c9a013dce2bf6","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"ea4a2543160e8e33d4e2d2b40339c90e0ab2960cfa59a65264b66782d7f9e35f","README.md":"382da770bccf4ebe07dded523a9ab5e4daead902edbbbffd20740404e73ee3d5","src/lib.rs":"01dc7e2a981fc95825584916d320fd60232b07fc88761126c89b12da3e3f20e5"},"package":"bb7bdea464ae038f09197b82430b921c53619fc8d2bcaf7b151013b3ca008017"}
|
24
third_party/rust/chunky-vec/Cargo.toml
vendored
Normal file
24
third_party/rust/chunky-vec/Cargo.toml
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
# 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 believe there's an error in this file please file an
|
||||
# issue against the rust-lang/cargo repository. If you're
|
||||
# editing this file be aware that the upstream Cargo.toml
|
||||
# will likely look very different (and much more reasonable)
|
||||
|
||||
[package]
|
||||
edition = "2018"
|
||||
name = "chunky-vec"
|
||||
version = "0.1.0"
|
||||
authors = ["Dan Glastonbury <dan.glastonbury@gmail.com>"]
|
||||
description = "A pin safe, append only vector never moves the backing store for an element.\n"
|
||||
keywords = ["data-structure", "vector", "pin"]
|
||||
categories = ["data-structures"]
|
||||
license = "Apache-2.0/MIT"
|
||||
repository = "https://github.com/djg/chunky-vec"
|
||||
|
||||
[dependencies]
|
201
third_party/rust/chunky-vec/LICENSE-APACHE
vendored
Normal file
201
third_party/rust/chunky-vec/LICENSE-APACHE
vendored
Normal 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.
|
27
third_party/rust/chunky-vec/LICENSE-MIT
vendored
Normal file
27
third_party/rust/chunky-vec/LICENSE-MIT
vendored
Normal file
@ -0,0 +1,27 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2020 Daniel Glastonbury
|
||||
|
||||
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.
|
6
third_party/rust/chunky-vec/README.md
vendored
Normal file
6
third_party/rust/chunky-vec/README.md
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
# Chunky Vec
|
||||
|
||||
[![License: MIT/Apache-2.0](https://img.shields.io/crates/l/chunky-vec.svg)](#license)
|
||||
|
||||
This crate provides a pin-safe, append-only vector which guarantees never
|
||||
to move the storage for an element once it has been allocated.
|
343
third_party/rust/chunky-vec/src/lib.rs
vendored
Normal file
343
third_party/rust/chunky-vec/src/lib.rs
vendored
Normal file
@ -0,0 +1,343 @@
|
||||
//! This crate provides a pin-safe, append-only vector which guarantees never
|
||||
//! to move the storage for an element once it has been allocated.
|
||||
|
||||
use std::{
|
||||
ops::{Index, IndexMut},
|
||||
slice,
|
||||
};
|
||||
|
||||
struct Chunk<T> {
|
||||
/// The elements of this chunk.
|
||||
elements: Vec<T>,
|
||||
}
|
||||
|
||||
impl<T> Chunk<T> {
|
||||
fn with_capacity(capacity: usize) -> Self {
|
||||
let elements = Vec::with_capacity(capacity);
|
||||
assert_eq!(elements.capacity(), capacity);
|
||||
Self { elements }
|
||||
}
|
||||
|
||||
fn len(&self) -> usize {
|
||||
self.elements.len()
|
||||
}
|
||||
|
||||
/// Returns the number of available empty elements.
|
||||
fn available(&self) -> usize {
|
||||
self.elements.capacity() - self.elements.len()
|
||||
}
|
||||
|
||||
/// Returns a shared reference to the element at the given index.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the index is out of bounds.
|
||||
pub fn get(&self, index: usize) -> Option<&T> {
|
||||
self.elements.get(index)
|
||||
}
|
||||
|
||||
/// Returns an exclusive reference to the element at the given index.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the index is out of bounds.
|
||||
pub fn get_mut(&mut self, index: usize) -> Option<&mut T> {
|
||||
self.elements.get_mut(index)
|
||||
}
|
||||
|
||||
/// Pushes a new value into the fixed capacity entry.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// If the entry is already at its capacity.
|
||||
/// Note that this panic should never happen since the entry is only ever
|
||||
/// accessed by its outer chunk vector that checks before pushing.
|
||||
pub fn push(&mut self, new_value: T) {
|
||||
if self.available() == 0 {
|
||||
panic!("No available elements.")
|
||||
}
|
||||
self.elements.push(new_value);
|
||||
}
|
||||
|
||||
pub fn push_get(&mut self, new_value: T) -> &mut T {
|
||||
self.push(new_value);
|
||||
unsafe {
|
||||
let last = self.elements.len() - 1;
|
||||
self.elements.get_unchecked_mut(last)
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns an iterator over the elements of the chunk.
|
||||
pub fn iter(&self) -> slice::Iter<T> {
|
||||
self.elements.iter()
|
||||
}
|
||||
|
||||
/// Returns an iterator over the elements of the chunk.
|
||||
pub fn iter_mut(&mut self) -> slice::IterMut<T> {
|
||||
self.elements.iter_mut()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Index<usize> for Chunk<T> {
|
||||
type Output = T;
|
||||
|
||||
fn index(&self, index: usize) -> &Self::Output {
|
||||
self.get(index).expect("index out of bounds")
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> IndexMut<usize> for Chunk<T> {
|
||||
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
|
||||
self.get_mut(index).expect("index out of bounds")
|
||||
}
|
||||
}
|
||||
|
||||
/// Pin safe vector
|
||||
///
|
||||
/// An append only vector that never moves the backing store for each element.
|
||||
pub struct ChunkyVec<T> {
|
||||
/// The chunks holding elements
|
||||
chunks: Vec<Chunk<T>>,
|
||||
}
|
||||
|
||||
impl<T> Default for ChunkyVec<T> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
chunks: Vec::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Unpin for ChunkyVec<T> {}
|
||||
|
||||
impl<T> ChunkyVec<T> {
|
||||
const DEFAULT_CAPACITY: usize = 32;
|
||||
|
||||
pub fn len(&self) -> usize {
|
||||
if self.chunks.is_empty() {
|
||||
0
|
||||
} else {
|
||||
// # Safety - There is at least one chunk here.
|
||||
(self.chunks.len() - 1) * Self::DEFAULT_CAPACITY + self.chunks.last().unwrap().len()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
// # Safety - Since it's impossible to pop, at least one chunk means we're not empty.
|
||||
self.chunks.is_empty()
|
||||
}
|
||||
|
||||
/// Returns an iterator that yields shared references to the elements of the bucket vector.
|
||||
pub fn iter(&self) -> Iter<T> {
|
||||
Iter::new(self)
|
||||
}
|
||||
|
||||
/// Returns an iterator that yields exclusive reference to the elements of the bucket vector.
|
||||
pub fn iter_mut(&mut self) -> IterMut<T> {
|
||||
IterMut::new(self)
|
||||
}
|
||||
|
||||
/// Returns a shared reference to the element at the given index if any.
|
||||
pub fn get(&self, index: usize) -> Option<&T> {
|
||||
let (x, y) = (
|
||||
index / Self::DEFAULT_CAPACITY,
|
||||
index % Self::DEFAULT_CAPACITY,
|
||||
);
|
||||
self.chunks.get(x).and_then(|chunk| chunk.get(y))
|
||||
}
|
||||
|
||||
/// Returns an exclusive reference to the element at the given index if any.
|
||||
pub fn get_mut(&mut self, index: usize) -> Option<&mut T> {
|
||||
let (x, y) = (
|
||||
index / Self::DEFAULT_CAPACITY,
|
||||
index % Self::DEFAULT_CAPACITY,
|
||||
);
|
||||
self.chunks.get_mut(x).and_then(|chunk| chunk.get_mut(y))
|
||||
}
|
||||
|
||||
/// Pushes a new element onto the bucket vector.
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
/// This operation will never move other elements, reallocates or otherwise
|
||||
/// invalidate pointers of elements contained by the bucket vector.
|
||||
pub fn push(&mut self, new_value: T) {
|
||||
self.push_get(new_value);
|
||||
}
|
||||
|
||||
pub fn push_get(&mut self, new_value: T) -> &mut T {
|
||||
if self.chunks.last().map(Chunk::available).unwrap_or_default() == 0 {
|
||||
self.chunks.push(Chunk::with_capacity(Self::DEFAULT_CAPACITY));
|
||||
}
|
||||
// Safety: Guaranteed to have a chunk with available elements
|
||||
unsafe {
|
||||
let last = self.chunks.len() - 1;
|
||||
self.chunks.get_unchecked_mut(last).push_get(new_value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Index<usize> for ChunkyVec<T> {
|
||||
type Output = T;
|
||||
|
||||
fn index(&self, index: usize) -> &Self::Output {
|
||||
self.get(index).expect("index out of bounds")
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> IndexMut<usize> for ChunkyVec<T> {
|
||||
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
|
||||
self.get_mut(index).expect("index out of bounds")
|
||||
}
|
||||
}
|
||||
|
||||
/// An iterator yielding shared references to the elements of a ChunkyVec.
|
||||
#[derive(Clone)]
|
||||
pub struct Iter<'a, T> {
|
||||
/// Chunks iterator.
|
||||
chunks: slice::Iter<'a, Chunk<T>>,
|
||||
/// Forward iterator for `next`.
|
||||
iter: Option<slice::Iter<'a, T>>,
|
||||
/// Number of elements that are to be yielded by the iterator.
|
||||
len: usize,
|
||||
}
|
||||
|
||||
impl<'a, T> Iter<'a, T> {
|
||||
/// Creates a new iterator over the ChunkyVec
|
||||
pub(crate) fn new(vec: &'a ChunkyVec<T>) -> Self {
|
||||
let len = vec.len();
|
||||
Self {
|
||||
chunks: vec.chunks.iter(),
|
||||
iter: None,
|
||||
len,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> Iterator for Iter<'a, T> {
|
||||
type Item = &'a T;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
loop {
|
||||
if let Some(ref mut iter) = self.iter {
|
||||
if let front @ Some(_) = iter.next() {
|
||||
self.len -= 1;
|
||||
return front;
|
||||
}
|
||||
}
|
||||
self.iter = Some(self.chunks.next()?.iter());
|
||||
}
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
(self.len(), Some(self.len()))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> ExactSizeIterator for Iter<'a, T> {
|
||||
fn len(&self) -> usize {
|
||||
self.len
|
||||
}
|
||||
}
|
||||
|
||||
/// An iterator yielding exclusive references to the elements of a ChunkVec.
|
||||
pub struct IterMut<'a, T> {
|
||||
/// Chunks iterator.
|
||||
chunks: slice::IterMut<'a, Chunk<T>>,
|
||||
/// Forward iterator for `next`.
|
||||
iter: Option<slice::IterMut<'a, T>>,
|
||||
/// Number of elements that are to be yielded by the iterator.
|
||||
len: usize,
|
||||
}
|
||||
|
||||
impl<'a, T> IterMut<'a, T> {
|
||||
/// Creates a new iterator over the bucket vector.
|
||||
pub(crate) fn new(vec: &'a mut ChunkyVec<T>) -> Self {
|
||||
let len = vec.len();
|
||||
Self {
|
||||
chunks: vec.chunks.iter_mut(),
|
||||
iter: None,
|
||||
len,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> Iterator for IterMut<'a, T> {
|
||||
type Item = &'a mut T;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
loop {
|
||||
if let Some(ref mut iter) = self.iter {
|
||||
if let front @ Some(_) = iter.next() {
|
||||
self.len -= 1;
|
||||
return front;
|
||||
}
|
||||
}
|
||||
self.iter = Some(self.chunks.next()?.iter_mut());
|
||||
}
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
(self.len(), Some(self.len()))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> ExactSizeIterator for IterMut<'a, T> {
|
||||
fn len(&self) -> usize {
|
||||
self.len
|
||||
}
|
||||
}
|
||||
impl<'a, T> IntoIterator for &'a ChunkyVec<T> {
|
||||
type Item = &'a T;
|
||||
type IntoIter = Iter<'a, T>;
|
||||
|
||||
fn into_iter(self) -> Iter<'a, T> {
|
||||
self.iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> IntoIterator for &'a mut ChunkyVec<T> {
|
||||
type Item = &'a mut T;
|
||||
type IntoIter = IterMut<'a, T>;
|
||||
|
||||
fn into_iter(self) -> IterMut<'a, T> {
|
||||
self.iter_mut()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn iterate_empty() {
|
||||
let v = ChunkyVec::<usize>::default();
|
||||
for i in &v {
|
||||
println!("{:?}", i);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn iterate_multiple_chunks() {
|
||||
let mut v = ChunkyVec::<usize>::default();
|
||||
for i in 0..33 {
|
||||
v.push(i);
|
||||
}
|
||||
let mut iter = v.iter();
|
||||
for _ in 0..32 {
|
||||
iter.next();
|
||||
}
|
||||
assert_eq!(iter.next(), Some(&32));
|
||||
assert_eq!(iter.next(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn index_multiple_chunks() {
|
||||
let mut v = ChunkyVec::<usize>::default();
|
||||
for i in 0..33 {
|
||||
v.push(i);
|
||||
}
|
||||
assert_eq!(v.get(32), Some(&32));
|
||||
assert_eq!(v[32], 32);
|
||||
}
|
||||
}
|
@ -1 +1 @@
|
||||
{"files":{"Cargo.toml":"a12d7a4455cac14350f4b9823c67d23d6813ea6a10e8642f1b9f01711bf45b70","LICENSE-APACHE":"5db2b182453ff32ed40f7da63589c9667a3f8bd8b16b1471b152caae56f77e45","LICENSE-MIT":"49c0b000c03731d9e3970dc059ad4ca345d773681f4a612b0024435b663e0220","README.md":"d74aad87a35e7d0a87ad5a2fc523f437ea6ceb419290cdb55f55257bcbfc0464","benches/resolver.rs":"bd46c8b710ac1898a0e69324a7ecf9aa38d577337bc5855a07ca0ad1043603a1","benches/resolver_iai.rs":"e9e940f4c09908069d474d379a0230dfc6fa44325300d72975e8f1d9ef64f6f1","examples/README.md":"99a51f7d388d2da3c5291e68de5264feaf642ba9a22f6f882c3b940c1b6429b2","src/args.rs":"51346e9ec84f2eeb4462e0e993b1bbb307585a2a40e41f6d0d745889bca56a7d","src/bundle.rs":"3da63b685acf559ee80fa489885da126f7c68405026ac065a07e559e2186a77f","src/concurrent.rs":"be77275513918809b98c554b26a65c6a9cf2a7bf52db3bbaf21ebdd34d94c651","src/entry.rs":"e1507b0e4c3e6d0d2efc5d622f4156a5156b9eeb40d9c5353cb7fdc236c38189","src/errors.rs":"a357f3a09335d31e362aa99a8d82eab4e238fdec8498141990f61ede58f4dabb","src/lib.rs":"42ac33b188b28159b3c683b8af8f739f063b3a372eea22b3022a899b0360e69a","src/memoizer.rs":"922084f71f02d0532056db9b41cec4c1434001fe60215ee6f6ac8e3fd2518f12","src/message.rs":"4a3c95d3ecd016aeaa5da07e99d78de62f13aac8aa447818aabd0f63f2d143c4","src/resolver/errors.rs":"beaf41fabbfd11211cb2c3db6ca0ba26bccf75817bed05a92b980393edfb3f9f","src/resolver/expression.rs":"f18413de1a6b3ba43c062e24d58a60d63f4dad66bcec67ed55756ff5014f9347","src/resolver/inline_expression.rs":"089ad6745d0790478ea698fd530f2236c550889f9be75e245ed94bba4b883884","src/resolver/mod.rs":"b367ed2a9bbb835c145387f13bec62eb9807c8ff41d73d6a945edec0cc760f3d","src/resolver/pattern.rs":"85542a4f161b161ef85da539c8710b9a9082b6c5279f3cc3867786ad31de6d35","src/resolver/scope.rs":"816f51146c38affea54c6e0911e3522f998485829e619cca5f72cec05180de59","src/resource.rs":"40f81ae728c427343f972bfc1ce96924ac977fa7082904d56cbad0c482a09ce1","src/types/mod.rs":"1cd65301fb32233fd241a79c6fa873edcb40a271330601360f72e2c452900509","src/types/number.rs":"2d3b403e5f545e2f4a3a16aec0bb019a4cc8e5ac0ab2db5642ba8039fbe203a8","src/types/plural.rs":"f28834e71d6970d5eb48089132f5242433b1e62b90765d85e3c76f805eecc92e"},"package":"5b589dfaa7e69ddf497be48cd0d184d7ff6e2cbb8186d1bb01c26d5cf5449a17"}
|
||||
{"files":{"Cargo.toml":"98dcbe4ffe6955c65496f4a528d4aa34d6672c7d920ee393f89a32aababaae9a","LICENSE-APACHE":"5db2b182453ff32ed40f7da63589c9667a3f8bd8b16b1471b152caae56f77e45","LICENSE-MIT":"49c0b000c03731d9e3970dc059ad4ca345d773681f4a612b0024435b663e0220","README.md":"d74aad87a35e7d0a87ad5a2fc523f437ea6ceb419290cdb55f55257bcbfc0464","benches/resolver.rs":"bd46c8b710ac1898a0e69324a7ecf9aa38d577337bc5855a07ca0ad1043603a1","benches/resolver_iai.rs":"e9e940f4c09908069d474d379a0230dfc6fa44325300d72975e8f1d9ef64f6f1","examples/README.md":"99a51f7d388d2da3c5291e68de5264feaf642ba9a22f6f882c3b940c1b6429b2","src/args.rs":"51346e9ec84f2eeb4462e0e993b1bbb307585a2a40e41f6d0d745889bca56a7d","src/bundle.rs":"3da63b685acf559ee80fa489885da126f7c68405026ac065a07e559e2186a77f","src/concurrent.rs":"be77275513918809b98c554b26a65c6a9cf2a7bf52db3bbaf21ebdd34d94c651","src/entry.rs":"e1507b0e4c3e6d0d2efc5d622f4156a5156b9eeb40d9c5353cb7fdc236c38189","src/errors.rs":"a357f3a09335d31e362aa99a8d82eab4e238fdec8498141990f61ede58f4dabb","src/lib.rs":"58a0c929322f83aac41280da035b50adbcdb05d8a8376359d58c177cd9755eab","src/memoizer.rs":"922084f71f02d0532056db9b41cec4c1434001fe60215ee6f6ac8e3fd2518f12","src/message.rs":"4a3c95d3ecd016aeaa5da07e99d78de62f13aac8aa447818aabd0f63f2d143c4","src/resolver/errors.rs":"beaf41fabbfd11211cb2c3db6ca0ba26bccf75817bed05a92b980393edfb3f9f","src/resolver/expression.rs":"f18413de1a6b3ba43c062e24d58a60d63f4dad66bcec67ed55756ff5014f9347","src/resolver/inline_expression.rs":"089ad6745d0790478ea698fd530f2236c550889f9be75e245ed94bba4b883884","src/resolver/mod.rs":"d1b15ce110ea49876909412c12c4c1841052bb80f4838a934dfccd6a5264855c","src/resolver/pattern.rs":"64162a7e2ad0df82d463d14ac6a472005bba4cef4a7e73fe2a9529e811124a85","src/resolver/scope.rs":"816f51146c38affea54c6e0911e3522f998485829e619cca5f72cec05180de59","src/resource.rs":"40f81ae728c427343f972bfc1ce96924ac977fa7082904d56cbad0c482a09ce1","src/types/mod.rs":"1cd65301fb32233fd241a79c6fa873edcb40a271330601360f72e2c452900509","src/types/number.rs":"2d3b403e5f545e2f4a3a16aec0bb019a4cc8e5ac0ab2db5642ba8039fbe203a8","src/types/plural.rs":"f28834e71d6970d5eb48089132f5242433b1e62b90765d85e3c76f805eecc92e"},"package":"8acf044eeb4872d9dbf2667541fbf461f5965c57e343878ad0fb24b5793fa007"}
|
4
third_party/rust/fluent-bundle/Cargo.toml
vendored
4
third_party/rust/fluent-bundle/Cargo.toml
vendored
@ -13,7 +13,7 @@
|
||||
[package]
|
||||
edition = "2018"
|
||||
name = "fluent-bundle"
|
||||
version = "0.15.0"
|
||||
version = "0.15.1"
|
||||
authors = ["Zibi Braniecki <gandalf@mozilla.com>", "Staś Małolepszy <stas@mozilla.com>"]
|
||||
include = ["src/**/*", "benches/*.rs", "Cargo.toml", "README.md", "LICENSE-APACHE", "LICENSE-MIT"]
|
||||
description = "A localization system designed to unleash the entire expressive power of\nnatural language translations.\n"
|
||||
@ -44,7 +44,7 @@ version = "0.5"
|
||||
version = "7.0.1"
|
||||
|
||||
[dependencies.ouroboros]
|
||||
version = "0.8"
|
||||
version = "0.9"
|
||||
|
||||
[dependencies.rustc-hash]
|
||||
version = "1"
|
||||
|
2
third_party/rust/fluent-bundle/src/lib.rs
vendored
2
third_party/rust/fluent-bundle/src/lib.rs
vendored
@ -1,6 +1,6 @@
|
||||
//! Fluent is a modern localization system designed to improve how software is translated.
|
||||
//!
|
||||
//! `fluent-bundle` is the mid-level component of the [Fluent Localization
|
||||
//! `fluent-bundle` is a mid-level component of the [Fluent Localization
|
||||
//! System](https://www.projectfluent.org).
|
||||
//!
|
||||
//! The crate builds on top of the low level [`fluent-syntax`](../fluent-syntax) package, and provides
|
||||
|
@ -1,4 +1,4 @@
|
||||
mod errors;
|
||||
pub mod errors;
|
||||
mod expression;
|
||||
mod inline_expression;
|
||||
mod pattern;
|
||||
|
@ -49,15 +49,16 @@ impl<'p> WriteValue for ast::Pattern<&'p str> {
|
||||
|
||||
let needs_isolation = scope.bundle.use_isolating
|
||||
&& len > 1
|
||||
&& !matches!(expression, ast::Expression::Inline(
|
||||
ast::InlineExpression::MessageReference { .. },
|
||||
)
|
||||
| ast::Expression::Inline(
|
||||
ast::InlineExpression::TermReference { .. },
|
||||
)
|
||||
| ast::Expression::Inline(
|
||||
ast::InlineExpression::StringLiteral { .. },
|
||||
));
|
||||
&& !matches!(
|
||||
expression,
|
||||
ast::Expression::Inline(ast::InlineExpression::MessageReference { .. },)
|
||||
| ast::Expression::Inline(
|
||||
ast::InlineExpression::TermReference { .. },
|
||||
)
|
||||
| ast::Expression::Inline(
|
||||
ast::InlineExpression::StringLiteral { .. },
|
||||
)
|
||||
);
|
||||
if needs_isolation {
|
||||
w.write_char('\u{2068}')?;
|
||||
}
|
||||
|
1
third_party/rust/fluent-fallback/.cargo-checksum.json
vendored
Normal file
1
third_party/rust/fluent-fallback/.cargo-checksum.json
vendored
Normal file
@ -0,0 +1 @@
|
||||
{"files":{"CHANGELOG.md":"a878b5f0c0102bfeba0991554ff71b869ac8eb39c2fafe96c3fdeb6b903b9f9b","Cargo.lock":"7517e395531cd8bc810ffbe1a97d45d78d65e25611fef6a9314cfc279da0a82d","Cargo.toml":"a9da4fd94ea03886127fdd5fffdeaf7ae5107ac287a9d5eeaafc38e2a374baeb","LICENSE-APACHE":"5db2b182453ff32ed40f7da63589c9667a3f8bd8b16b1471b152caae56f77e45","LICENSE-MIT":"49c0b000c03731d9e3970dc059ad4ca345d773681f4a612b0024435b663e0220","README.md":"0e3c37b7cd14a1c672125f58a6dbc5eb43d11c3c67f2ada7f77569e788b9f435","examples/resources/en-US/simple.ftl":"55e8a72973d239c6ed3eb3d9cbc21d37dc90cea9fad85d1d8d73c96d63941629","examples/resources/pl/simple.ftl":"d63d7c62c225897d9f28f166c17e038b8f780dca9e9ee640e81360f23219a212","examples/simple-fallback.rs":"c5033e15c9d0cd9270c57fd498509e17202c8a5c4332e74170184cb2b3e05f85","src/bundles.rs":"f452624b00e7ee01be9b91d702ee28e8900d309597a3340baa8426a72f2697e7","src/cache.rs":"d0e886b95999120baf513d0438491c68cf37e9f99c515cdcf250ffc8f263f439","src/env.rs":"c11b27dcaf76a0f69d31007f8027cbdc6fb1330082afd28cf9f30aeb0352d127","src/errors.rs":"bf1977011093eace65a32fea782776431a3ce718b2454c7c98f5c7364298c240","src/generator.rs":"cf8272de5f97a1efd4a85504363196ebf79826c01dc86056b165d473b7b2b90d","src/lib.rs":"73c1793d76de949a1980424912525ad7f5d4183310d8ce5db3ec8efef2eb2b33","src/localization.rs":"a76bb9c3f89365c88fc6bf18e4f2966d15148b70584d7f5988c76b96018c07fc","src/pin_cell/README.md":"b230e479f0ce5de00ce6638aa47cdf1bd30a934df5f3ad33523c3b9f16ffb02b","src/pin_cell/mod.rs":"7247334eb4c6753babe8aeceacf1b36bdbbd60aed86754da61e09e87f1da24d1","src/pin_cell/pin_mut.rs":"116d0ac2353fbc4d2d1084610f90a9aa414b4607bb01595528025ed49889127c","src/pin_cell/pin_ref.rs":"e67ef14faf7d1d47e082732d3a5446e4ae78a25b9577f0819faa1705b236f01d","src/types.rs":"c4fba75e632b7ec80a40559437e284b61874b6ea29393d3634b9f6eef5f72adb","tests/localization_test.rs":"ba476dd1a7f435e3350a8661a0283ba328912f4efe30288b62afa10cbdca59d8","tests/resources/en-US/test.ftl":"1103dafb98582e728ddcd30b3fa8ffc1b9d4231cc86296f4e2d8865e9d40b25d","tests/resources/en-US/test2.ftl":"821c99ed74f57635e02877fccf6c2ac9e388997e646c850cd0f86cbd3238b490","tests/resources/pl/test.ftl":"5f7f7a9a8ef2c7175c2a9e2d68ff3748667e8ede877bb6b8a72337ac24e5dfeb","tests/resources/pl/test2.ftl":"68550e8e37adfb49c03a95e6b0a6501d58fbfb6e498cda00b52fc258758245b9"},"package":"768feaf8a77beababd5cf3bb1154597b7161eb5a555013a3ac594fe3ed8b18ee"}
|
61
third_party/rust/fluent-fallback/CHANGELOG.md
vendored
Normal file
61
third_party/rust/fluent-fallback/CHANGELOG.md
vendored
Normal file
@ -0,0 +1,61 @@
|
||||
# Changelog
|
||||
|
||||
## Unreleased
|
||||
|
||||
- …
|
||||
|
||||
## fluent-fallback 0.5.0 (Jul 8, 2021)
|
||||
- Separate out `Bundles` for iterator state management.
|
||||
|
||||
## fluent-fallback 0.4.4 (May 3, 2021)
|
||||
- Fix waiting from multiple tasks. (#224)
|
||||
- Bind locale iterator generics of `LocalesProvider` and `BundleGenerator`.
|
||||
|
||||
## fluent-fallback 0.4.3 (April 26, 2021)
|
||||
- Align errors even closer to fluent.js
|
||||
|
||||
## fluent-fallback 0.4.2 (April 9, 2021)
|
||||
- Align errors closer to fluent.js
|
||||
|
||||
## fluent-fallback 0.4.0 (February 9, 2021)
|
||||
- Use `fluent-bundle` 0.15.
|
||||
|
||||
## fluent-fallback 0.3.0 (February 3, 2021)
|
||||
- Handle locale management in `Localization`.
|
||||
|
||||
## fluent-fallback 0.2.2 (January 16, 2021)
|
||||
- Invalidate bundles on resource list change.
|
||||
|
||||
## fluent-fallback 0.2.1 (January 15, 2021)
|
||||
- Add `Localization::is_sync`
|
||||
|
||||
## fluent-fallback 0.2.0 (January 12, 2021)
|
||||
- Separate `Sync` and `Async` bundle generators.
|
||||
- Reorganize fallback logic.
|
||||
- Separate out prefetching trait.
|
||||
- Vendor in pin-cell.
|
||||
|
||||
## fluent-fallback 0.1.0 (January 3, 2021)
|
||||
- Update `fluent-bundle` to 0.14.
|
||||
- Switch from `elsa` to `chunky-vec`.
|
||||
- Add `Localization::with_generator`.
|
||||
- Add support for Streamed bundles.
|
||||
- Add `LocalizationError`.
|
||||
- Make `L10nKey`, `L10nMessage` and `L10nAttribute` types.
|
||||
|
||||
## fluent-fallback 0.0.4 (May 6, 2020)
|
||||
- Update `fluent-bundle` to 0.12.
|
||||
- Update `unic-langid` to 0.9.
|
||||
|
||||
## fluent-fallback 0.0.3 (February 13, 2020)
|
||||
- Update `fluent-bundle` to 0.10.
|
||||
- Update `unic-langid` to 0.8.
|
||||
|
||||
## fluent-fallback 0.0.2 (November 26, 2019)
|
||||
- Update `fluent-bundle` to 0.9.
|
||||
- Update `unic-langid` to 0.7.
|
||||
|
||||
## fluent-fallback 0.0.1 (August 1, 2019)
|
||||
|
||||
- This is the first release to be listed in the CHANGELOG.
|
||||
- Basic support for language fallbacking and runtime locale changes.
|
468
third_party/rust/fluent-fallback/Cargo.lock
generated
vendored
Normal file
468
third_party/rust/fluent-fallback/Cargo.lock
generated
vendored
Normal file
@ -0,0 +1,468 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "Inflector"
|
||||
version = "0.11.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3"
|
||||
|
||||
[[package]]
|
||||
name = "async-trait"
|
||||
version = "0.1.50"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b98e84bbb4cbcdd97da190ba0c58a1bb0de2c1fdf67d159e192ed766aeca722"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
|
||||
|
||||
[[package]]
|
||||
name = "chunky-vec"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bb7bdea464ae038f09197b82430b921c53619fc8d2bcaf7b151013b3ca008017"
|
||||
|
||||
[[package]]
|
||||
name = "fluent-bundle"
|
||||
version = "0.15.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8acf044eeb4872d9dbf2667541fbf461f5965c57e343878ad0fb24b5793fa007"
|
||||
dependencies = [
|
||||
"fluent-langneg",
|
||||
"fluent-syntax",
|
||||
"intl-memoizer",
|
||||
"intl_pluralrules",
|
||||
"ouroboros",
|
||||
"rustc-hash",
|
||||
"smallvec",
|
||||
"unic-langid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fluent-fallback"
|
||||
version = "0.5.0"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"chunky-vec",
|
||||
"fluent-bundle",
|
||||
"fluent-langneg",
|
||||
"futures",
|
||||
"once_cell",
|
||||
"tokio",
|
||||
"unic-langid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fluent-langneg"
|
||||
version = "0.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2c4ad0989667548f06ccd0e306ed56b61bd4d35458d54df5ec7587c0e8ed5e94"
|
||||
dependencies = [
|
||||
"unic-langid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fluent-syntax"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c0abed97648395c902868fee9026de96483933faa54ea3b40d652f7dfe61ca78"
|
||||
dependencies = [
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures"
|
||||
version = "0.3.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0e7e43a803dae2fa37c1f6a8fe121e1f7bf9548b4dfc0522a42f34145dadfc27"
|
||||
dependencies = [
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"futures-executor",
|
||||
"futures-io",
|
||||
"futures-sink",
|
||||
"futures-task",
|
||||
"futures-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-channel"
|
||||
version = "0.3.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e682a68b29a882df0545c143dc3646daefe80ba479bcdede94d5a703de2871e2"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-core"
|
||||
version = "0.3.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0402f765d8a89a26043b889b26ce3c4679d268fa6bb22cd7c6aad98340e179d1"
|
||||
|
||||
[[package]]
|
||||
name = "futures-executor"
|
||||
version = "0.3.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "badaa6a909fac9e7236d0620a2f57f7664640c56575b71a7552fbd68deafab79"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-task",
|
||||
"futures-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-io"
|
||||
version = "0.3.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "acc499defb3b348f8d8f3f66415835a9131856ff7714bf10dadfc4ec4bdb29a1"
|
||||
|
||||
[[package]]
|
||||
name = "futures-macro"
|
||||
version = "0.3.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a4c40298486cdf52cc00cd6d6987892ba502c7656a16a4192a9992b1ccedd121"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"proc-macro-hack",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-sink"
|
||||
version = "0.3.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a57bead0ceff0d6dde8f465ecd96c9338121bb7717d3e7b108059531870c4282"
|
||||
|
||||
[[package]]
|
||||
name = "futures-task"
|
||||
version = "0.3.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a16bef9fc1a4dddb5bee51c989e3fbba26569cbb0e31f5b303c184e3dd33dae"
|
||||
|
||||
[[package]]
|
||||
name = "futures-util"
|
||||
version = "0.3.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "feb5c238d27e2bf94ffdfd27b2c29e3df4a68c4193bb6427384259e2bf191967"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"futures-io",
|
||||
"futures-macro",
|
||||
"futures-sink",
|
||||
"futures-task",
|
||||
"memchr",
|
||||
"pin-project-lite",
|
||||
"pin-utils",
|
||||
"proc-macro-hack",
|
||||
"proc-macro-nested",
|
||||
"slab",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.1.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "intl-memoizer"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c310433e4a310918d6ed9243542a6b83ec1183df95dff8f23f87bb88a264a66f"
|
||||
dependencies = [
|
||||
"type-map",
|
||||
"unic-langid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "intl_pluralrules"
|
||||
version = "7.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b18f988384267d7066cc2be425e6faf352900652c046b6971d2e228d3b1c5ecf"
|
||||
dependencies = [
|
||||
"tinystr",
|
||||
"unic-langid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.98"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "320cfe77175da3a483efed4bc0adc1968ca050b098ce4f2f1c13a56626128790"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b16bd47d9e329435e309c58469fe0791c2d0d1ba96ec0954152a5ae2b04387dc"
|
||||
|
||||
[[package]]
|
||||
name = "num_cpus"
|
||||
version = "1.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "692fcb63b64b1758029e0a96ee63e049ce8c5948587f2f7208df04625e5f6b56"
|
||||
|
||||
[[package]]
|
||||
name = "ouroboros"
|
||||
version = "0.9.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fbeff60e3e37407a80ead3e9458145b456e978c4068cddbfea6afb48572962ca"
|
||||
dependencies = [
|
||||
"ouroboros_macro",
|
||||
"stable_deref_trait",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ouroboros_macro"
|
||||
version = "0.9.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "03f2cb802b5bdfdf52f1ffa0b54ce105e4d346e91990dd571f86c91321ad49e2"
|
||||
dependencies = [
|
||||
"Inflector",
|
||||
"proc-macro-error",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pin-project-lite"
|
||||
version = "0.2.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8d31d11c69a6b52a174b42bdc0c30e5e11670f90788b2c471c31c1d17d449443"
|
||||
|
||||
[[package]]
|
||||
name = "pin-utils"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-error"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
|
||||
dependencies = [
|
||||
"proc-macro-error-attr",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-error-attr"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-hack"
|
||||
version = "0.5.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-nested"
|
||||
version = "0.1.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bc881b2c22681370c6a780e47af9840ef841837bc98118431d4e1868bd0c1086"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f0d8caf72986c1a598726adc988bb5984792ef84f5ee5aa50209145ee8077038"
|
||||
dependencies = [
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc-hash"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
|
||||
|
||||
[[package]]
|
||||
name = "slab"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f173ac3d1a7e3b28003f40de0b5ce7fe2710f9b9dc3fc38664cebee46b3b6527"
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e"
|
||||
|
||||
[[package]]
|
||||
name = "stable_deref_trait"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.73"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f71489ff30030d2ae598524f61326b902466f72a0fb1a8564c001cc63425bcc7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "93119e4feac1cbe6c798c34d3a53ea0026b0b1de6a120deef895137c0529bfe2"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "060d69a0afe7796bf42e9e2ff91f5ee691fb15c53d38b4b62a9a53eb23164745"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tinystr"
|
||||
version = "0.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "29738eedb4388d9ea620eeab9384884fc3f06f586a2eddb56bedc5885126c7c1"
|
||||
|
||||
[[package]]
|
||||
name = "tokio"
|
||||
version = "1.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "98c8b05dc14c75ea83d63dd391100353789f5f24b8b3866542a5e85c8be8e985"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"num_cpus",
|
||||
"pin-project-lite",
|
||||
"tokio-macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-macros"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "54473be61f4ebe4efd09cec9bd5d16fa51d70ea0192213d754d2d500457db110"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "type-map"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b6d3364c5e96cb2ad1603037ab253ddd34d7fb72a58bdddf4b7350760fc69a46"
|
||||
dependencies = [
|
||||
"rustc-hash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unic-langid"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "73328fcd730a030bdb19ddf23e192187a6b01cd98be6d3140622a89129459ce5"
|
||||
dependencies = [
|
||||
"unic-langid-impl",
|
||||
"unic-langid-macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unic-langid-impl"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1a4a8eeaf0494862c1404c95ec2f4c33a2acff5076f64314b465e3ddae1b934d"
|
||||
dependencies = [
|
||||
"tinystr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unic-langid-macros"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "18f980d6d87e8805f2836d64b4138cc95aa7986fa63b1f51f67d5fbff64dd6e5"
|
||||
dependencies = [
|
||||
"proc-macro-hack",
|
||||
"tinystr",
|
||||
"unic-langid-impl",
|
||||
"unic-langid-macros-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unic-langid-macros-impl"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "29396ffd97e27574c3e01368b1a64267d3064969e4848e2e130ff668be9daa9f"
|
||||
dependencies = [
|
||||
"proc-macro-hack",
|
||||
"quote",
|
||||
"syn",
|
||||
"unic-langid-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe"
|
51
third_party/rust/fluent-fallback/Cargo.toml
vendored
Normal file
51
third_party/rust/fluent-fallback/Cargo.toml
vendored
Normal file
@ -0,0 +1,51 @@
|
||||
# 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 believe there's an error in this file please file an
|
||||
# issue against the rust-lang/cargo repository. If you're
|
||||
# editing this file be aware that the upstream Cargo.toml
|
||||
# will likely look very different (and much more reasonable)
|
||||
|
||||
[package]
|
||||
edition = "2018"
|
||||
name = "fluent-fallback"
|
||||
version = "0.5.0"
|
||||
authors = ["Zibi Braniecki <gandalf@mozilla.com>", "Staś Małolepszy <stas@mozilla.com>"]
|
||||
description = "High-level abstraction model for managing localization resources\nand runtime localization lifecycle.\n"
|
||||
homepage = "http://www.projectfluent.org"
|
||||
readme = "README.md"
|
||||
keywords = ["localization", "l10n", "i18n", "intl", "internationalization"]
|
||||
categories = ["localization", "internationalization"]
|
||||
license = "Apache-2.0/MIT"
|
||||
repository = "https://github.com/projectfluent/fluent-rs"
|
||||
[dependencies.async-trait]
|
||||
version = "0.1"
|
||||
|
||||
[dependencies.chunky-vec]
|
||||
version = "0.1"
|
||||
|
||||
[dependencies.fluent-bundle]
|
||||
version = "0.15.1"
|
||||
|
||||
[dependencies.futures]
|
||||
version = "0.3"
|
||||
|
||||
[dependencies.once_cell]
|
||||
version = "1.8"
|
||||
|
||||
[dependencies.unic-langid]
|
||||
version = "0.9"
|
||||
[dev-dependencies.fluent-langneg]
|
||||
version = "0.13"
|
||||
|
||||
[dev-dependencies.tokio]
|
||||
version = "1.0"
|
||||
features = ["rt-multi-thread", "macros"]
|
||||
|
||||
[dev-dependencies.unic-langid]
|
||||
version = "0.9"
|
||||
features = ["macros"]
|
201
third_party/rust/fluent-fallback/LICENSE-APACHE
vendored
Normal file
201
third_party/rust/fluent-fallback/LICENSE-APACHE
vendored
Normal 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 2017 Mozilla
|
||||
|
||||
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.
|
19
third_party/rust/fluent-fallback/LICENSE-MIT
vendored
Normal file
19
third_party/rust/fluent-fallback/LICENSE-MIT
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
Copyright 2017 Mozilla
|
||||
|
||||
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.
|
102
third_party/rust/fluent-fallback/README.md
vendored
Normal file
102
third_party/rust/fluent-fallback/README.md
vendored
Normal file
@ -0,0 +1,102 @@
|
||||
# Fluent
|
||||
|
||||
`fluent-fallback` is a Rust implementation of the [Project Fluent][] higher level API.
|
||||
|
||||
The `Localization` struct encapsulates a persistant localization context providing
|
||||
language fallbacking. The instance remains available throughout the whole life cycle of
|
||||
the corresponding UI, reacting to events such as locale changes, resource updates etc.
|
||||
|
||||
The API can be used directly, or can serve as an example of state manager for `fluent-bundle` and `fluent-resmgr`.
|
||||
|
||||
[![crates.io](https://meritbadge.herokuapp.com/fluent-fallback)](https://crates.io/crates/fluent-fallback)
|
||||
[![Build and test](https://github.com/projectfluent/fluent-rs/workflows/Build%20and%20test/badge.svg)](https://github.com/projectfluent/fluent-rs/actions?query=branch%3Amaster+workflow%3A%22Build+and+test%22)
|
||||
[![Coverage Status](https://coveralls.io/repos/github/projectfluent/fluent-rs/badge.svg?branch=master)](https://coveralls.io/github/projectfluent/fluent-rs?branch=master)
|
||||
|
||||
Project Fluent keeps simple things simple and makes complex things possible.
|
||||
The syntax used for describing translations is easy to read and understand. At
|
||||
the same time it allows, when necessary, to represent complex concepts from
|
||||
natural languages like gender, plurals, conjugations, and others.
|
||||
|
||||
[Documentation][]
|
||||
|
||||
[Project Fluent]: http://projectfluent.org
|
||||
[Documentation]: https://docs.rs/fluent/
|
||||
|
||||
Usage
|
||||
-----
|
||||
|
||||
```rust
|
||||
use fluent_fallback::Localization;
|
||||
|
||||
fn main() {
|
||||
// generate_messages is a closure that returns an iterator over FluentBundle
|
||||
// instances.
|
||||
let loc = Localization::new(vec!["simple.ftl".into()], generate_messages);
|
||||
|
||||
let value = bundle.format_value("hello-world", None);
|
||||
|
||||
assert_eq!(&value, "Hello, world!");
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
Status
|
||||
------
|
||||
|
||||
The implementation is in its early stages and supports only some of the Project
|
||||
Fluent's spec. Consult the [list of milestones][] for more information about
|
||||
release planning and scope.
|
||||
|
||||
[list of milestones]: https://github.com/projectfluent/fluent-rs/milestones
|
||||
|
||||
|
||||
Local Development
|
||||
-----------------
|
||||
|
||||
cargo build
|
||||
cargo test
|
||||
cargo run --example simple-fallback
|
||||
|
||||
When submitting a PR please use [`cargo fmt`][] (nightly).
|
||||
|
||||
[`cargo fmt`]: https://github.com/rust-lang-nursery/rustfmt
|
||||
|
||||
|
||||
Learn the FTL syntax
|
||||
--------------------
|
||||
|
||||
FTL is a localization file format used for describing translation resources.
|
||||
FTL stands for _Fluent Translation List_.
|
||||
|
||||
FTL is designed to be simple to read, but at the same time allows to represent
|
||||
complex concepts from natural languages like gender, plurals, conjugations, and
|
||||
others.
|
||||
|
||||
hello-user = Hello, { $username }!
|
||||
|
||||
[Read the Fluent Syntax Guide][] in order to learn more about the syntax. If
|
||||
you're a tool author you may be interested in the formal [EBNF grammar][].
|
||||
|
||||
[Read the Fluent Syntax Guide]: http://projectfluent.org/fluent/guide/
|
||||
[EBNF grammar]: https://github.com/projectfluent/fluent/tree/master/spec
|
||||
|
||||
|
||||
Get Involved
|
||||
------------
|
||||
|
||||
`fluent-rs` is open-source, licensed under the Apache License, Version 2.0. We
|
||||
encourage everyone to take a look at our code and we'll listen to your
|
||||
feedback.
|
||||
|
||||
|
||||
Discuss
|
||||
-------
|
||||
|
||||
We'd love to hear your thoughts on Project Fluent! Whether you're a localizer
|
||||
looking for a better way to express yourself in your language, or a developer
|
||||
trying to make your app localizable and multilingual, or a hacker looking for
|
||||
a project to contribute to, please do get in touch on the mailing list and the
|
||||
IRC channel.
|
||||
|
||||
- Discourse: https://discourse.mozilla.org/c/fluent
|
||||
- IRC channel: [irc://irc.mozilla.org/l20n](irc://irc.mozilla.org/l20n)
|
7
third_party/rust/fluent-fallback/examples/resources/en-US/simple.ftl
vendored
Normal file
7
third_party/rust/fluent-fallback/examples/resources/en-US/simple.ftl
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
missing-arg-error = Error: Please provide a number as argument.
|
||||
input-parse-error = Error: Could not parse input `{ $input }`. Reason: { $reason }
|
||||
response-msg =
|
||||
{ $value ->
|
||||
[one] "{ $input }" has one Collatz step.
|
||||
*[other] "{ $input }" has { $value } Collatz steps.
|
||||
}
|
8
third_party/rust/fluent-fallback/examples/resources/pl/simple.ftl
vendored
Normal file
8
third_party/rust/fluent-fallback/examples/resources/pl/simple.ftl
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
missing-arg-error = Błąd: Proszę wprowadzić liczbę jako argument.
|
||||
input-parse-error = Błąd: Nie udało się sparsować `{ $input }`. Powód: { $reason }
|
||||
response-msg =
|
||||
{ $value ->
|
||||
[one] "{ $input }" ma jeden krok Collatza.
|
||||
[few] "{ $input }" ma { $value } kroki Collatza.
|
||||
*[many] "{ $input }" ma { $value } kroków Collatza.
|
||||
}
|
231
third_party/rust/fluent-fallback/examples/simple-fallback.rs
vendored
Normal file
231
third_party/rust/fluent-fallback/examples/simple-fallback.rs
vendored
Normal file
@ -0,0 +1,231 @@
|
||||
//! This is an example of a simple application
|
||||
//! which calculates the Collatz conjecture.
|
||||
//!
|
||||
//! The function itself is trivial on purpose,
|
||||
//! so that we can focus on understanding how
|
||||
//! the application can be made localizable
|
||||
//! via Fluent.
|
||||
//!
|
||||
//! To try the app launch `cargo run --example simple-fallback NUM (LOCALES)`
|
||||
//!
|
||||
//! NUM is a number to be calculated, and LOCALES is an optional
|
||||
//! parameter with a comma-separated list of locales requested by the user.
|
||||
//!
|
||||
//! Example:
|
||||
//!
|
||||
//! cargo run --example simple-fallback 123 de,pl
|
||||
//!
|
||||
//! If the second argument is omitted, `en-US` locale is used as the
|
||||
//! default one.
|
||||
|
||||
use std::{env, fs, io, path::PathBuf, str::FromStr};
|
||||
|
||||
use fluent_bundle::{FluentArgs, FluentBundle, FluentResource};
|
||||
use fluent_fallback::{
|
||||
generator::{BundleGenerator, FluentBundleResult},
|
||||
Localization,
|
||||
};
|
||||
use fluent_langneg::{negotiate_languages, NegotiationStrategy};
|
||||
|
||||
use unic_langid::{langid, LanguageIdentifier};
|
||||
|
||||
/// This helper struct holds the scheme for converting
|
||||
/// resource paths into full paths. It is used to customise
|
||||
/// `fluent-fallback::SyncLocalization`.
|
||||
struct Bundles {
|
||||
res_path_scheme: PathBuf,
|
||||
}
|
||||
|
||||
/// This helper function allows us to read the list
|
||||
/// of available locales by reading the list of
|
||||
/// directories in `./examples/resources`.
|
||||
///
|
||||
/// It is expected that every directory inside it
|
||||
/// has a name that is a valid BCP47 language tag.
|
||||
fn get_available_locales() -> io::Result<Vec<LanguageIdentifier>> {
|
||||
let mut dir = env::current_dir()?;
|
||||
if dir.to_string_lossy().ends_with("fluent-rs") {
|
||||
dir.push("fluent-fallback");
|
||||
}
|
||||
dir.push("examples");
|
||||
dir.push("resources");
|
||||
let res_dir = fs::read_dir(dir)?;
|
||||
|
||||
let locales = res_dir
|
||||
.into_iter()
|
||||
.filter_map(|entry| entry.ok())
|
||||
.filter(|entry| entry.path().is_dir())
|
||||
.filter_map(|dir| {
|
||||
let file_name = dir.file_name();
|
||||
let name = file_name.to_str()?;
|
||||
Some(name.parse().expect("Parsing failed."))
|
||||
})
|
||||
.collect();
|
||||
Ok(locales)
|
||||
}
|
||||
|
||||
fn resolve_app_locales<'l>(args: &[String]) -> Vec<LanguageIdentifier> {
|
||||
let default_locale = langid!("en-US");
|
||||
let available = get_available_locales().expect("Retrieving available locales failed.");
|
||||
|
||||
let requested: Vec<LanguageIdentifier> = args.get(2).map_or(vec![], |arg| {
|
||||
arg.split(",")
|
||||
.map(|s| s.parse().expect("Parsing locale failed."))
|
||||
.collect()
|
||||
});
|
||||
|
||||
negotiate_languages(
|
||||
&requested,
|
||||
&available,
|
||||
Some(&default_locale),
|
||||
NegotiationStrategy::Filtering,
|
||||
)
|
||||
.into_iter()
|
||||
.cloned()
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn get_resource_manager() -> Bundles {
|
||||
let mut res_path_scheme = env::current_dir().expect("Failed to retrieve current dir.");
|
||||
|
||||
if res_path_scheme.to_string_lossy().ends_with("fluent-rs") {
|
||||
res_path_scheme.push("fluent-fallback");
|
||||
}
|
||||
res_path_scheme.push("examples");
|
||||
res_path_scheme.push("resources");
|
||||
|
||||
res_path_scheme.push("{locale}");
|
||||
res_path_scheme.push("{res_id}");
|
||||
|
||||
Bundles { res_path_scheme }
|
||||
}
|
||||
|
||||
static L10N_RESOURCES: &[&str] = &["simple.ftl"];
|
||||
|
||||
fn main() {
|
||||
let args: Vec<String> = env::args().collect();
|
||||
|
||||
let app_locales: Vec<LanguageIdentifier> = resolve_app_locales(&args);
|
||||
|
||||
let bundles = get_resource_manager();
|
||||
|
||||
let loc = Localization::with_env(
|
||||
L10N_RESOURCES.iter().map(|&res| res.into()).collect(),
|
||||
true,
|
||||
app_locales,
|
||||
bundles,
|
||||
);
|
||||
let bundles = loc.bundles();
|
||||
|
||||
let mut errors = vec![];
|
||||
|
||||
match args.get(1) {
|
||||
Some(input) => match isize::from_str(&input) {
|
||||
Ok(i) => {
|
||||
let mut args = FluentArgs::new();
|
||||
args.set("input", i);
|
||||
args.set("value", collatz(i));
|
||||
let value = bundles
|
||||
.format_value_sync("response-msg", Some(&args), &mut errors)
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
println!("{}", value);
|
||||
}
|
||||
Err(err) => {
|
||||
let mut args = FluentArgs::new();
|
||||
args.set("input", input.as_str());
|
||||
args.set("reason", err.to_string());
|
||||
let value = bundles
|
||||
.format_value_sync("input-parse-error-msg", Some(&args), &mut errors)
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
println!("{}", value);
|
||||
}
|
||||
},
|
||||
None => {
|
||||
let value = bundles
|
||||
.format_value_sync("missing-arg-error", None, &mut errors)
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
println!("{}", value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Collatz conjecture calculating function.
|
||||
fn collatz(n: isize) -> isize {
|
||||
match n {
|
||||
1 => 0,
|
||||
_ => match n % 2 {
|
||||
0 => 1 + collatz(n / 2),
|
||||
_ => 1 + collatz(n * 3 + 1),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Bundle iterator used by BundleGeneratorSync implementation for Locales.
|
||||
struct BundleIter {
|
||||
res_path_scheme: String,
|
||||
locales: <Vec<LanguageIdentifier> as IntoIterator>::IntoIter,
|
||||
res_ids: Vec<String>,
|
||||
}
|
||||
|
||||
impl Iterator for BundleIter {
|
||||
type Item = FluentBundleResult<FluentResource>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let locale = self.locales.next()?;
|
||||
let res_path_scheme = self
|
||||
.res_path_scheme
|
||||
.as_str()
|
||||
.replace("{locale}", &locale.to_string());
|
||||
let mut bundle = FluentBundle::new(vec![locale]);
|
||||
|
||||
let mut errors = vec![];
|
||||
|
||||
for res_id in &self.res_ids {
|
||||
let res_path = res_path_scheme.as_str().replace("{res_id}", res_id);
|
||||
let source = fs::read_to_string(res_path).unwrap();
|
||||
let res = match FluentResource::try_new(source) {
|
||||
Ok(res) => res,
|
||||
Err((res, err)) => {
|
||||
errors.extend(err.into_iter().map(Into::into));
|
||||
res
|
||||
}
|
||||
};
|
||||
bundle.add_resource(res).unwrap();
|
||||
}
|
||||
|
||||
if errors.is_empty() {
|
||||
Some(Ok(bundle))
|
||||
} else {
|
||||
Some(Err((bundle, errors)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl futures::Stream for BundleIter {
|
||||
type Item = FluentBundleResult<FluentResource>;
|
||||
|
||||
fn poll_next(
|
||||
self: std::pin::Pin<&mut Self>,
|
||||
_cx: &mut std::task::Context<'_>,
|
||||
) -> std::task::Poll<Option<Self::Item>> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl BundleGenerator for Bundles {
|
||||
type Resource = FluentResource;
|
||||
type LocalesIter = std::vec::IntoIter<LanguageIdentifier>;
|
||||
type Iter = BundleIter;
|
||||
type Stream = BundleIter;
|
||||
|
||||
fn bundles_iter(&self, locales: Self::LocalesIter, res_ids: Vec<String>) -> Self::Iter {
|
||||
BundleIter {
|
||||
res_path_scheme: self.res_path_scheme.to_string_lossy().to_string(),
|
||||
locales,
|
||||
res_ids,
|
||||
}
|
||||
}
|
||||
}
|
425
third_party/rust/fluent-fallback/src/bundles.rs
vendored
Normal file
425
third_party/rust/fluent-fallback/src/bundles.rs
vendored
Normal file
@ -0,0 +1,425 @@
|
||||
use crate::{
|
||||
cache::{AsyncCache, Cache},
|
||||
env::LocalesProvider,
|
||||
errors::LocalizationError,
|
||||
generator::{BundleGenerator, BundleIterator, BundleStream},
|
||||
types::{L10nAttribute, L10nKey, L10nMessage},
|
||||
};
|
||||
use fluent_bundle::{FluentArgs, FluentBundle, FluentError};
|
||||
use std::borrow::Cow;
|
||||
|
||||
pub enum BundlesInner<G>
|
||||
where
|
||||
G: BundleGenerator,
|
||||
{
|
||||
Iter(Cache<G::Iter, G::Resource>),
|
||||
Stream(AsyncCache<G::Stream, G::Resource>),
|
||||
}
|
||||
|
||||
pub struct Bundles<G>(BundlesInner<G>)
|
||||
where
|
||||
G: BundleGenerator;
|
||||
|
||||
impl<G> Bundles<G>
|
||||
where
|
||||
G: BundleGenerator,
|
||||
G::Iter: BundleIterator,
|
||||
{
|
||||
pub fn prefetch_sync(&self) {
|
||||
match &self.0 {
|
||||
BundlesInner::Iter(iter) => iter.prefetch(),
|
||||
BundlesInner::Stream(_) => panic!("Can't prefetch a sync bundle set asynchronously"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<G> Bundles<G>
|
||||
where
|
||||
G: BundleGenerator,
|
||||
G::Stream: BundleStream,
|
||||
{
|
||||
pub async fn prefetch_async(&self) {
|
||||
match &self.0 {
|
||||
BundlesInner::Iter(_) => panic!("Can't prefetch a async bundle set synchronously"),
|
||||
BundlesInner::Stream(stream) => stream.prefetch().await,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<G> Bundles<G>
|
||||
where
|
||||
G: BundleGenerator,
|
||||
{
|
||||
pub fn new<P>(sync: bool, res_ids: Vec<String>, generator: &G, provider: &P) -> Self
|
||||
where
|
||||
G: BundleGenerator<LocalesIter = P::Iter>,
|
||||
P: LocalesProvider,
|
||||
{
|
||||
Self(if sync {
|
||||
BundlesInner::Iter(Cache::new(
|
||||
generator.bundles_iter(provider.locales(), res_ids),
|
||||
))
|
||||
} else {
|
||||
BundlesInner::Stream(AsyncCache::new(
|
||||
generator.bundles_stream(provider.locales(), res_ids),
|
||||
))
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn format_value<'l>(
|
||||
&'l self,
|
||||
id: &'l str,
|
||||
args: Option<&'l FluentArgs<'_>>,
|
||||
errors: &mut Vec<LocalizationError>,
|
||||
) -> Option<Cow<'l, str>> {
|
||||
match &self.0 {
|
||||
BundlesInner::Iter(cache) => Self::format_value_from_iter(cache, id, args, errors),
|
||||
BundlesInner::Stream(stream) => {
|
||||
Self::format_value_from_stream(stream, id, args, errors).await
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn format_values<'l>(
|
||||
&'l self,
|
||||
keys: &'l [L10nKey<'l>],
|
||||
errors: &mut Vec<LocalizationError>,
|
||||
) -> Vec<Option<Cow<'l, str>>> {
|
||||
match &self.0 {
|
||||
BundlesInner::Iter(cache) => Self::format_values_from_iter(cache, keys, errors),
|
||||
BundlesInner::Stream(stream) => {
|
||||
Self::format_values_from_stream(stream, keys, errors).await
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn format_messages<'l>(
|
||||
&'l self,
|
||||
keys: &'l [L10nKey<'l>],
|
||||
errors: &mut Vec<LocalizationError>,
|
||||
) -> Vec<Option<L10nMessage<'l>>> {
|
||||
match &self.0 {
|
||||
BundlesInner::Iter(cache) => Self::format_messages_from_iter(cache, keys, errors),
|
||||
BundlesInner::Stream(stream) => {
|
||||
Self::format_messages_from_stream(stream, keys, errors).await
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn format_value_sync<'l>(
|
||||
&'l self,
|
||||
id: &'l str,
|
||||
args: Option<&'l FluentArgs>,
|
||||
errors: &mut Vec<LocalizationError>,
|
||||
) -> Result<Option<Cow<'l, str>>, LocalizationError> {
|
||||
match &self.0 {
|
||||
BundlesInner::Iter(cache) => Ok(Self::format_value_from_iter(cache, id, args, errors)),
|
||||
BundlesInner::Stream(_) => Err(LocalizationError::SyncRequestInAsyncMode),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn format_values_sync<'l>(
|
||||
&'l self,
|
||||
keys: &'l [L10nKey<'l>],
|
||||
errors: &mut Vec<LocalizationError>,
|
||||
) -> Result<Vec<Option<Cow<'l, str>>>, LocalizationError> {
|
||||
match &self.0 {
|
||||
BundlesInner::Iter(cache) => Ok(Self::format_values_from_iter(cache, keys, errors)),
|
||||
BundlesInner::Stream(_) => Err(LocalizationError::SyncRequestInAsyncMode),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn format_messages_sync<'l>(
|
||||
&'l self,
|
||||
keys: &'l [L10nKey<'l>],
|
||||
errors: &mut Vec<LocalizationError>,
|
||||
) -> Result<Vec<Option<L10nMessage<'l>>>, LocalizationError> {
|
||||
match &self.0 {
|
||||
BundlesInner::Iter(cache) => Ok(Self::format_messages_from_iter(cache, keys, errors)),
|
||||
BundlesInner::Stream(_) => Err(LocalizationError::SyncRequestInAsyncMode),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! format_value_from_inner {
|
||||
($step:expr, $id:expr, $args:expr, $errors:expr) => {
|
||||
let mut found_message = false;
|
||||
|
||||
while let Some(bundle) = $step {
|
||||
let bundle = bundle.as_ref().unwrap_or_else(|(bundle, err)| {
|
||||
$errors.extend(err.iter().cloned().map(Into::into));
|
||||
bundle
|
||||
});
|
||||
|
||||
if let Some(msg) = bundle.get_message($id) {
|
||||
found_message = true;
|
||||
if let Some(value) = msg.value() {
|
||||
let mut format_errors = vec![];
|
||||
let result = bundle.format_pattern(value, $args, &mut format_errors);
|
||||
if !format_errors.is_empty() {
|
||||
$errors.push(LocalizationError::Resolver {
|
||||
id: $id.to_string(),
|
||||
locale: bundle.locales[0].clone(),
|
||||
errors: format_errors,
|
||||
});
|
||||
}
|
||||
return Some(result);
|
||||
} else {
|
||||
$errors.push(LocalizationError::MissingValue {
|
||||
id: $id.to_string(),
|
||||
locale: Some(bundle.locales[0].clone()),
|
||||
});
|
||||
}
|
||||
} else {
|
||||
$errors.push(LocalizationError::MissingMessage {
|
||||
id: $id.to_string(),
|
||||
locale: Some(bundle.locales[0].clone()),
|
||||
});
|
||||
}
|
||||
}
|
||||
if found_message {
|
||||
$errors.push(LocalizationError::MissingValue {
|
||||
id: $id.to_string(),
|
||||
locale: None,
|
||||
});
|
||||
} else {
|
||||
$errors.push(LocalizationError::MissingMessage {
|
||||
id: $id.to_string(),
|
||||
locale: None,
|
||||
});
|
||||
}
|
||||
return None;
|
||||
};
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
enum Value<'l> {
|
||||
Value(Cow<'l, str>),
|
||||
MissingValue,
|
||||
None,
|
||||
}
|
||||
|
||||
macro_rules! format_values_from_inner {
|
||||
($step:expr, $keys:expr, $errors:expr) => {
|
||||
let mut cells = vec![Value::None; $keys.len()];
|
||||
|
||||
while let Some(bundle) = $step {
|
||||
let bundle = bundle.as_ref().unwrap_or_else(|(bundle, err)| {
|
||||
$errors.extend(err.iter().cloned().map(Into::into));
|
||||
bundle
|
||||
});
|
||||
|
||||
let mut has_missing = false;
|
||||
|
||||
for (key, cell) in $keys
|
||||
.iter()
|
||||
.zip(&mut cells)
|
||||
.filter(|(_, cell)| !matches!(cell, Value::Value(_)))
|
||||
{
|
||||
if let Some(msg) = bundle.get_message(&key.id) {
|
||||
if let Some(value) = msg.value() {
|
||||
let mut format_errors = vec![];
|
||||
*cell = Value::Value(bundle.format_pattern(
|
||||
value,
|
||||
key.args.as_ref(),
|
||||
&mut format_errors,
|
||||
));
|
||||
if !format_errors.is_empty() {
|
||||
$errors.push(LocalizationError::Resolver {
|
||||
id: key.id.to_string(),
|
||||
locale: bundle.locales[0].clone(),
|
||||
errors: format_errors,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
*cell = Value::MissingValue;
|
||||
has_missing = true;
|
||||
$errors.push(LocalizationError::MissingValue {
|
||||
id: key.id.to_string(),
|
||||
locale: Some(bundle.locales[0].clone()),
|
||||
});
|
||||
}
|
||||
} else {
|
||||
has_missing = true;
|
||||
$errors.push(LocalizationError::MissingMessage {
|
||||
id: key.id.to_string(),
|
||||
locale: Some(bundle.locales[0].clone()),
|
||||
});
|
||||
}
|
||||
}
|
||||
if !has_missing {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $keys
|
||||
.iter()
|
||||
.zip(cells)
|
||||
.map(|(key, value)| match value {
|
||||
Value::Value(value) => Some(value),
|
||||
Value::MissingValue => {
|
||||
$errors.push(LocalizationError::MissingValue {
|
||||
id: key.id.to_string(),
|
||||
locale: None,
|
||||
});
|
||||
None
|
||||
}
|
||||
Value::None => {
|
||||
$errors.push(LocalizationError::MissingMessage {
|
||||
id: key.id.to_string(),
|
||||
locale: None,
|
||||
});
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! format_messages_from_inner {
|
||||
($step:expr, $keys:expr, $errors:expr) => {
|
||||
let mut result = vec![None; $keys.len()];
|
||||
|
||||
let mut is_complete = false;
|
||||
|
||||
while let Some(bundle) = $step {
|
||||
let bundle = bundle.as_ref().unwrap_or_else(|(bundle, err)| {
|
||||
$errors.extend(err.iter().cloned().map(Into::into));
|
||||
bundle
|
||||
});
|
||||
|
||||
let mut has_missing = false;
|
||||
for (key, cell) in $keys
|
||||
.iter()
|
||||
.zip(&mut result)
|
||||
.filter(|(_, cell)| cell.is_none())
|
||||
{
|
||||
let mut format_errors = vec![];
|
||||
let msg = Self::format_message_from_bundle(bundle, key, &mut format_errors);
|
||||
|
||||
if msg.is_none() {
|
||||
has_missing = true;
|
||||
$errors.push(LocalizationError::MissingMessage {
|
||||
id: key.id.to_string(),
|
||||
locale: Some(bundle.locales[0].clone()),
|
||||
});
|
||||
} else if !format_errors.is_empty() {
|
||||
$errors.push(LocalizationError::Resolver {
|
||||
id: key.id.to_string(),
|
||||
locale: bundle.locales.get(0).cloned().unwrap(),
|
||||
errors: format_errors,
|
||||
});
|
||||
}
|
||||
|
||||
*cell = msg;
|
||||
}
|
||||
if !has_missing {
|
||||
is_complete = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if !is_complete {
|
||||
for (key, _) in $keys
|
||||
.iter()
|
||||
.zip(&mut result)
|
||||
.filter(|(_, cell)| cell.is_none())
|
||||
{
|
||||
$errors.push(LocalizationError::MissingMessage {
|
||||
id: key.id.to_string(),
|
||||
locale: None,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
}
|
||||
|
||||
impl<G> Bundles<G>
|
||||
where
|
||||
G: BundleGenerator,
|
||||
{
|
||||
fn format_value_from_iter<'l>(
|
||||
cache: &'l Cache<G::Iter, G::Resource>,
|
||||
id: &'l str,
|
||||
args: Option<&'l FluentArgs>,
|
||||
errors: &mut Vec<LocalizationError>,
|
||||
) -> Option<Cow<'l, str>> {
|
||||
let mut bundle_iter = cache.into_iter();
|
||||
format_value_from_inner!(bundle_iter.next(), id, args, errors);
|
||||
}
|
||||
|
||||
async fn format_value_from_stream<'l>(
|
||||
stream: &'l AsyncCache<G::Stream, G::Resource>,
|
||||
id: &'l str,
|
||||
args: Option<&'l FluentArgs<'_>>,
|
||||
errors: &mut Vec<LocalizationError>,
|
||||
) -> Option<Cow<'l, str>> {
|
||||
use futures::StreamExt;
|
||||
|
||||
let mut bundle_stream = stream.stream();
|
||||
format_value_from_inner!(bundle_stream.next().await, id, args, errors);
|
||||
}
|
||||
|
||||
async fn format_messages_from_stream<'l>(
|
||||
stream: &'l AsyncCache<G::Stream, G::Resource>,
|
||||
keys: &'l [L10nKey<'l>],
|
||||
errors: &mut Vec<LocalizationError>,
|
||||
) -> Vec<Option<L10nMessage<'l>>> {
|
||||
use futures::StreamExt;
|
||||
let mut bundle_stream = stream.stream();
|
||||
format_messages_from_inner!(bundle_stream.next().await, keys, errors);
|
||||
}
|
||||
|
||||
async fn format_values_from_stream<'l>(
|
||||
stream: &'l AsyncCache<G::Stream, G::Resource>,
|
||||
keys: &'l [L10nKey<'l>],
|
||||
errors: &mut Vec<LocalizationError>,
|
||||
) -> Vec<Option<Cow<'l, str>>> {
|
||||
use futures::StreamExt;
|
||||
let mut bundle_stream = stream.stream();
|
||||
|
||||
format_values_from_inner!(bundle_stream.next().await, keys, errors);
|
||||
}
|
||||
|
||||
fn format_message_from_bundle<'l>(
|
||||
bundle: &'l FluentBundle<G::Resource>,
|
||||
key: &'l L10nKey,
|
||||
format_errors: &mut Vec<FluentError>,
|
||||
) -> Option<L10nMessage<'l>> {
|
||||
let msg = bundle.get_message(&key.id)?;
|
||||
let value = msg
|
||||
.value()
|
||||
.map(|pattern| bundle.format_pattern(pattern, key.args.as_ref(), format_errors));
|
||||
let attributes = msg
|
||||
.attributes()
|
||||
.map(|attr| {
|
||||
let value = bundle.format_pattern(attr.value(), key.args.as_ref(), format_errors);
|
||||
L10nAttribute {
|
||||
name: attr.id().into(),
|
||||
value,
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
Some(L10nMessage { value, attributes })
|
||||
}
|
||||
|
||||
fn format_messages_from_iter<'l>(
|
||||
cache: &'l Cache<G::Iter, G::Resource>,
|
||||
keys: &'l [L10nKey<'l>],
|
||||
errors: &mut Vec<LocalizationError>,
|
||||
) -> Vec<Option<L10nMessage<'l>>> {
|
||||
let mut bundle_iter = cache.into_iter();
|
||||
format_messages_from_inner!(bundle_iter.next(), keys, errors);
|
||||
}
|
||||
|
||||
fn format_values_from_iter<'l>(
|
||||
cache: &'l Cache<G::Iter, G::Resource>,
|
||||
keys: &'l [L10nKey<'l>],
|
||||
errors: &mut Vec<LocalizationError>,
|
||||
) -> Vec<Option<Cow<'l, str>>> {
|
||||
let mut bundle_iter = cache.into_iter();
|
||||
format_values_from_inner!(bundle_iter.next(), keys, errors);
|
||||
}
|
||||
}
|
253
third_party/rust/fluent-fallback/src/cache.rs
vendored
Normal file
253
third_party/rust/fluent-fallback/src/cache.rs
vendored
Normal file
@ -0,0 +1,253 @@
|
||||
use std::{
|
||||
cell::{RefCell, UnsafeCell},
|
||||
cmp::Ordering,
|
||||
pin::Pin,
|
||||
task::Context,
|
||||
task::Poll,
|
||||
task::Waker,
|
||||
};
|
||||
|
||||
use crate::generator::{BundleIterator, BundleStream};
|
||||
use crate::pin_cell::{PinCell, PinMut};
|
||||
use chunky_vec::ChunkyVec;
|
||||
use futures::{ready, Stream};
|
||||
|
||||
pub struct Cache<I, R>
|
||||
where
|
||||
I: Iterator,
|
||||
{
|
||||
iter: RefCell<I>,
|
||||
items: UnsafeCell<ChunkyVec<I::Item>>,
|
||||
res: std::marker::PhantomData<R>,
|
||||
}
|
||||
|
||||
impl<I, R> Cache<I, R>
|
||||
where
|
||||
I: Iterator,
|
||||
{
|
||||
pub fn new(iter: I) -> Self {
|
||||
Self {
|
||||
iter: RefCell::new(iter),
|
||||
items: Default::default(),
|
||||
res: std::marker::PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize {
|
||||
unsafe {
|
||||
let items = self.items.get();
|
||||
(*items).len()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get(&self, index: usize) -> Option<&I::Item> {
|
||||
unsafe {
|
||||
let items = self.items.get();
|
||||
(*items).get(index)
|
||||
}
|
||||
}
|
||||
|
||||
/// Push, immediately getting a reference to the element
|
||||
pub fn push_get(&self, new_value: I::Item) -> &I::Item {
|
||||
unsafe {
|
||||
let items = self.items.get();
|
||||
(*items).push_get(new_value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, R> Cache<I, R>
|
||||
where
|
||||
I: BundleIterator + Iterator,
|
||||
{
|
||||
pub fn prefetch(&self) {
|
||||
self.iter.borrow_mut().prefetch_sync();
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CacheIter<'a, I, R>
|
||||
where
|
||||
I: Iterator,
|
||||
{
|
||||
cache: &'a Cache<I, R>,
|
||||
curr: usize,
|
||||
}
|
||||
|
||||
impl<'a, I, R> Iterator for CacheIter<'a, I, R>
|
||||
where
|
||||
I: Iterator,
|
||||
{
|
||||
type Item = &'a I::Item;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let cache_len = self.cache.len();
|
||||
match self.curr.cmp(&cache_len) {
|
||||
Ordering::Less => {
|
||||
// Cached value
|
||||
self.curr += 1;
|
||||
self.cache.get(self.curr - 1)
|
||||
}
|
||||
Ordering::Equal => {
|
||||
// Get the next item from the iterator
|
||||
let item = self.cache.iter.borrow_mut().next();
|
||||
self.curr += 1;
|
||||
if let Some(item) = item {
|
||||
Some(self.cache.push_get(item))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
Ordering::Greater => {
|
||||
// Ran off the end of the cache
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, I, R> IntoIterator for &'a Cache<I, R>
|
||||
where
|
||||
I: Iterator,
|
||||
{
|
||||
type Item = &'a I::Item;
|
||||
type IntoIter = CacheIter<'a, I, R>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
CacheIter {
|
||||
cache: self,
|
||||
curr: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
pub struct AsyncCache<S, R>
|
||||
where
|
||||
S: Stream,
|
||||
{
|
||||
stream: PinCell<S>,
|
||||
items: UnsafeCell<ChunkyVec<S::Item>>,
|
||||
// TODO: Should probably be an SmallVec<[Waker; 1]> or something? I guess
|
||||
// multiple pending wakes are not really all that common.
|
||||
pending_wakes: RefCell<Vec<Waker>>,
|
||||
res: std::marker::PhantomData<R>,
|
||||
}
|
||||
|
||||
impl<S, R> AsyncCache<S, R>
|
||||
where
|
||||
S: Stream,
|
||||
{
|
||||
pub fn new(stream: S) -> Self {
|
||||
Self {
|
||||
stream: PinCell::new(stream),
|
||||
items: Default::default(),
|
||||
pending_wakes: Default::default(),
|
||||
res: std::marker::PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize {
|
||||
unsafe {
|
||||
let items = self.items.get();
|
||||
(*items).len()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get(&self, index: usize) -> Poll<Option<&S::Item>> {
|
||||
unsafe {
|
||||
let items = self.items.get();
|
||||
(*items).get(index).into()
|
||||
}
|
||||
}
|
||||
|
||||
/// Push, immediately getting a reference to the element
|
||||
pub fn push_get(&self, new_value: S::Item) -> &S::Item {
|
||||
unsafe {
|
||||
let items = self.items.get();
|
||||
(*items).push_get(new_value)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn stream(&self) -> AsyncCacheStream<'_, S, R> {
|
||||
AsyncCacheStream {
|
||||
cache: self,
|
||||
curr: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<S, R> AsyncCache<S, R>
|
||||
where
|
||||
S: BundleStream + Stream,
|
||||
{
|
||||
pub async fn prefetch(&self) {
|
||||
let pin = unsafe { Pin::new_unchecked(&self.stream) };
|
||||
unsafe { PinMut::as_mut(&mut pin.borrow_mut()).get_unchecked_mut() }
|
||||
.prefetch_async()
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
impl<S, R> AsyncCache<S, R>
|
||||
where
|
||||
S: Stream,
|
||||
{
|
||||
// Helper function that gets the next value from wrapped stream.
|
||||
fn poll_next_item(&self, cx: &mut Context<'_>) -> Poll<Option<S::Item>> {
|
||||
let pin = unsafe { Pin::new_unchecked(&self.stream) };
|
||||
let poll = PinMut::as_mut(&mut pin.borrow_mut()).poll_next(cx);
|
||||
if poll.is_ready() {
|
||||
let wakers = std::mem::take(&mut *self.pending_wakes.borrow_mut());
|
||||
for waker in wakers {
|
||||
waker.wake();
|
||||
}
|
||||
} else {
|
||||
self.pending_wakes.borrow_mut().push(cx.waker().clone());
|
||||
}
|
||||
poll
|
||||
}
|
||||
}
|
||||
|
||||
pub struct AsyncCacheStream<'a, S, R>
|
||||
where
|
||||
S: Stream,
|
||||
{
|
||||
cache: &'a AsyncCache<S, R>,
|
||||
curr: usize,
|
||||
}
|
||||
|
||||
impl<'a, S, R> Stream for AsyncCacheStream<'a, S, R>
|
||||
where
|
||||
S: Stream,
|
||||
{
|
||||
type Item = &'a S::Item;
|
||||
|
||||
fn poll_next(
|
||||
mut self: std::pin::Pin<&mut Self>,
|
||||
cx: &mut std::task::Context<'_>,
|
||||
) -> Poll<Option<Self::Item>> {
|
||||
let cache_len = self.cache.len();
|
||||
match self.curr.cmp(&cache_len) {
|
||||
Ordering::Less => {
|
||||
// Cached value
|
||||
self.curr += 1;
|
||||
self.cache.get(self.curr - 1)
|
||||
}
|
||||
Ordering::Equal => {
|
||||
// Get the next item from the stream
|
||||
let item = ready!(self.cache.poll_next_item(cx));
|
||||
self.curr += 1;
|
||||
if let Some(item) = item {
|
||||
Some(self.cache.push_get(item)).into()
|
||||
} else {
|
||||
None.into()
|
||||
}
|
||||
}
|
||||
Ordering::Greater => {
|
||||
// Ran off the end of the cache
|
||||
None.into()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
84
third_party/rust/fluent-fallback/src/env.rs
vendored
Normal file
84
third_party/rust/fluent-fallback/src/env.rs
vendored
Normal file
@ -0,0 +1,84 @@
|
||||
//! Traits required to provide environment driven data for [`Localization`](crate::Localization).
|
||||
//!
|
||||
//! Since [`Localization`](crate::Localization) is a long-lived structure,
|
||||
//! the model in which the user provides ability for the system to react to changes
|
||||
//! is by implementing the given environmental trait and triggering
|
||||
//! [`Localization::on_change`](crate::Localization::on_change) method.
|
||||
//!
|
||||
//! At the moment just a single trait is provided, which allows the
|
||||
//! environment to feed a selection of locales to be provided to the instance.
|
||||
//!
|
||||
//! The locales provided to [`Localization`](crate::Localization) should be
|
||||
//! already negotiated to ensure that the resources in those locales
|
||||
//! are available. The list should also be sorted according to the user
|
||||
//! preference, as the order is significant for how [`Localization`](crate::Localization) performs
|
||||
//! fallbacking.
|
||||
use unic_langid::LanguageIdentifier;
|
||||
|
||||
/// A trait used to provide a selection of locales to be used by the
|
||||
/// [`Localization`](crate::Localization) instance for runtime
|
||||
/// locale fallbacking.
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// use fluent_fallback::{Localization, env::LocalesProvider};
|
||||
/// use fluent_resmgr::ResourceManager;
|
||||
/// use unic_langid::LanguageIdentifier;
|
||||
/// use std::{
|
||||
/// rc::Rc,
|
||||
/// cell::RefCell
|
||||
/// };
|
||||
///
|
||||
/// #[derive(Clone)]
|
||||
/// struct Env {
|
||||
/// locales: Rc<RefCell<Vec<LanguageIdentifier>>>,
|
||||
/// }
|
||||
///
|
||||
/// impl Env {
|
||||
/// pub fn new(locales: Vec<LanguageIdentifier>) -> Self {
|
||||
/// Self { locales: Rc::new(RefCell::new(locales)) }
|
||||
/// }
|
||||
///
|
||||
/// pub fn set_locales(&mut self, new_locales: Vec<LanguageIdentifier>) {
|
||||
/// let mut locales = self.locales.borrow_mut();
|
||||
/// locales.clear();
|
||||
/// locales.extend(new_locales);
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// impl LocalesProvider for Env {
|
||||
/// type Iter = <Vec<LanguageIdentifier> as IntoIterator>::IntoIter;
|
||||
/// fn locales(&self) -> Self::Iter {
|
||||
/// self.locales.borrow().clone().into_iter()
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// let res_mgr = ResourceManager::new("./path/{locale}/".to_string());
|
||||
///
|
||||
/// let mut env = Env::new(vec![
|
||||
/// "en-GB".parse().unwrap()
|
||||
/// ]);
|
||||
///
|
||||
/// let mut loc = Localization::with_env(vec![], true, env.clone(), res_mgr);
|
||||
///
|
||||
/// env.set_locales(vec![
|
||||
/// "de".parse().unwrap(),
|
||||
/// "en-GB".parse().unwrap(),
|
||||
/// ]);
|
||||
///
|
||||
/// loc.on_change();
|
||||
///
|
||||
/// // The next format call will attempt to localize to `de` first and
|
||||
/// // fallback on `en-GB`.
|
||||
/// ```
|
||||
pub trait LocalesProvider {
|
||||
type Iter: Iterator<Item = LanguageIdentifier>;
|
||||
fn locales(&self) -> Self::Iter;
|
||||
}
|
||||
|
||||
impl LocalesProvider for Vec<LanguageIdentifier> {
|
||||
type Iter = <Vec<LanguageIdentifier> as IntoIterator>::IntoIter;
|
||||
fn locales(&self) -> Self::Iter {
|
||||
self.clone().into_iter()
|
||||
}
|
||||
}
|
71
third_party/rust/fluent-fallback/src/errors.rs
vendored
Normal file
71
third_party/rust/fluent-fallback/src/errors.rs
vendored
Normal file
@ -0,0 +1,71 @@
|
||||
use fluent_bundle::FluentError;
|
||||
use std::error::Error;
|
||||
use unic_langid::LanguageIdentifier;
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum LocalizationError {
|
||||
Bundle {
|
||||
error: FluentError,
|
||||
},
|
||||
Resolver {
|
||||
id: String,
|
||||
locale: LanguageIdentifier,
|
||||
errors: Vec<FluentError>,
|
||||
},
|
||||
MissingMessage {
|
||||
id: String,
|
||||
locale: Option<LanguageIdentifier>,
|
||||
},
|
||||
MissingValue {
|
||||
id: String,
|
||||
locale: Option<LanguageIdentifier>,
|
||||
},
|
||||
SyncRequestInAsyncMode,
|
||||
}
|
||||
|
||||
impl From<FluentError> for LocalizationError {
|
||||
fn from(error: FluentError) -> Self {
|
||||
Self::Bundle { error }
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for LocalizationError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::Bundle { error } => write!(f, "[fluent][bundle] error: {}", error),
|
||||
Self::Resolver { id, locale, errors } => {
|
||||
let errors: Vec<String> = errors.iter().map(|err| err.to_string()).collect();
|
||||
write!(
|
||||
f,
|
||||
"[fluent][resolver] errors in {}/{}: {}",
|
||||
locale.to_string(),
|
||||
id,
|
||||
errors.join(", ")
|
||||
)
|
||||
}
|
||||
Self::MissingMessage {
|
||||
id,
|
||||
locale: Some(locale),
|
||||
} => write!(f, "[fluent] Missing message in locale {}: {}", locale, id),
|
||||
Self::MissingMessage { id, locale: None } => {
|
||||
write!(f, "[fluent] Couldn't find a message: {}", id)
|
||||
}
|
||||
Self::MissingValue {
|
||||
id,
|
||||
locale: Some(locale),
|
||||
} => write!(
|
||||
f,
|
||||
"[fluent] Message has no value in locale {}: {}",
|
||||
locale, id
|
||||
),
|
||||
Self::MissingValue { id, locale: None } => {
|
||||
write!(f, "[fluent] Couldn't find a message with value: {}", id)
|
||||
}
|
||||
Self::SyncRequestInAsyncMode => {
|
||||
write!(f, "Triggered synchronous format while in async mode")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Error for LocalizationError {}
|
30
third_party/rust/fluent-fallback/src/generator.rs
vendored
Normal file
30
third_party/rust/fluent-fallback/src/generator.rs
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
use fluent_bundle::{FluentBundle, FluentError, FluentResource};
|
||||
use futures::Stream;
|
||||
use std::borrow::Borrow;
|
||||
use unic_langid::LanguageIdentifier;
|
||||
|
||||
pub type FluentBundleResult<R> = Result<FluentBundle<R>, (FluentBundle<R>, Vec<FluentError>)>;
|
||||
|
||||
pub trait BundleIterator {
|
||||
fn prefetch_sync(&mut self) {}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
pub trait BundleStream {
|
||||
async fn prefetch_async(&mut self) {}
|
||||
}
|
||||
|
||||
pub trait BundleGenerator {
|
||||
type Resource: Borrow<FluentResource>;
|
||||
type LocalesIter: Iterator<Item = LanguageIdentifier>;
|
||||
type Iter: Iterator<Item = FluentBundleResult<Self::Resource>>;
|
||||
type Stream: Stream<Item = FluentBundleResult<Self::Resource>>;
|
||||
|
||||
fn bundles_iter(&self, _locales: Self::LocalesIter, _res_ids: Vec<String>) -> Self::Iter {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
fn bundles_stream(&self, _locales: Self::LocalesIter, _res_ids: Vec<String>) -> Self::Stream {
|
||||
unimplemented!();
|
||||
}
|
||||
}
|
104
third_party/rust/fluent-fallback/src/lib.rs
vendored
Normal file
104
third_party/rust/fluent-fallback/src/lib.rs
vendored
Normal file
@ -0,0 +1,104 @@
|
||||
//! Fluent is a modern localization system designed to improve how software is translated.
|
||||
//!
|
||||
//! `fluent-fallback` is a high-level component of the [Fluent Localization
|
||||
//! System](https://www.projectfluent.org).
|
||||
//!
|
||||
//! The crate builds on top of the mid-level [`fluent-bundle`](../fluent-bundle) package, and provides an ergonomic API for highly flexible localization.
|
||||
//!
|
||||
//! The functionality of this level is complete, but the API itself is in the
|
||||
//! early stages and the goal of being ergonomic is yet to be achieved.
|
||||
//!
|
||||
//! If the user is willing to work through the challenge of setting up the
|
||||
//! boiler-plate that will eventually go away, `fluent-fallback` provides
|
||||
//! a powerful abstraction around [`FluentBundle`](fluent_bundle::FluentBundle) coupled
|
||||
//! with a localization resource management system.
|
||||
//!
|
||||
//! The main struct, [`Localization`], is a long-lived, reactive, multi-lingual
|
||||
//! struct which allows for strong error recovery and locale
|
||||
//! fallbacking, exposing synchronous and asynchronous ergonomic methods
|
||||
//! for [`L10nMessage`](types::L10nMessage) retrieval.
|
||||
//!
|
||||
//! [`Localization`] is also an API that is to be used when designing bindings
|
||||
//! to user interface systems, such as DOM, React, and others.
|
||||
//!
|
||||
//! # Example
|
||||
//!
|
||||
//! ```
|
||||
//! use fluent_fallback::Localization;
|
||||
//! use fluent_resmgr::ResourceManager;
|
||||
//! use unic_langid::langid;
|
||||
//!
|
||||
//! let res_mgr = ResourceManager::new("./tests/resources/{locale}/".to_string());
|
||||
//!
|
||||
//! let loc = Localization::with_env(
|
||||
//! vec![
|
||||
//! "test.ftl".to_string(),
|
||||
//! "test2.ftl".to_string()
|
||||
//! ],
|
||||
//! true,
|
||||
//! vec![langid!("en-US")],
|
||||
//! res_mgr,
|
||||
//! );
|
||||
//! let bundles = loc.bundles();
|
||||
//!
|
||||
//! let mut errors = vec![];
|
||||
//! let value = bundles.format_value_sync("hello-world", None, &mut errors)
|
||||
//! .expect("Failed to format a value");
|
||||
//!
|
||||
//! assert_eq!(value, Some("Hello World [en]".into()));
|
||||
//! ```
|
||||
//!
|
||||
//! The above example is far from the ergonomical API style the Fluent project
|
||||
//! is aiming for, but it represents the full scope of functionality intended
|
||||
//! for the model.
|
||||
//!
|
||||
//! # Resource Management
|
||||
//!
|
||||
//! Resource management is one of the most complicated parts of a localization system.
|
||||
//! In particular, modern software may have needs for both synchronous
|
||||
//! and asynchronous I/O. That, in turn has a large impact on what can happen
|
||||
//! in case of missing resources, or errors.
|
||||
//!
|
||||
//! Currently, [`Localization`] can be specialized over an implementation of
|
||||
//! [`generator::BundleGenerator`] trait which provides a method to generate an
|
||||
//! [`Iterator`] and [`Stream`](futures::stream::Stream).
|
||||
//!
|
||||
//! This is not very elegant and will likely be improved in the future, but for the time being, if
|
||||
//! the customer doesn't need one of the modes, the unnecessary method should use the
|
||||
//! `unimplemented!()` macro as its body.
|
||||
//!
|
||||
//! `fluent-resmgr` provides a simple resource manager which handles synchronous I/O
|
||||
//! and uses local file system to store resources in a directory structure.
|
||||
//!
|
||||
//! That model is often sufficient and the user can either use `fluent-resmgr` or write
|
||||
//! a similar API to provide the generator for [`Localization`].
|
||||
//!
|
||||
//! Alternatively, a much more sophisticated resource manager can be used. Mozilla
|
||||
//! for its needs in Firefox uses [`L10nRegistry`](https://github.com/zbraniecki/l10nregistry-rs)
|
||||
//! library which implements [`BundleGenerator`](generator::BundleGenerator).
|
||||
//!
|
||||
//! # Locale Management
|
||||
//!
|
||||
//! As a long lived structure, the [`Localization`] is intended to handle runtime locale
|
||||
//! management.
|
||||
//!
|
||||
//! In the example above, [`Vec<LagnuageIdentifier>`](unic_langid::LanguageIdentifier)
|
||||
//! provides a static list of locales that the [`Localization`] handles, but that's just the
|
||||
//! simplest implementation of the [`env::LocalesProvider`], and one can implement
|
||||
//! a much more sophisticated one that reacts to user or environment driven changes, and
|
||||
//! called [`Localization::on_change`] to trigger a new locales to be used for the
|
||||
//! next translation request.
|
||||
//!
|
||||
//! See [`env::LocalesProvider`] trait for an example of a reactive system implementation.
|
||||
mod bundles;
|
||||
mod cache;
|
||||
pub mod env;
|
||||
mod errors;
|
||||
pub mod generator;
|
||||
mod localization;
|
||||
mod pin_cell;
|
||||
pub mod types;
|
||||
|
||||
pub use bundles::Bundles;
|
||||
pub use errors::LocalizationError;
|
||||
pub use localization::Localization;
|
129
third_party/rust/fluent-fallback/src/localization.rs
vendored
Normal file
129
third_party/rust/fluent-fallback/src/localization.rs
vendored
Normal file
@ -0,0 +1,129 @@
|
||||
use crate::{
|
||||
bundles::Bundles,
|
||||
env::LocalesProvider,
|
||||
generator::{BundleGenerator, BundleIterator, BundleStream},
|
||||
};
|
||||
use once_cell::sync::OnceCell;
|
||||
use std::rc::Rc;
|
||||
|
||||
pub struct Localization<G, P>
|
||||
where
|
||||
G: BundleGenerator<LocalesIter = P::Iter>,
|
||||
P: LocalesProvider,
|
||||
{
|
||||
bundles: OnceCell<Rc<Bundles<G>>>,
|
||||
generator: G,
|
||||
provider: P,
|
||||
sync: bool,
|
||||
res_ids: Vec<String>,
|
||||
}
|
||||
|
||||
impl<G, P> Localization<G, P>
|
||||
where
|
||||
G: BundleGenerator<LocalesIter = P::Iter> + Default,
|
||||
P: LocalesProvider + Default,
|
||||
{
|
||||
pub fn new(res_ids: Vec<String>, sync: bool) -> Self {
|
||||
Self {
|
||||
bundles: OnceCell::new(),
|
||||
generator: G::default(),
|
||||
provider: P::default(),
|
||||
sync,
|
||||
res_ids,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<G, P> Localization<G, P>
|
||||
where
|
||||
G: BundleGenerator<LocalesIter = P::Iter>,
|
||||
P: LocalesProvider,
|
||||
{
|
||||
pub fn with_env(res_ids: Vec<String>, sync: bool, provider: P, generator: G) -> Self {
|
||||
Self {
|
||||
bundles: OnceCell::new(),
|
||||
generator,
|
||||
provider,
|
||||
sync,
|
||||
res_ids,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_sync(&self) -> bool {
|
||||
self.sync
|
||||
}
|
||||
|
||||
pub fn add_resource_id(&mut self, res_id: String) {
|
||||
self.res_ids.push(res_id);
|
||||
self.on_change();
|
||||
}
|
||||
|
||||
pub fn add_resource_ids(&mut self, res_ids: Vec<String>) {
|
||||
self.res_ids.extend(res_ids);
|
||||
self.on_change();
|
||||
}
|
||||
|
||||
pub fn remove_resource_id(&mut self, res_id: String) -> usize {
|
||||
self.res_ids.retain(|x| *x != res_id);
|
||||
self.on_change();
|
||||
self.res_ids.len()
|
||||
}
|
||||
|
||||
pub fn remove_resource_ids(&mut self, res_ids: Vec<String>) -> usize {
|
||||
self.res_ids.retain(|x| !res_ids.contains(x));
|
||||
self.on_change();
|
||||
self.res_ids.len()
|
||||
}
|
||||
|
||||
pub fn set_async(&mut self) {
|
||||
if self.sync {
|
||||
self.sync = false;
|
||||
self.on_change();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn on_change(&mut self) {
|
||||
self.bundles.take();
|
||||
}
|
||||
}
|
||||
|
||||
impl<G, P> Localization<G, P>
|
||||
where
|
||||
G: BundleGenerator<LocalesIter = P::Iter>,
|
||||
G::Iter: BundleIterator,
|
||||
P: LocalesProvider,
|
||||
{
|
||||
pub fn prefetch_sync(&mut self) {
|
||||
let bundles = self.bundles();
|
||||
bundles.prefetch_sync();
|
||||
}
|
||||
}
|
||||
|
||||
impl<G, P> Localization<G, P>
|
||||
where
|
||||
G: BundleGenerator<LocalesIter = P::Iter>,
|
||||
G::Stream: BundleStream,
|
||||
P: LocalesProvider,
|
||||
{
|
||||
pub async fn prefetch_async(&mut self) {
|
||||
let bundles = self.bundles();
|
||||
bundles.prefetch_async().await
|
||||
}
|
||||
}
|
||||
|
||||
impl<G, P> Localization<G, P>
|
||||
where
|
||||
G: BundleGenerator<LocalesIter = P::Iter>,
|
||||
P: LocalesProvider,
|
||||
{
|
||||
pub fn bundles(&self) -> &Rc<Bundles<G>> {
|
||||
self.bundles.get_or_init(|| {
|
||||
Rc::new(Bundles::new(
|
||||
self.sync,
|
||||
self.res_ids.clone(),
|
||||
&self.generator,
|
||||
&self.provider,
|
||||
))
|
||||
})
|
||||
}
|
||||
}
|
2
third_party/rust/fluent-fallback/src/pin_cell/README.md
vendored
Normal file
2
third_party/rust/fluent-fallback/src/pin_cell/README.md
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
This is a temporary fork of https://github.com/withoutboats/pin-cell until
|
||||
https://github.com/withoutboats/pin-cell/issues/6 gets resolved.
|
97
third_party/rust/fluent-fallback/src/pin_cell/mod.rs
vendored
Normal file
97
third_party/rust/fluent-fallback/src/pin_cell/mod.rs
vendored
Normal file
@ -0,0 +1,97 @@
|
||||
#![deny(missing_docs, missing_debug_implementations)]
|
||||
//! This library defines the `PinCell` type, a pinning variant of the standard
|
||||
//! library's `RefCell`.
|
||||
//!
|
||||
//! It is not safe to "pin project" through a `RefCell` - getting a pinned
|
||||
//! reference to something inside the `RefCell` when you have a pinned
|
||||
//! refernece to the `RefCell` - because `RefCell` is too powerful.
|
||||
//!
|
||||
//! A `PinCell` is slightly less powerful than `RefCell`: unlike a `RefCell`,
|
||||
//! one cannot get a mutable reference into a `PinCell`, only a pinned mutable
|
||||
//! reference (`Pin<&mut T>`). This makes pin projection safe, allowing you
|
||||
//! to use interior mutability with the knowledge that `T` will never actually
|
||||
//! be moved out of the `RefCell` that wraps it.
|
||||
|
||||
mod pin_mut;
|
||||
mod pin_ref;
|
||||
|
||||
use core::cell::{BorrowMutError, RefCell, RefMut};
|
||||
use core::pin::Pin;
|
||||
|
||||
pub use pin_mut::PinMut;
|
||||
pub use pin_ref::PinRef;
|
||||
|
||||
/// A mutable memory location with dynamically checked borrow rules
|
||||
///
|
||||
/// Unlike `RefCell`, this type only allows *pinned* mutable access to the
|
||||
/// inner value, enabling a "pin-safe" version of interior mutability.
|
||||
///
|
||||
/// See the standard library documentation for more information.
|
||||
#[derive(Default, Clone, Ord, PartialOrd, Eq, PartialEq, Debug)]
|
||||
pub struct PinCell<T: ?Sized> {
|
||||
inner: RefCell<T>,
|
||||
}
|
||||
|
||||
impl<T> PinCell<T> {
|
||||
/// Creates a new `PinCell` containing `value`.
|
||||
pub const fn new(value: T) -> PinCell<T> {
|
||||
PinCell {
|
||||
inner: RefCell::new(value),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized> PinCell<T> {
|
||||
/// Mutably borrows the wrapped value, preserving its pinnedness.
|
||||
///
|
||||
/// The borrow lasts until the returned `PinMut` or all `PinMut`s derived
|
||||
/// from it exit scope. The value cannot be borrowed while this borrow is
|
||||
/// active.
|
||||
pub fn borrow_mut(self: Pin<&Self>) -> PinMut<'_, T> {
|
||||
self.try_borrow_mut().expect("already borrowed")
|
||||
}
|
||||
|
||||
/// Mutably borrows the wrapped value, preserving its pinnedness,
|
||||
/// returning an error if the value is currently borrowed.
|
||||
///
|
||||
/// The borrow lasts until the returned `PinMut` or all `PinMut`s derived
|
||||
/// from it exit scope. The value cannot be borrowed while this borrow is
|
||||
/// active.
|
||||
///
|
||||
/// This is the non-panicking variant of `borrow_mut`.
|
||||
pub fn try_borrow_mut<'a>(self: Pin<&'a Self>) -> Result<PinMut<'a, T>, BorrowMutError> {
|
||||
let ref_mut: RefMut<'a, T> = Pin::get_ref(self).inner.try_borrow_mut()?;
|
||||
|
||||
// this is a pin projection from Pin<&PinCell<T>> to Pin<RefMut<T>>
|
||||
// projecting is safe because:
|
||||
//
|
||||
// - for<T: ?Sized> (PinCell<T>: Unpin) imples (RefMut<T>: Unpin)
|
||||
// holds true
|
||||
// - PinCell does not implement Drop
|
||||
//
|
||||
// see discussion on tracking issue #49150 about pin projection
|
||||
// invariants
|
||||
let pin_ref_mut: Pin<RefMut<'a, T>> = unsafe { Pin::new_unchecked(ref_mut) };
|
||||
|
||||
Ok(PinMut { inner: pin_ref_mut })
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<T> for PinCell<T> {
|
||||
fn from(value: T) -> PinCell<T> {
|
||||
PinCell::new(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<RefCell<T>> for PinCell<T> {
|
||||
fn from(cell: RefCell<T>) -> PinCell<T> {
|
||||
PinCell { inner: cell }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<PinCell<T>> for RefCell<T> {
|
||||
fn from(input: PinCell<T>) -> Self {
|
||||
input.inner
|
||||
}
|
||||
}
|
||||
// TODO CoerceUnsized
|
50
third_party/rust/fluent-fallback/src/pin_cell/pin_mut.rs
vendored
Normal file
50
third_party/rust/fluent-fallback/src/pin_cell/pin_mut.rs
vendored
Normal file
@ -0,0 +1,50 @@
|
||||
use core::cell::RefMut;
|
||||
use core::fmt;
|
||||
use core::ops::Deref;
|
||||
use core::pin::Pin;
|
||||
|
||||
#[derive(Debug)]
|
||||
/// A wrapper type for a mutably borrowed value from a `PinCell<T>`.
|
||||
pub struct PinMut<'a, T: ?Sized> {
|
||||
pub(crate) inner: Pin<RefMut<'a, T>>,
|
||||
}
|
||||
|
||||
impl<'a, T: ?Sized> Deref for PinMut<'a, T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &T {
|
||||
&*self.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: ?Sized> PinMut<'a, T> {
|
||||
/// Get a pinned mutable reference to the value inside this wrapper.
|
||||
pub fn as_mut<'b>(orig: &'b mut PinMut<'a, T>) -> Pin<&'b mut T> {
|
||||
orig.inner.as_mut()
|
||||
}
|
||||
}
|
||||
|
||||
/* TODO implement these APIs
|
||||
|
||||
impl<'a, T: ?Sized> PinMut<'a, T> {
|
||||
pub fn map<U, F>(orig: PinMut<'a, T>, f: F) -> PinMut<'a, U> where
|
||||
F: FnOnce(Pin<&mut T>) -> Pin<&mut U>,
|
||||
{
|
||||
panic!()
|
||||
}
|
||||
|
||||
pub fn map_split<U, V, F>(orig: PinMut<'a, T>, f: F) -> (PinMut<'a, U>, PinMut<'a, V>) where
|
||||
F: FnOnce(Pin<&mut T>) -> (Pin<&mut U>, Pin<&mut V>)
|
||||
{
|
||||
panic!()
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
impl<'a, T: fmt::Display + ?Sized> fmt::Display for PinMut<'a, T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
<T as fmt::Display>::fmt(&**self, f)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO CoerceUnsized
|
47
third_party/rust/fluent-fallback/src/pin_cell/pin_ref.rs
vendored
Normal file
47
third_party/rust/fluent-fallback/src/pin_cell/pin_ref.rs
vendored
Normal file
@ -0,0 +1,47 @@
|
||||
use core::cell::Ref;
|
||||
use core::fmt;
|
||||
use core::ops::Deref;
|
||||
use core::pin::Pin;
|
||||
|
||||
#[derive(Debug)]
|
||||
/// A wrapper type for a immutably borrowed value from a `PinCell<T>`.
|
||||
pub struct PinRef<'a, T: ?Sized> {
|
||||
pub(crate) inner: Pin<Ref<'a, T>>,
|
||||
}
|
||||
|
||||
impl<'a, T: ?Sized> Deref for PinRef<'a, T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &T {
|
||||
&*self.inner
|
||||
}
|
||||
}
|
||||
|
||||
/* TODO implement these APIs
|
||||
|
||||
impl<'a, T: ?Sized> PinRef<'a, T> {
|
||||
pub fn clone(orig: &PinRef<'a, T>) -> PinRef<'a, T> {
|
||||
panic!()
|
||||
}
|
||||
|
||||
pub fn map<U, F>(orig: PinRef<'a, T>, f: F) -> PinRef<'a, U> where
|
||||
F: FnOnce(Pin<&T>) -> Pin<&U>,
|
||||
{
|
||||
panic!()
|
||||
}
|
||||
|
||||
pub fn map_split<U, V, F>(orig: PinRef<'a, T>, f: F) -> (PinRef<'a, U>, PinRef<'a, V>) where
|
||||
F: FnOnce(Pin<&T>) -> (Pin<&U>, Pin<&V>)
|
||||
{
|
||||
panic!()
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
impl<'a, T: fmt::Display + ?Sized> fmt::Display for PinRef<'a, T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
<T as fmt::Display>::fmt(&**self, f)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO CoerceUnsized
|
29
third_party/rust/fluent-fallback/src/types.rs
vendored
Normal file
29
third_party/rust/fluent-fallback/src/types.rs
vendored
Normal file
@ -0,0 +1,29 @@
|
||||
use fluent_bundle::FluentArgs;
|
||||
use std::borrow::Cow;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct L10nKey<'l> {
|
||||
pub id: Cow<'l, str>,
|
||||
pub args: Option<FluentArgs<'l>>,
|
||||
}
|
||||
|
||||
impl<'l> From<&'l str> for L10nKey<'l> {
|
||||
fn from(id: &'l str) -> Self {
|
||||
Self {
|
||||
id: id.into(),
|
||||
args: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct L10nAttribute<'l> {
|
||||
pub name: Cow<'l, str>,
|
||||
pub value: Cow<'l, str>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct L10nMessage<'l> {
|
||||
pub value: Option<Cow<'l, str>>,
|
||||
pub attributes: Vec<L10nAttribute<'l>>,
|
||||
}
|
490
third_party/rust/fluent-fallback/tests/localization_test.rs
vendored
Normal file
490
third_party/rust/fluent-fallback/tests/localization_test.rs
vendored
Normal file
@ -0,0 +1,490 @@
|
||||
use std::borrow::Cow;
|
||||
use std::fs;
|
||||
|
||||
use fluent_bundle::{
|
||||
resolver::errors::{ReferenceKind, ResolverError},
|
||||
FluentArgs, FluentBundle, FluentError, FluentResource,
|
||||
};
|
||||
use fluent_fallback::{
|
||||
env::LocalesProvider,
|
||||
generator::{BundleGenerator, FluentBundleResult},
|
||||
types::L10nKey,
|
||||
Localization, LocalizationError,
|
||||
};
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
use unic_langid::{langid, LanguageIdentifier};
|
||||
|
||||
struct InnerLocales {
|
||||
locales: RefCell<Vec<LanguageIdentifier>>,
|
||||
}
|
||||
|
||||
impl InnerLocales {
|
||||
pub fn insert(&self, index: usize, element: LanguageIdentifier) {
|
||||
self.locales.borrow_mut().insert(index, element);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
struct Locales {
|
||||
inner: Rc<InnerLocales>,
|
||||
}
|
||||
|
||||
impl Locales {
|
||||
pub fn new(locales: Vec<LanguageIdentifier>) -> Self {
|
||||
Self {
|
||||
inner: Rc::new(InnerLocales {
|
||||
locales: RefCell::new(locales),
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn insert(&mut self, index: usize, element: LanguageIdentifier) {
|
||||
self.inner.insert(index, element);
|
||||
}
|
||||
}
|
||||
|
||||
impl LocalesProvider for Locales {
|
||||
type Iter = <Vec<LanguageIdentifier> as IntoIterator>::IntoIter;
|
||||
fn locales(&self) -> Self::Iter {
|
||||
self.inner.locales.borrow().clone().into_iter()
|
||||
}
|
||||
}
|
||||
|
||||
// Due to limitation of trait, we need a nameable Iterator type. Due to the
|
||||
// lack of GATs, these have to own members instead of taking slices.
|
||||
struct BundleIter {
|
||||
locales: <Vec<LanguageIdentifier> as IntoIterator>::IntoIter,
|
||||
res_ids: Vec<String>,
|
||||
}
|
||||
|
||||
impl Iterator for BundleIter {
|
||||
type Item = FluentBundleResult<FluentResource>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let locale = self.locales.next()?;
|
||||
|
||||
let mut bundle = FluentBundle::new(vec![locale.clone()]);
|
||||
bundle.set_use_isolating(false);
|
||||
|
||||
let mut errors = vec![];
|
||||
|
||||
for res_id in &self.res_ids {
|
||||
let full_path = format!("./tests/resources/{}/{}", locale, res_id);
|
||||
let source = fs::read_to_string(full_path).unwrap();
|
||||
let res = match FluentResource::try_new(source) {
|
||||
Ok(res) => res,
|
||||
Err((res, err)) => {
|
||||
errors.extend(err.into_iter().map(Into::into));
|
||||
res
|
||||
}
|
||||
};
|
||||
bundle.add_resource(res).unwrap();
|
||||
}
|
||||
if errors.is_empty() {
|
||||
Some(Ok(bundle))
|
||||
} else {
|
||||
Some(Err((bundle, errors)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl futures::Stream for BundleIter {
|
||||
type Item = FluentBundleResult<FluentResource>;
|
||||
|
||||
fn poll_next(
|
||||
mut self: std::pin::Pin<&mut Self>,
|
||||
_cx: &mut std::task::Context<'_>,
|
||||
) -> std::task::Poll<Option<Self::Item>> {
|
||||
if let Some(locale) = self.locales.next() {
|
||||
let mut bundle = FluentBundle::new(vec![locale.clone()]);
|
||||
bundle.set_use_isolating(false);
|
||||
|
||||
let mut errors = vec![];
|
||||
for res_id in &self.res_ids {
|
||||
let full_path = format!("./tests/resources/{}/{}", locale, res_id);
|
||||
let source = fs::read_to_string(full_path).unwrap();
|
||||
let res = match FluentResource::try_new(source) {
|
||||
Ok(res) => res,
|
||||
Err((res, err)) => {
|
||||
errors.extend(err.into_iter().map(Into::into));
|
||||
res
|
||||
}
|
||||
};
|
||||
bundle.add_resource(res).unwrap();
|
||||
}
|
||||
if errors.is_empty() {
|
||||
Some(Ok(bundle)).into()
|
||||
} else {
|
||||
Some(Err((bundle, errors))).into()
|
||||
}
|
||||
} else {
|
||||
None.into()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct ResourceManager;
|
||||
|
||||
impl BundleGenerator for ResourceManager {
|
||||
type Resource = FluentResource;
|
||||
type LocalesIter = std::vec::IntoIter<LanguageIdentifier>;
|
||||
type Iter = BundleIter;
|
||||
type Stream = BundleIter;
|
||||
|
||||
fn bundles_iter(&self, locales: Self::LocalesIter, res_ids: Vec<String>) -> Self::Iter {
|
||||
BundleIter { locales, res_ids }
|
||||
}
|
||||
|
||||
fn bundles_stream(&self, locales: Self::LocalesIter, res_ids: Vec<String>) -> Self::Stream {
|
||||
BundleIter { locales, res_ids }
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn localization_format() {
|
||||
let resource_ids: Vec<String> = vec!["test.ftl".into(), "test2.ftl".into()];
|
||||
let locales = Locales::new(vec![langid!("pl"), langid!("en-US")]);
|
||||
let res_mgr = ResourceManager;
|
||||
let mut errors = vec![];
|
||||
|
||||
let loc = Localization::with_env(resource_ids, true, locales, res_mgr);
|
||||
let bundles = loc.bundles();
|
||||
|
||||
let value = bundles
|
||||
.format_value_sync("hello-world", None, &mut errors)
|
||||
.unwrap();
|
||||
assert_eq!(value, Some(Cow::Borrowed("Hello World [pl]")));
|
||||
|
||||
let value = bundles
|
||||
.format_value_sync("missing-message", None, &mut errors)
|
||||
.unwrap();
|
||||
assert_eq!(value, None);
|
||||
|
||||
let value = bundles
|
||||
.format_value_sync("hello-world-3", None, &mut errors)
|
||||
.unwrap();
|
||||
assert_eq!(value, Some(Cow::Borrowed("Hello World 3 [en]")));
|
||||
|
||||
assert_eq!(errors.len(), 4);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn localization_on_change() {
|
||||
let resource_ids: Vec<String> = vec!["test.ftl".into(), "test2.ftl".into()];
|
||||
|
||||
let mut locales = Locales::new(vec![langid!("en-US")]);
|
||||
let res_mgr = ResourceManager;
|
||||
let mut errors = vec![];
|
||||
|
||||
let mut loc = Localization::with_env(resource_ids, true, locales.clone(), res_mgr);
|
||||
let bundles = loc.bundles();
|
||||
|
||||
let value = bundles
|
||||
.format_value_sync("hello-world", None, &mut errors)
|
||||
.unwrap();
|
||||
assert_eq!(value, Some(Cow::Borrowed("Hello World [en]")));
|
||||
|
||||
locales.insert(0, langid!("pl"));
|
||||
loc.on_change();
|
||||
|
||||
let bundles = loc.bundles();
|
||||
let value = bundles
|
||||
.format_value_sync("hello-world", None, &mut errors)
|
||||
.unwrap();
|
||||
assert_eq!(value, Some(Cow::Borrowed("Hello World [pl]")));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn localization_format_value_missing_errors() {
|
||||
let resource_ids: Vec<String> = vec!["test.ftl".into(), "test2.ftl".into()];
|
||||
|
||||
let locales = Locales::new(vec![langid!("pl"), langid!("en-US")]);
|
||||
let res_mgr = ResourceManager;
|
||||
let mut errors = vec![];
|
||||
|
||||
let loc = Localization::with_env(resource_ids, true, locales.clone(), res_mgr);
|
||||
let bundles = loc.bundles();
|
||||
|
||||
let _ = bundles
|
||||
.format_value_sync("missing-message", None, &mut errors)
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
errors,
|
||||
vec![
|
||||
LocalizationError::MissingMessage {
|
||||
id: "missing-message".to_string(),
|
||||
locale: Some(langid!("pl"))
|
||||
},
|
||||
LocalizationError::MissingMessage {
|
||||
id: "missing-message".to_string(),
|
||||
locale: Some(langid!("en-US"))
|
||||
},
|
||||
LocalizationError::MissingMessage {
|
||||
id: "missing-message".to_string(),
|
||||
locale: None
|
||||
},
|
||||
]
|
||||
);
|
||||
|
||||
errors.clear();
|
||||
|
||||
let _ = bundles
|
||||
.format_value_sync("message-3", None, &mut errors)
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
errors,
|
||||
vec![
|
||||
LocalizationError::MissingValue {
|
||||
id: "message-3".to_string(),
|
||||
locale: Some(langid!("pl"))
|
||||
},
|
||||
LocalizationError::MissingValue {
|
||||
id: "message-3".to_string(),
|
||||
locale: Some(langid!("en-US"))
|
||||
},
|
||||
LocalizationError::MissingValue {
|
||||
id: "message-3".to_string(),
|
||||
locale: None
|
||||
},
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn localization_format_value_sync_missing_errors() {
|
||||
let resource_ids: Vec<String> = vec!["test.ftl".into(), "test2.ftl".into()];
|
||||
|
||||
let locales = Locales::new(vec![langid!("pl"), langid!("en-US")]);
|
||||
let res_mgr = ResourceManager;
|
||||
let mut errors = vec![];
|
||||
|
||||
let loc = Localization::with_env(resource_ids, true, locales.clone(), res_mgr);
|
||||
let bundles = loc.bundles();
|
||||
|
||||
let _ = bundles
|
||||
.format_value_sync("missing-message", None, &mut errors)
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
errors,
|
||||
vec![
|
||||
LocalizationError::MissingMessage {
|
||||
id: "missing-message".to_string(),
|
||||
locale: Some(langid!("pl"))
|
||||
},
|
||||
LocalizationError::MissingMessage {
|
||||
id: "missing-message".to_string(),
|
||||
locale: Some(langid!("en-US"))
|
||||
},
|
||||
LocalizationError::MissingMessage {
|
||||
id: "missing-message".to_string(),
|
||||
locale: None
|
||||
},
|
||||
]
|
||||
);
|
||||
|
||||
errors.clear();
|
||||
|
||||
let _ = bundles
|
||||
.format_value_sync("message-3", None, &mut errors)
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
errors,
|
||||
vec![
|
||||
LocalizationError::MissingValue {
|
||||
id: "message-3".to_string(),
|
||||
locale: Some(langid!("pl"))
|
||||
},
|
||||
LocalizationError::MissingValue {
|
||||
id: "message-3".to_string(),
|
||||
locale: Some(langid!("en-US"))
|
||||
},
|
||||
LocalizationError::MissingValue {
|
||||
id: "message-3".to_string(),
|
||||
locale: None
|
||||
},
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn localization_format_values_sync_missing_errors() {
|
||||
let resource_ids: Vec<String> = vec!["test.ftl".into(), "test2.ftl".into()];
|
||||
|
||||
let locales = Locales::new(vec![langid!("pl"), langid!("en-US")]);
|
||||
let res_mgr = ResourceManager;
|
||||
let mut errors = vec![];
|
||||
|
||||
let loc = Localization::with_env(resource_ids, true, locales.clone(), res_mgr);
|
||||
let bundles = loc.bundles();
|
||||
|
||||
let _ = bundles
|
||||
.format_values_sync(
|
||||
&["missing-message".into(), "missing-message-2".into()],
|
||||
&mut errors,
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
errors,
|
||||
vec![
|
||||
LocalizationError::MissingMessage {
|
||||
id: "missing-message".to_string(),
|
||||
locale: Some(langid!("pl"))
|
||||
},
|
||||
LocalizationError::MissingMessage {
|
||||
id: "missing-message-2".to_string(),
|
||||
locale: Some(langid!("pl"))
|
||||
},
|
||||
LocalizationError::MissingMessage {
|
||||
id: "missing-message".to_string(),
|
||||
locale: Some(langid!("en-US"))
|
||||
},
|
||||
LocalizationError::MissingMessage {
|
||||
id: "missing-message-2".to_string(),
|
||||
locale: Some(langid!("en-US"))
|
||||
},
|
||||
LocalizationError::MissingMessage {
|
||||
id: "missing-message".to_string(),
|
||||
locale: None
|
||||
},
|
||||
LocalizationError::MissingMessage {
|
||||
id: "missing-message-2".to_string(),
|
||||
locale: None
|
||||
},
|
||||
]
|
||||
);
|
||||
|
||||
errors.clear();
|
||||
|
||||
let _ = bundles
|
||||
.format_values_sync(&["message-3".into()], &mut errors)
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
errors,
|
||||
vec![
|
||||
LocalizationError::MissingValue {
|
||||
id: "message-3".to_string(),
|
||||
locale: Some(langid!("pl"))
|
||||
},
|
||||
LocalizationError::MissingValue {
|
||||
id: "message-3".to_string(),
|
||||
locale: Some(langid!("en-US"))
|
||||
},
|
||||
LocalizationError::MissingValue {
|
||||
id: "message-3".to_string(),
|
||||
locale: None
|
||||
},
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn localization_format_messages_sync_missing_errors() {
|
||||
let resource_ids: Vec<String> = vec!["test.ftl".into(), "test2.ftl".into()];
|
||||
|
||||
let locales = Locales::new(vec![langid!("pl"), langid!("en-US")]);
|
||||
let res_mgr = ResourceManager;
|
||||
let mut errors = vec![];
|
||||
|
||||
let loc = Localization::with_env(resource_ids, true, locales.clone(), res_mgr);
|
||||
let bundles = loc.bundles();
|
||||
|
||||
let _ = bundles
|
||||
.format_messages_sync(
|
||||
&["missing-message".into(), "missing-message-2".into()],
|
||||
&mut errors,
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
errors,
|
||||
vec![
|
||||
LocalizationError::MissingMessage {
|
||||
id: "missing-message".to_string(),
|
||||
locale: Some(langid!("pl"))
|
||||
},
|
||||
LocalizationError::MissingMessage {
|
||||
id: "missing-message-2".to_string(),
|
||||
locale: Some(langid!("pl"))
|
||||
},
|
||||
LocalizationError::MissingMessage {
|
||||
id: "missing-message".to_string(),
|
||||
locale: Some(langid!("en-US"))
|
||||
},
|
||||
LocalizationError::MissingMessage {
|
||||
id: "missing-message-2".to_string(),
|
||||
locale: Some(langid!("en-US"))
|
||||
},
|
||||
LocalizationError::MissingMessage {
|
||||
id: "missing-message".to_string(),
|
||||
locale: None
|
||||
},
|
||||
LocalizationError::MissingMessage {
|
||||
id: "missing-message-2".to_string(),
|
||||
locale: None
|
||||
},
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn localization_format_missing_argument_error() {
|
||||
let resource_ids: Vec<String> = vec!["test2.ftl".into()];
|
||||
let locales = Locales::new(vec![langid!("en-US")]);
|
||||
let res_mgr = ResourceManager;
|
||||
let mut errors = vec![];
|
||||
|
||||
let loc = Localization::with_env(resource_ids, true, locales, res_mgr);
|
||||
let bundles = loc.bundles();
|
||||
|
||||
let mut args = FluentArgs::new();
|
||||
args.set("userName", "John");
|
||||
let keys = vec![L10nKey {
|
||||
id: "message-4".into(),
|
||||
args: Some(args),
|
||||
}];
|
||||
|
||||
let msgs = bundles.format_messages_sync(&keys, &mut errors).unwrap();
|
||||
assert_eq!(
|
||||
msgs.get(0).unwrap().as_ref().unwrap().value,
|
||||
Some(Cow::Borrowed("Hello, John. [en]"))
|
||||
);
|
||||
assert_eq!(errors.len(), 0);
|
||||
|
||||
let keys = vec![L10nKey {
|
||||
id: "message-4".into(),
|
||||
args: None,
|
||||
}];
|
||||
let msgs = bundles.format_messages_sync(&keys, &mut errors).unwrap();
|
||||
assert_eq!(
|
||||
msgs.get(0).unwrap().as_ref().unwrap().value,
|
||||
Some(Cow::Borrowed("Hello, {$userName}. [en]"))
|
||||
);
|
||||
assert_eq!(
|
||||
errors,
|
||||
vec![LocalizationError::Resolver {
|
||||
id: "message-4".to_string(),
|
||||
locale: langid!("en-US"),
|
||||
errors: vec![FluentError::ResolverError(ResolverError::Reference(
|
||||
ReferenceKind::Variable {
|
||||
id: "userName".to_string(),
|
||||
}
|
||||
))],
|
||||
},]
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn localization_handle_state_changes_mid_async() {
|
||||
let resource_ids: Vec<String> = vec!["test.ftl".into()];
|
||||
let locales = Locales::new(vec![langid!("en-US")]);
|
||||
let res_mgr = ResourceManager;
|
||||
let mut errors = vec![];
|
||||
|
||||
let mut loc = Localization::with_env(resource_ids, false, locales, res_mgr);
|
||||
|
||||
let bundles = loc.bundles().clone();
|
||||
|
||||
loc.add_resource_id("test2.ftl".to_string());
|
||||
|
||||
bundles.format_value("key", None, &mut errors).await;
|
||||
}
|
4
third_party/rust/fluent-fallback/tests/resources/en-US/test.ftl
vendored
Normal file
4
third_party/rust/fluent-fallback/tests/resources/en-US/test.ftl
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
hello-world = Hello World [en]
|
||||
|
||||
message-1 = Message 1 Value [en]
|
||||
.attr1 = Message 1 Attribute [en]
|
10
third_party/rust/fluent-fallback/tests/resources/en-US/test2.ftl
vendored
Normal file
10
third_party/rust/fluent-fallback/tests/resources/en-US/test2.ftl
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
hello-world-2 = Hello World 2 [en]
|
||||
hello-world-3 = Hello World 3 [en]
|
||||
|
||||
message-2 = Message 2 Value [en]
|
||||
.attr1 = Message 2 Attribute [en]
|
||||
|
||||
message-3 =
|
||||
.attr1 = Message 3 Attribute [en]
|
||||
|
||||
message-4 = Hello, { $userName }. [en]
|
4
third_party/rust/fluent-fallback/tests/resources/pl/test.ftl
vendored
Normal file
4
third_party/rust/fluent-fallback/tests/resources/pl/test.ftl
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
hello-world = Hello World [pl]
|
||||
|
||||
message-1 = Message 1 Value [pl]
|
||||
.attr1 = Message 1 Attribute [pl]
|
9
third_party/rust/fluent-fallback/tests/resources/pl/test2.ftl
vendored
Normal file
9
third_party/rust/fluent-fallback/tests/resources/pl/test2.ftl
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
hello-world-2 = Hello World 2 [pl]
|
||||
|
||||
message-2 = Message 2 Value [pl]
|
||||
.attr1 = Message 2 Attribute [pl]
|
||||
|
||||
message-3 =
|
||||
.attr1 = Message 3 Attribute [pl]
|
||||
|
||||
message-4 = Hello, { $userName }. [pl]
|
1
third_party/rust/futures-executor/.cargo-checksum.json
vendored
Normal file
1
third_party/rust/futures-executor/.cargo-checksum.json
vendored
Normal file
@ -0,0 +1 @@
|
||||
{"files":{"Cargo.toml":"e6cacb38174963e31cebfcee26619efa118915bac81086756fd474d563101d68","LICENSE-APACHE":"275c491d6d1160553c32fd6127061d7f9606c3ea25abfad6ca3f6ed088785427","LICENSE-MIT":"6652c868f35dfe5e8ef636810a4e576b9d663f3a17fb0f5613ad73583e1b88fd","benches/thread_notify.rs":"e601968527bee85766f32d2d11de5ed8f6b4bd5a29989b5c369a52bd3cd3d024","src/enter.rs":"c1a771f373b469d98e2599d8e37da7d7a7083c30332d643f37867f86406ab1e2","src/lib.rs":"0ceeed35d70e3d890ad71d1ac691d372dca3a6e180949b50b261aecbfbd9f697","src/local_pool.rs":"1661a58468491d714a358b6382df88bbd7557e19506009763f841cbcf85781f5","src/thread_pool.rs":"9667ce8e99fb6a08675a238aea6c33a4b2c9a94f6a42459e56a6b8cee84b6804","src/unpark_mutex.rs":"e186464d9bdec22a6d1e1d900ed03a1154e6b0d422ede9bd3b768657cdbb6113","tests/local_pool.rs":"d12d2f6240ec6ab1b2e189cd8aac9548249ca66ccdb5f3b142e237b320f812c4"},"package":"badaa6a909fac9e7236d0620a2f57f7664640c56575b71a7552fbd68deafab79"}
|
47
third_party/rust/futures-executor/Cargo.toml
vendored
Normal file
47
third_party/rust/futures-executor/Cargo.toml
vendored
Normal file
@ -0,0 +1,47 @@
|
||||
# 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 believe there's an error in this file please file an
|
||||
# issue against the rust-lang/cargo repository. If you're
|
||||
# editing this file be aware that the upstream Cargo.toml
|
||||
# will likely look very different (and much more reasonable)
|
||||
|
||||
[package]
|
||||
edition = "2018"
|
||||
name = "futures-executor"
|
||||
version = "0.3.15"
|
||||
authors = ["Alex Crichton <alex@alexcrichton.com>"]
|
||||
description = "Executors for asynchronous tasks based on the futures-rs library.\n"
|
||||
homepage = "https://rust-lang.github.io/futures-rs"
|
||||
documentation = "https://docs.rs/futures-executor/0.3"
|
||||
license = "MIT OR Apache-2.0"
|
||||
repository = "https://github.com/rust-lang/futures-rs"
|
||||
[package.metadata.docs.rs]
|
||||
all-features = true
|
||||
rustdoc-args = ["--cfg", "docsrs"]
|
||||
[dependencies.futures-core]
|
||||
version = "0.3.15"
|
||||
default-features = false
|
||||
|
||||
[dependencies.futures-task]
|
||||
version = "0.3.15"
|
||||
default-features = false
|
||||
|
||||
[dependencies.futures-util]
|
||||
version = "0.3.15"
|
||||
default-features = false
|
||||
|
||||
[dependencies.num_cpus]
|
||||
version = "1.8.0"
|
||||
optional = true
|
||||
|
||||
[dev-dependencies]
|
||||
|
||||
[features]
|
||||
default = ["std"]
|
||||
std = ["futures-core/std", "futures-task/std", "futures-util/std"]
|
||||
thread-pool = ["std", "num_cpus"]
|
202
third_party/rust/futures-executor/LICENSE-APACHE
vendored
Normal file
202
third_party/rust/futures-executor/LICENSE-APACHE
vendored
Normal file
@ -0,0 +1,202 @@
|
||||
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 (c) 2016 Alex Crichton
|
||||
Copyright (c) 2017 The Tokio Authors
|
||||
|
||||
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.
|
26
third_party/rust/futures-executor/LICENSE-MIT
vendored
Normal file
26
third_party/rust/futures-executor/LICENSE-MIT
vendored
Normal file
@ -0,0 +1,26 @@
|
||||
Copyright (c) 2016 Alex Crichton
|
||||
Copyright (c) 2017 The Tokio Authors
|
||||
|
||||
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.
|
109
third_party/rust/futures-executor/benches/thread_notify.rs
vendored
Normal file
109
third_party/rust/futures-executor/benches/thread_notify.rs
vendored
Normal file
@ -0,0 +1,109 @@
|
||||
#![feature(test)]
|
||||
|
||||
extern crate test;
|
||||
use crate::test::Bencher;
|
||||
|
||||
use futures::executor::block_on;
|
||||
use futures::future::Future;
|
||||
use futures::task::{Context, Poll, Waker};
|
||||
use std::pin::Pin;
|
||||
|
||||
#[bench]
|
||||
fn thread_yield_single_thread_one_wait(b: &mut Bencher) {
|
||||
const NUM: usize = 10_000;
|
||||
|
||||
struct Yield {
|
||||
rem: usize,
|
||||
}
|
||||
|
||||
impl Future for Yield {
|
||||
type Output = ();
|
||||
|
||||
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
if self.rem == 0 {
|
||||
Poll::Ready(())
|
||||
} else {
|
||||
self.rem -= 1;
|
||||
cx.waker().wake_by_ref();
|
||||
Poll::Pending
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
b.iter(|| {
|
||||
let y = Yield { rem: NUM };
|
||||
block_on(y);
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn thread_yield_single_thread_many_wait(b: &mut Bencher) {
|
||||
const NUM: usize = 10_000;
|
||||
|
||||
struct Yield {
|
||||
rem: usize,
|
||||
}
|
||||
|
||||
impl Future for Yield {
|
||||
type Output = ();
|
||||
|
||||
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
if self.rem == 0 {
|
||||
Poll::Ready(())
|
||||
} else {
|
||||
self.rem -= 1;
|
||||
cx.waker().wake_by_ref();
|
||||
Poll::Pending
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
b.iter(|| {
|
||||
for _ in 0..NUM {
|
||||
let y = Yield { rem: 1 };
|
||||
block_on(y);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn thread_yield_multi_thread(b: &mut Bencher) {
|
||||
use std::sync::mpsc;
|
||||
use std::thread;
|
||||
|
||||
const NUM: usize = 1_000;
|
||||
|
||||
let (tx, rx) = mpsc::sync_channel::<Waker>(10_000);
|
||||
|
||||
struct Yield {
|
||||
rem: usize,
|
||||
tx: mpsc::SyncSender<Waker>,
|
||||
}
|
||||
impl Unpin for Yield {}
|
||||
|
||||
impl Future for Yield {
|
||||
type Output = ();
|
||||
|
||||
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
if self.rem == 0 {
|
||||
Poll::Ready(())
|
||||
} else {
|
||||
self.rem -= 1;
|
||||
self.tx.send(cx.waker().clone()).unwrap();
|
||||
Poll::Pending
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
thread::spawn(move || {
|
||||
while let Ok(task) = rx.recv() {
|
||||
task.wake();
|
||||
}
|
||||
});
|
||||
|
||||
b.iter(move || {
|
||||
let y = Yield { rem: NUM, tx: tx.clone() };
|
||||
|
||||
block_on(y);
|
||||
});
|
||||
}
|
80
third_party/rust/futures-executor/src/enter.rs
vendored
Normal file
80
third_party/rust/futures-executor/src/enter.rs
vendored
Normal file
@ -0,0 +1,80 @@
|
||||
use std::cell::Cell;
|
||||
use std::fmt;
|
||||
|
||||
thread_local!(static ENTERED: Cell<bool> = Cell::new(false));
|
||||
|
||||
/// Represents an executor context.
|
||||
///
|
||||
/// For more details, see [`enter` documentation](enter()).
|
||||
pub struct Enter {
|
||||
_priv: (),
|
||||
}
|
||||
|
||||
/// An error returned by `enter` if an execution scope has already been
|
||||
/// entered.
|
||||
pub struct EnterError {
|
||||
_priv: (),
|
||||
}
|
||||
|
||||
impl fmt::Debug for EnterError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("EnterError").finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for EnterError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "an execution scope has already been entered")
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for EnterError {}
|
||||
|
||||
/// Marks the current thread as being within the dynamic extent of an
|
||||
/// executor.
|
||||
///
|
||||
/// Executor implementations should call this function before beginning to
|
||||
/// execute a tasks, and drop the returned [`Enter`](Enter) value after
|
||||
/// completing task execution:
|
||||
///
|
||||
/// ```
|
||||
/// use futures::executor::enter;
|
||||
///
|
||||
/// let enter = enter().expect("...");
|
||||
/// /* run task */
|
||||
/// drop(enter);
|
||||
/// ```
|
||||
///
|
||||
/// Doing so ensures that executors aren't
|
||||
/// accidentally invoked in a nested fashion.
|
||||
///
|
||||
/// # Error
|
||||
///
|
||||
/// Returns an error if the current thread is already marked, in which case the
|
||||
/// caller should panic with a tailored error message.
|
||||
pub fn enter() -> Result<Enter, EnterError> {
|
||||
ENTERED.with(|c| {
|
||||
if c.get() {
|
||||
Err(EnterError { _priv: () })
|
||||
} else {
|
||||
c.set(true);
|
||||
|
||||
Ok(Enter { _priv: () })
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
impl fmt::Debug for Enter {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("Enter").finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Enter {
|
||||
fn drop(&mut self) {
|
||||
ENTERED.with(|c| {
|
||||
assert!(c.get());
|
||||
c.set(false);
|
||||
});
|
||||
}
|
||||
}
|
67
third_party/rust/futures-executor/src/lib.rs
vendored
Normal file
67
third_party/rust/futures-executor/src/lib.rs
vendored
Normal file
@ -0,0 +1,67 @@
|
||||
//! Built-in executors and related tools.
|
||||
//!
|
||||
//! All asynchronous computation occurs within an executor, which is
|
||||
//! capable of spawning futures as tasks. This module provides several
|
||||
//! built-in executors, as well as tools for building your own.
|
||||
//!
|
||||
//! All items are only available when the `std` feature of this
|
||||
//! library is activated, and it is activated by default.
|
||||
//!
|
||||
//! # Using a thread pool (M:N task scheduling)
|
||||
//!
|
||||
//! Most of the time tasks should be executed on a [thread pool](ThreadPool).
|
||||
//! A small set of worker threads can handle a very large set of spawned tasks
|
||||
//! (which are much lighter weight than threads). Tasks spawned onto the pool
|
||||
//! with the [`spawn_ok`](ThreadPool::spawn_ok) function will run ambiently on
|
||||
//! the created threads.
|
||||
//!
|
||||
//! # Spawning additional tasks
|
||||
//!
|
||||
//! Tasks can be spawned onto a spawner by calling its [`spawn_obj`] method
|
||||
//! directly. In the case of `!Send` futures, [`spawn_local_obj`] can be used
|
||||
//! instead.
|
||||
//!
|
||||
//! # Single-threaded execution
|
||||
//!
|
||||
//! In addition to thread pools, it's possible to run a task (and the tasks
|
||||
//! it spawns) entirely within a single thread via the [`LocalPool`] executor.
|
||||
//! Aside from cutting down on synchronization costs, this executor also makes
|
||||
//! it possible to spawn non-`Send` tasks, via [`spawn_local_obj`]. The
|
||||
//! [`LocalPool`] is best suited for running I/O-bound tasks that do relatively
|
||||
//! little work between I/O operations.
|
||||
//!
|
||||
//! There is also a convenience function [`block_on`] for simply running a
|
||||
//! future to completion on the current thread.
|
||||
//!
|
||||
//! [`spawn_obj`]: https://docs.rs/futures/0.3/futures/task/trait.Spawn.html#tymethod.spawn_obj
|
||||
//! [`spawn_local_obj`]: https://docs.rs/futures/0.3/futures/task/trait.LocalSpawn.html#tymethod.spawn_local_obj
|
||||
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
#![warn(missing_docs, missing_debug_implementations, rust_2018_idioms, unreachable_pub)]
|
||||
// It cannot be included in the published code because this lints have false positives in the minimum required version.
|
||||
#![cfg_attr(test, warn(single_use_lifetimes))]
|
||||
#![warn(clippy::all)]
|
||||
#![doc(test(attr(deny(warnings), allow(dead_code, unused_assignments, unused_variables))))]
|
||||
#![cfg_attr(docsrs, feature(doc_cfg))]
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
mod local_pool;
|
||||
#[cfg(feature = "std")]
|
||||
pub use crate::local_pool::{block_on, block_on_stream, BlockingStream, LocalPool, LocalSpawner};
|
||||
|
||||
#[cfg(feature = "thread-pool")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "thread-pool")))]
|
||||
#[cfg(feature = "std")]
|
||||
mod thread_pool;
|
||||
#[cfg(feature = "thread-pool")]
|
||||
#[cfg(feature = "std")]
|
||||
mod unpark_mutex;
|
||||
#[cfg(feature = "thread-pool")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "thread-pool")))]
|
||||
#[cfg(feature = "std")]
|
||||
pub use crate::thread_pool::{ThreadPool, ThreadPoolBuilder};
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
mod enter;
|
||||
#[cfg(feature = "std")]
|
||||
pub use crate::enter::{enter, Enter, EnterError};
|
400
third_party/rust/futures-executor/src/local_pool.rs
vendored
Normal file
400
third_party/rust/futures-executor/src/local_pool.rs
vendored
Normal file
@ -0,0 +1,400 @@
|
||||
use crate::enter;
|
||||
use futures_core::future::Future;
|
||||
use futures_core::stream::Stream;
|
||||
use futures_core::task::{Context, Poll};
|
||||
use futures_task::{waker_ref, ArcWake};
|
||||
use futures_task::{FutureObj, LocalFutureObj, LocalSpawn, Spawn, SpawnError};
|
||||
use futures_util::pin_mut;
|
||||
use futures_util::stream::FuturesUnordered;
|
||||
use futures_util::stream::StreamExt;
|
||||
use std::cell::RefCell;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::rc::{Rc, Weak};
|
||||
use std::sync::{
|
||||
atomic::{AtomicBool, Ordering},
|
||||
Arc,
|
||||
};
|
||||
use std::thread::{self, Thread};
|
||||
|
||||
/// A single-threaded task pool for polling futures to completion.
|
||||
///
|
||||
/// This executor allows you to multiplex any number of tasks onto a single
|
||||
/// thread. It's appropriate to poll strictly I/O-bound futures that do very
|
||||
/// little work in between I/O actions.
|
||||
///
|
||||
/// To get a handle to the pool that implements
|
||||
/// [`Spawn`](futures_task::Spawn), use the
|
||||
/// [`spawner()`](LocalPool::spawner) method. Because the executor is
|
||||
/// single-threaded, it supports a special form of task spawning for non-`Send`
|
||||
/// futures, via [`spawn_local_obj`](futures_task::LocalSpawn::spawn_local_obj).
|
||||
#[derive(Debug)]
|
||||
pub struct LocalPool {
|
||||
pool: FuturesUnordered<LocalFutureObj<'static, ()>>,
|
||||
incoming: Rc<Incoming>,
|
||||
}
|
||||
|
||||
/// A handle to a [`LocalPool`](LocalPool) that implements
|
||||
/// [`Spawn`](futures_task::Spawn).
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct LocalSpawner {
|
||||
incoming: Weak<Incoming>,
|
||||
}
|
||||
|
||||
type Incoming = RefCell<Vec<LocalFutureObj<'static, ()>>>;
|
||||
|
||||
pub(crate) struct ThreadNotify {
|
||||
/// The (single) executor thread.
|
||||
thread: Thread,
|
||||
/// A flag to ensure a wakeup (i.e. `unpark()`) is not "forgotten"
|
||||
/// before the next `park()`, which may otherwise happen if the code
|
||||
/// being executed as part of the future(s) being polled makes use of
|
||||
/// park / unpark calls of its own, i.e. we cannot assume that no other
|
||||
/// code uses park / unpark on the executing `thread`.
|
||||
unparked: AtomicBool,
|
||||
}
|
||||
|
||||
thread_local! {
|
||||
static CURRENT_THREAD_NOTIFY: Arc<ThreadNotify> = Arc::new(ThreadNotify {
|
||||
thread: thread::current(),
|
||||
unparked: AtomicBool::new(false),
|
||||
});
|
||||
}
|
||||
|
||||
impl ArcWake for ThreadNotify {
|
||||
fn wake_by_ref(arc_self: &Arc<Self>) {
|
||||
// Make sure the wakeup is remembered until the next `park()`.
|
||||
let unparked = arc_self.unparked.swap(true, Ordering::Relaxed);
|
||||
if !unparked {
|
||||
// If the thread has not been unparked yet, it must be done
|
||||
// now. If it was actually parked, it will run again,
|
||||
// otherwise the token made available by `unpark`
|
||||
// may be consumed before reaching `park()`, but `unparked`
|
||||
// ensures it is not forgotten.
|
||||
arc_self.thread.unpark();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set up and run a basic single-threaded spawner loop, invoking `f` on each
|
||||
// turn.
|
||||
fn run_executor<T, F: FnMut(&mut Context<'_>) -> Poll<T>>(mut f: F) -> T {
|
||||
let _enter = enter().expect(
|
||||
"cannot execute `LocalPool` executor from within \
|
||||
another executor",
|
||||
);
|
||||
|
||||
CURRENT_THREAD_NOTIFY.with(|thread_notify| {
|
||||
let waker = waker_ref(thread_notify);
|
||||
let mut cx = Context::from_waker(&waker);
|
||||
loop {
|
||||
if let Poll::Ready(t) = f(&mut cx) {
|
||||
return t;
|
||||
}
|
||||
// Consume the wakeup that occurred while executing `f`, if any.
|
||||
let unparked = thread_notify.unparked.swap(false, Ordering::Acquire);
|
||||
if !unparked {
|
||||
// No wakeup occurred. It may occur now, right before parking,
|
||||
// but in that case the token made available by `unpark()`
|
||||
// is guaranteed to still be available and `park()` is a no-op.
|
||||
thread::park();
|
||||
// When the thread is unparked, `unparked` will have been set
|
||||
// and needs to be unset before the next call to `f` to avoid
|
||||
// a redundant loop iteration.
|
||||
thread_notify.unparked.store(false, Ordering::Release);
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn poll_executor<T, F: FnMut(&mut Context<'_>) -> T>(mut f: F) -> T {
|
||||
let _enter = enter().expect(
|
||||
"cannot execute `LocalPool` executor from within \
|
||||
another executor",
|
||||
);
|
||||
|
||||
CURRENT_THREAD_NOTIFY.with(|thread_notify| {
|
||||
let waker = waker_ref(thread_notify);
|
||||
let mut cx = Context::from_waker(&waker);
|
||||
f(&mut cx)
|
||||
})
|
||||
}
|
||||
|
||||
impl LocalPool {
|
||||
/// Create a new, empty pool of tasks.
|
||||
pub fn new() -> Self {
|
||||
Self { pool: FuturesUnordered::new(), incoming: Default::default() }
|
||||
}
|
||||
|
||||
/// Get a clonable handle to the pool as a [`Spawn`].
|
||||
pub fn spawner(&self) -> LocalSpawner {
|
||||
LocalSpawner { incoming: Rc::downgrade(&self.incoming) }
|
||||
}
|
||||
|
||||
/// Run all tasks in the pool to completion.
|
||||
///
|
||||
/// ```
|
||||
/// use futures::executor::LocalPool;
|
||||
///
|
||||
/// let mut pool = LocalPool::new();
|
||||
///
|
||||
/// // ... spawn some initial tasks using `spawn.spawn()` or `spawn.spawn_local()`
|
||||
///
|
||||
/// // run *all* tasks in the pool to completion, including any newly-spawned ones.
|
||||
/// pool.run();
|
||||
/// ```
|
||||
///
|
||||
/// The function will block the calling thread until *all* tasks in the pool
|
||||
/// are complete, including any spawned while running existing tasks.
|
||||
pub fn run(&mut self) {
|
||||
run_executor(|cx| self.poll_pool(cx))
|
||||
}
|
||||
|
||||
/// Runs all the tasks in the pool until the given future completes.
|
||||
///
|
||||
/// ```
|
||||
/// use futures::executor::LocalPool;
|
||||
///
|
||||
/// let mut pool = LocalPool::new();
|
||||
/// # let my_app = async {};
|
||||
///
|
||||
/// // run tasks in the pool until `my_app` completes
|
||||
/// pool.run_until(my_app);
|
||||
/// ```
|
||||
///
|
||||
/// The function will block the calling thread *only* until the future `f`
|
||||
/// completes; there may still be incomplete tasks in the pool, which will
|
||||
/// be inert after the call completes, but can continue with further use of
|
||||
/// one of the pool's run or poll methods. While the function is running,
|
||||
/// however, all tasks in the pool will try to make progress.
|
||||
pub fn run_until<F: Future>(&mut self, future: F) -> F::Output {
|
||||
pin_mut!(future);
|
||||
|
||||
run_executor(|cx| {
|
||||
{
|
||||
// if our main task is done, so are we
|
||||
let result = future.as_mut().poll(cx);
|
||||
if let Poll::Ready(output) = result {
|
||||
return Poll::Ready(output);
|
||||
}
|
||||
}
|
||||
|
||||
let _ = self.poll_pool(cx);
|
||||
Poll::Pending
|
||||
})
|
||||
}
|
||||
|
||||
/// Runs all tasks and returns after completing one future or until no more progress
|
||||
/// can be made. Returns `true` if one future was completed, `false` otherwise.
|
||||
///
|
||||
/// ```
|
||||
/// use futures::executor::LocalPool;
|
||||
/// use futures::task::LocalSpawnExt;
|
||||
/// use futures::future::{ready, pending};
|
||||
///
|
||||
/// let mut pool = LocalPool::new();
|
||||
/// let spawner = pool.spawner();
|
||||
///
|
||||
/// spawner.spawn_local(ready(())).unwrap();
|
||||
/// spawner.spawn_local(ready(())).unwrap();
|
||||
/// spawner.spawn_local(pending()).unwrap();
|
||||
///
|
||||
/// // Run the two ready tasks and return true for them.
|
||||
/// pool.try_run_one(); // returns true after completing one of the ready futures
|
||||
/// pool.try_run_one(); // returns true after completing the other ready future
|
||||
///
|
||||
/// // the remaining task can not be completed
|
||||
/// assert!(!pool.try_run_one()); // returns false
|
||||
/// ```
|
||||
///
|
||||
/// This function will not block the calling thread and will return the moment
|
||||
/// that there are no tasks left for which progress can be made or after exactly one
|
||||
/// task was completed; Remaining incomplete tasks in the pool can continue with
|
||||
/// further use of one of the pool's run or poll methods.
|
||||
/// Though only one task will be completed, progress may be made on multiple tasks.
|
||||
pub fn try_run_one(&mut self) -> bool {
|
||||
poll_executor(|ctx| {
|
||||
loop {
|
||||
let ret = self.poll_pool_once(ctx);
|
||||
|
||||
// return if we have executed a future
|
||||
if let Poll::Ready(Some(_)) = ret {
|
||||
return true;
|
||||
}
|
||||
|
||||
// if there are no new incoming futures
|
||||
// then there is no feature that can make progress
|
||||
// and we can return without having completed a single future
|
||||
if self.incoming.borrow().is_empty() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Runs all tasks in the pool and returns if no more progress can be made
|
||||
/// on any task.
|
||||
///
|
||||
/// ```
|
||||
/// use futures::executor::LocalPool;
|
||||
/// use futures::task::LocalSpawnExt;
|
||||
/// use futures::future::{ready, pending};
|
||||
///
|
||||
/// let mut pool = LocalPool::new();
|
||||
/// let spawner = pool.spawner();
|
||||
///
|
||||
/// spawner.spawn_local(ready(())).unwrap();
|
||||
/// spawner.spawn_local(ready(())).unwrap();
|
||||
/// spawner.spawn_local(pending()).unwrap();
|
||||
///
|
||||
/// // Runs the two ready task and returns.
|
||||
/// // The empty task remains in the pool.
|
||||
/// pool.run_until_stalled();
|
||||
/// ```
|
||||
///
|
||||
/// This function will not block the calling thread and will return the moment
|
||||
/// that there are no tasks left for which progress can be made;
|
||||
/// remaining incomplete tasks in the pool can continue with further use of one
|
||||
/// of the pool's run or poll methods. While the function is running, all tasks
|
||||
/// in the pool will try to make progress.
|
||||
pub fn run_until_stalled(&mut self) {
|
||||
poll_executor(|ctx| {
|
||||
let _ = self.poll_pool(ctx);
|
||||
});
|
||||
}
|
||||
|
||||
// Make maximal progress on the entire pool of spawned task, returning `Ready`
|
||||
// if the pool is empty and `Pending` if no further progress can be made.
|
||||
fn poll_pool(&mut self, cx: &mut Context<'_>) -> Poll<()> {
|
||||
// state for the FuturesUnordered, which will never be used
|
||||
loop {
|
||||
let ret = self.poll_pool_once(cx);
|
||||
|
||||
// we queued up some new tasks; add them and poll again
|
||||
if !self.incoming.borrow().is_empty() {
|
||||
continue;
|
||||
}
|
||||
|
||||
// no queued tasks; we may be done
|
||||
match ret {
|
||||
Poll::Pending => return Poll::Pending,
|
||||
Poll::Ready(None) => return Poll::Ready(()),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Try make minimal progress on the pool of spawned tasks
|
||||
fn poll_pool_once(&mut self, cx: &mut Context<'_>) -> Poll<Option<()>> {
|
||||
// empty the incoming queue of newly-spawned tasks
|
||||
{
|
||||
let mut incoming = self.incoming.borrow_mut();
|
||||
for task in incoming.drain(..) {
|
||||
self.pool.push(task)
|
||||
}
|
||||
}
|
||||
|
||||
// try to execute the next ready future
|
||||
self.pool.poll_next_unpin(cx)
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for LocalPool {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
/// Run a future to completion on the current thread.
|
||||
///
|
||||
/// This function will block the caller until the given future has completed.
|
||||
///
|
||||
/// Use a [`LocalPool`](LocalPool) if you need finer-grained control over
|
||||
/// spawned tasks.
|
||||
pub fn block_on<F: Future>(f: F) -> F::Output {
|
||||
pin_mut!(f);
|
||||
run_executor(|cx| f.as_mut().poll(cx))
|
||||
}
|
||||
|
||||
/// Turn a stream into a blocking iterator.
|
||||
///
|
||||
/// When `next` is called on the resulting `BlockingStream`, the caller
|
||||
/// will be blocked until the next element of the `Stream` becomes available.
|
||||
pub fn block_on_stream<S: Stream + Unpin>(stream: S) -> BlockingStream<S> {
|
||||
BlockingStream { stream }
|
||||
}
|
||||
|
||||
/// An iterator which blocks on values from a stream until they become available.
|
||||
#[derive(Debug)]
|
||||
pub struct BlockingStream<S: Stream + Unpin> {
|
||||
stream: S,
|
||||
}
|
||||
|
||||
impl<S: Stream + Unpin> Deref for BlockingStream<S> {
|
||||
type Target = S;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.stream
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: Stream + Unpin> DerefMut for BlockingStream<S> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.stream
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: Stream + Unpin> BlockingStream<S> {
|
||||
/// Convert this `BlockingStream` into the inner `Stream` type.
|
||||
pub fn into_inner(self) -> S {
|
||||
self.stream
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: Stream + Unpin> Iterator for BlockingStream<S> {
|
||||
type Item = S::Item;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
LocalPool::new().run_until(self.stream.next())
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
self.stream.size_hint()
|
||||
}
|
||||
}
|
||||
|
||||
impl Spawn for LocalSpawner {
|
||||
fn spawn_obj(&self, future: FutureObj<'static, ()>) -> Result<(), SpawnError> {
|
||||
if let Some(incoming) = self.incoming.upgrade() {
|
||||
incoming.borrow_mut().push(future.into());
|
||||
Ok(())
|
||||
} else {
|
||||
Err(SpawnError::shutdown())
|
||||
}
|
||||
}
|
||||
|
||||
fn status(&self) -> Result<(), SpawnError> {
|
||||
if self.incoming.upgrade().is_some() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(SpawnError::shutdown())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl LocalSpawn for LocalSpawner {
|
||||
fn spawn_local_obj(&self, future: LocalFutureObj<'static, ()>) -> Result<(), SpawnError> {
|
||||
if let Some(incoming) = self.incoming.upgrade() {
|
||||
incoming.borrow_mut().push(future);
|
||||
Ok(())
|
||||
} else {
|
||||
Err(SpawnError::shutdown())
|
||||
}
|
||||
}
|
||||
|
||||
fn status_local(&self) -> Result<(), SpawnError> {
|
||||
if self.incoming.upgrade().is_some() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(SpawnError::shutdown())
|
||||
}
|
||||
}
|
||||
}
|
375
third_party/rust/futures-executor/src/thread_pool.rs
vendored
Normal file
375
third_party/rust/futures-executor/src/thread_pool.rs
vendored
Normal file
@ -0,0 +1,375 @@
|
||||
use crate::enter;
|
||||
use crate::unpark_mutex::UnparkMutex;
|
||||
use futures_core::future::Future;
|
||||
use futures_core::task::{Context, Poll};
|
||||
use futures_task::{waker_ref, ArcWake};
|
||||
use futures_task::{FutureObj, Spawn, SpawnError};
|
||||
use futures_util::future::FutureExt;
|
||||
use std::cmp;
|
||||
use std::fmt;
|
||||
use std::io;
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
use std::sync::mpsc;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::thread;
|
||||
|
||||
/// A general-purpose thread pool for scheduling tasks that poll futures to
|
||||
/// completion.
|
||||
///
|
||||
/// The thread pool multiplexes any number of tasks onto a fixed number of
|
||||
/// worker threads.
|
||||
///
|
||||
/// This type is a clonable handle to the threadpool itself.
|
||||
/// Cloning it will only create a new reference, not a new threadpool.
|
||||
///
|
||||
/// This type is only available when the `thread-pool` feature of this
|
||||
/// library is activated.
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "thread-pool")))]
|
||||
pub struct ThreadPool {
|
||||
state: Arc<PoolState>,
|
||||
}
|
||||
|
||||
/// Thread pool configuration object.
|
||||
///
|
||||
/// This type is only available when the `thread-pool` feature of this
|
||||
/// library is activated.
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "thread-pool")))]
|
||||
pub struct ThreadPoolBuilder {
|
||||
pool_size: usize,
|
||||
stack_size: usize,
|
||||
name_prefix: Option<String>,
|
||||
after_start: Option<Arc<dyn Fn(usize) + Send + Sync>>,
|
||||
before_stop: Option<Arc<dyn Fn(usize) + Send + Sync>>,
|
||||
}
|
||||
|
||||
trait AssertSendSync: Send + Sync {}
|
||||
impl AssertSendSync for ThreadPool {}
|
||||
|
||||
struct PoolState {
|
||||
tx: Mutex<mpsc::Sender<Message>>,
|
||||
rx: Mutex<mpsc::Receiver<Message>>,
|
||||
cnt: AtomicUsize,
|
||||
size: usize,
|
||||
}
|
||||
|
||||
impl fmt::Debug for ThreadPool {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("ThreadPool").field("size", &self.state.size).finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for ThreadPoolBuilder {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("ThreadPoolBuilder")
|
||||
.field("pool_size", &self.pool_size)
|
||||
.field("name_prefix", &self.name_prefix)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
enum Message {
|
||||
Run(Task),
|
||||
Close,
|
||||
}
|
||||
|
||||
impl ThreadPool {
|
||||
/// Creates a new thread pool with the default configuration.
|
||||
///
|
||||
/// See documentation for the methods in
|
||||
/// [`ThreadPoolBuilder`](ThreadPoolBuilder) for details on the default
|
||||
/// configuration.
|
||||
pub fn new() -> Result<Self, io::Error> {
|
||||
ThreadPoolBuilder::new().create()
|
||||
}
|
||||
|
||||
/// Create a default thread pool configuration, which can then be customized.
|
||||
///
|
||||
/// See documentation for the methods in
|
||||
/// [`ThreadPoolBuilder`](ThreadPoolBuilder) for details on the default
|
||||
/// configuration.
|
||||
pub fn builder() -> ThreadPoolBuilder {
|
||||
ThreadPoolBuilder::new()
|
||||
}
|
||||
|
||||
/// Spawns a future that will be run to completion.
|
||||
///
|
||||
/// > **Note**: This method is similar to `Spawn::spawn_obj`, except that
|
||||
/// > it is guaranteed to always succeed.
|
||||
pub fn spawn_obj_ok(&self, future: FutureObj<'static, ()>) {
|
||||
let task = Task {
|
||||
future,
|
||||
wake_handle: Arc::new(WakeHandle { exec: self.clone(), mutex: UnparkMutex::new() }),
|
||||
exec: self.clone(),
|
||||
};
|
||||
self.state.send(Message::Run(task));
|
||||
}
|
||||
|
||||
/// Spawns a task that polls the given future with output `()` to
|
||||
/// completion.
|
||||
///
|
||||
/// ```
|
||||
/// use futures::executor::ThreadPool;
|
||||
///
|
||||
/// let pool = ThreadPool::new().unwrap();
|
||||
///
|
||||
/// let future = async { /* ... */ };
|
||||
/// pool.spawn_ok(future);
|
||||
/// ```
|
||||
///
|
||||
/// > **Note**: This method is similar to `SpawnExt::spawn`, except that
|
||||
/// > it is guaranteed to always succeed.
|
||||
pub fn spawn_ok<Fut>(&self, future: Fut)
|
||||
where
|
||||
Fut: Future<Output = ()> + Send + 'static,
|
||||
{
|
||||
self.spawn_obj_ok(FutureObj::new(Box::new(future)))
|
||||
}
|
||||
}
|
||||
|
||||
impl Spawn for ThreadPool {
|
||||
fn spawn_obj(&self, future: FutureObj<'static, ()>) -> Result<(), SpawnError> {
|
||||
self.spawn_obj_ok(future);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl PoolState {
|
||||
fn send(&self, msg: Message) {
|
||||
self.tx.lock().unwrap().send(msg).unwrap();
|
||||
}
|
||||
|
||||
fn work(
|
||||
&self,
|
||||
idx: usize,
|
||||
after_start: Option<Arc<dyn Fn(usize) + Send + Sync>>,
|
||||
before_stop: Option<Arc<dyn Fn(usize) + Send + Sync>>,
|
||||
) {
|
||||
let _scope = enter().unwrap();
|
||||
if let Some(after_start) = after_start {
|
||||
after_start(idx);
|
||||
}
|
||||
loop {
|
||||
let msg = self.rx.lock().unwrap().recv().unwrap();
|
||||
match msg {
|
||||
Message::Run(task) => task.run(),
|
||||
Message::Close => break,
|
||||
}
|
||||
}
|
||||
if let Some(before_stop) = before_stop {
|
||||
before_stop(idx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for ThreadPool {
|
||||
fn clone(&self) -> Self {
|
||||
self.state.cnt.fetch_add(1, Ordering::Relaxed);
|
||||
Self { state: self.state.clone() }
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for ThreadPool {
|
||||
fn drop(&mut self) {
|
||||
if self.state.cnt.fetch_sub(1, Ordering::Relaxed) == 1 {
|
||||
for _ in 0..self.state.size {
|
||||
self.state.send(Message::Close);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ThreadPoolBuilder {
|
||||
/// Create a default thread pool configuration.
|
||||
///
|
||||
/// See the other methods on this type for details on the defaults.
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
pool_size: cmp::max(1, num_cpus::get()),
|
||||
stack_size: 0,
|
||||
name_prefix: None,
|
||||
after_start: None,
|
||||
before_stop: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Set size of a future ThreadPool
|
||||
///
|
||||
/// The size of a thread pool is the number of worker threads spawned. By
|
||||
/// default, this is equal to the number of CPU cores.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if `pool_size == 0`.
|
||||
pub fn pool_size(&mut self, size: usize) -> &mut Self {
|
||||
assert!(size > 0);
|
||||
self.pool_size = size;
|
||||
self
|
||||
}
|
||||
|
||||
/// Set stack size of threads in the pool, in bytes.
|
||||
///
|
||||
/// By default, worker threads use Rust's standard stack size.
|
||||
pub fn stack_size(&mut self, stack_size: usize) -> &mut Self {
|
||||
self.stack_size = stack_size;
|
||||
self
|
||||
}
|
||||
|
||||
/// Set thread name prefix of a future ThreadPool.
|
||||
///
|
||||
/// Thread name prefix is used for generating thread names. For example, if prefix is
|
||||
/// `my-pool-`, then threads in the pool will get names like `my-pool-1` etc.
|
||||
///
|
||||
/// By default, worker threads are assigned Rust's standard thread name.
|
||||
pub fn name_prefix<S: Into<String>>(&mut self, name_prefix: S) -> &mut Self {
|
||||
self.name_prefix = Some(name_prefix.into());
|
||||
self
|
||||
}
|
||||
|
||||
/// Execute the closure `f` immediately after each worker thread is started,
|
||||
/// but before running any tasks on it.
|
||||
///
|
||||
/// This hook is intended for bookkeeping and monitoring.
|
||||
/// The closure `f` will be dropped after the `builder` is dropped
|
||||
/// and all worker threads in the pool have executed it.
|
||||
///
|
||||
/// The closure provided will receive an index corresponding to the worker
|
||||
/// thread it's running on.
|
||||
pub fn after_start<F>(&mut self, f: F) -> &mut Self
|
||||
where
|
||||
F: Fn(usize) + Send + Sync + 'static,
|
||||
{
|
||||
self.after_start = Some(Arc::new(f));
|
||||
self
|
||||
}
|
||||
|
||||
/// Execute closure `f` just prior to shutting down each worker thread.
|
||||
///
|
||||
/// This hook is intended for bookkeeping and monitoring.
|
||||
/// The closure `f` will be dropped after the `builder` is droppped
|
||||
/// and all threads in the pool have executed it.
|
||||
///
|
||||
/// The closure provided will receive an index corresponding to the worker
|
||||
/// thread it's running on.
|
||||
pub fn before_stop<F>(&mut self, f: F) -> &mut Self
|
||||
where
|
||||
F: Fn(usize) + Send + Sync + 'static,
|
||||
{
|
||||
self.before_stop = Some(Arc::new(f));
|
||||
self
|
||||
}
|
||||
|
||||
/// Create a [`ThreadPool`](ThreadPool) with the given configuration.
|
||||
pub fn create(&mut self) -> Result<ThreadPool, io::Error> {
|
||||
let (tx, rx) = mpsc::channel();
|
||||
let pool = ThreadPool {
|
||||
state: Arc::new(PoolState {
|
||||
tx: Mutex::new(tx),
|
||||
rx: Mutex::new(rx),
|
||||
cnt: AtomicUsize::new(1),
|
||||
size: self.pool_size,
|
||||
}),
|
||||
};
|
||||
|
||||
for counter in 0..self.pool_size {
|
||||
let state = pool.state.clone();
|
||||
let after_start = self.after_start.clone();
|
||||
let before_stop = self.before_stop.clone();
|
||||
let mut thread_builder = thread::Builder::new();
|
||||
if let Some(ref name_prefix) = self.name_prefix {
|
||||
thread_builder = thread_builder.name(format!("{}{}", name_prefix, counter));
|
||||
}
|
||||
if self.stack_size > 0 {
|
||||
thread_builder = thread_builder.stack_size(self.stack_size);
|
||||
}
|
||||
thread_builder.spawn(move || state.work(counter, after_start, before_stop))?;
|
||||
}
|
||||
Ok(pool)
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for ThreadPoolBuilder {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
/// A task responsible for polling a future to completion.
|
||||
struct Task {
|
||||
future: FutureObj<'static, ()>,
|
||||
exec: ThreadPool,
|
||||
wake_handle: Arc<WakeHandle>,
|
||||
}
|
||||
|
||||
struct WakeHandle {
|
||||
mutex: UnparkMutex<Task>,
|
||||
exec: ThreadPool,
|
||||
}
|
||||
|
||||
impl Task {
|
||||
/// Actually run the task (invoking `poll` on the future) on the current
|
||||
/// thread.
|
||||
fn run(self) {
|
||||
let Self { mut future, wake_handle, mut exec } = self;
|
||||
let waker = waker_ref(&wake_handle);
|
||||
let mut cx = Context::from_waker(&waker);
|
||||
|
||||
// Safety: The ownership of this `Task` object is evidence that
|
||||
// we are in the `POLLING`/`REPOLL` state for the mutex.
|
||||
unsafe {
|
||||
wake_handle.mutex.start_poll();
|
||||
|
||||
loop {
|
||||
let res = future.poll_unpin(&mut cx);
|
||||
match res {
|
||||
Poll::Pending => {}
|
||||
Poll::Ready(()) => return wake_handle.mutex.complete(),
|
||||
}
|
||||
let task = Self { future, wake_handle: wake_handle.clone(), exec };
|
||||
match wake_handle.mutex.wait(task) {
|
||||
Ok(()) => return, // we've waited
|
||||
Err(task) => {
|
||||
// someone's notified us
|
||||
future = task.future;
|
||||
exec = task.exec;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Task {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("Task").field("contents", &"...").finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl ArcWake for WakeHandle {
|
||||
fn wake_by_ref(arc_self: &Arc<Self>) {
|
||||
match arc_self.mutex.notify() {
|
||||
Ok(task) => arc_self.exec.state.send(Message::Run(task)),
|
||||
Err(()) => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use std::sync::mpsc;
|
||||
|
||||
#[test]
|
||||
fn test_drop_after_start() {
|
||||
let (tx, rx) = mpsc::sync_channel(2);
|
||||
let _cpu_pool = ThreadPoolBuilder::new()
|
||||
.pool_size(2)
|
||||
.after_start(move |_| tx.send(1).unwrap())
|
||||
.create()
|
||||
.unwrap();
|
||||
|
||||
// After ThreadPoolBuilder is deconstructed, the tx should be droped
|
||||
// so that we can use rx as an iterator.
|
||||
let count = rx.into_iter().count();
|
||||
assert_eq!(count, 2);
|
||||
}
|
||||
}
|
137
third_party/rust/futures-executor/src/unpark_mutex.rs
vendored
Normal file
137
third_party/rust/futures-executor/src/unpark_mutex.rs
vendored
Normal file
@ -0,0 +1,137 @@
|
||||
use std::cell::UnsafeCell;
|
||||
use std::sync::atomic::AtomicUsize;
|
||||
use std::sync::atomic::Ordering::SeqCst;
|
||||
|
||||
/// A "lock" around data `D`, which employs a *helping* strategy.
|
||||
///
|
||||
/// Used to ensure that concurrent `unpark` invocations lead to (1) `poll` being
|
||||
/// invoked on only a single thread at a time (2) `poll` being invoked at least
|
||||
/// once after each `unpark` (unless the future has completed).
|
||||
pub(crate) struct UnparkMutex<D> {
|
||||
// The state of task execution (state machine described below)
|
||||
status: AtomicUsize,
|
||||
|
||||
// The actual task data, accessible only in the POLLING state
|
||||
inner: UnsafeCell<Option<D>>,
|
||||
}
|
||||
|
||||
// `UnparkMutex<D>` functions in many ways like a `Mutex<D>`, except that on
|
||||
// acquisition failure, the current lock holder performs the desired work --
|
||||
// re-polling.
|
||||
//
|
||||
// As such, these impls mirror those for `Mutex<D>`. In particular, a reference
|
||||
// to `UnparkMutex` can be used to gain `&mut` access to the inner data, which
|
||||
// must therefore be `Send`.
|
||||
unsafe impl<D: Send> Send for UnparkMutex<D> {}
|
||||
unsafe impl<D: Send> Sync for UnparkMutex<D> {}
|
||||
|
||||
// There are four possible task states, listed below with their possible
|
||||
// transitions:
|
||||
|
||||
// The task is blocked, waiting on an event
|
||||
const WAITING: usize = 0; // --> POLLING
|
||||
|
||||
// The task is actively being polled by a thread; arrival of additional events
|
||||
// of interest should move it to the REPOLL state
|
||||
const POLLING: usize = 1; // --> WAITING, REPOLL, or COMPLETE
|
||||
|
||||
// The task is actively being polled, but will need to be re-polled upon
|
||||
// completion to ensure that all events were observed.
|
||||
const REPOLL: usize = 2; // --> POLLING
|
||||
|
||||
// The task has finished executing (either successfully or with an error/panic)
|
||||
const COMPLETE: usize = 3; // No transitions out
|
||||
|
||||
impl<D> UnparkMutex<D> {
|
||||
pub(crate) fn new() -> Self {
|
||||
Self { status: AtomicUsize::new(WAITING), inner: UnsafeCell::new(None) }
|
||||
}
|
||||
|
||||
/// Attempt to "notify" the mutex that a poll should occur.
|
||||
///
|
||||
/// An `Ok` result indicates that the `POLLING` state has been entered, and
|
||||
/// the caller can proceed to poll the future. An `Err` result indicates
|
||||
/// that polling is not necessary (because the task is finished or the
|
||||
/// polling has been delegated).
|
||||
pub(crate) fn notify(&self) -> Result<D, ()> {
|
||||
let mut status = self.status.load(SeqCst);
|
||||
loop {
|
||||
match status {
|
||||
// The task is idle, so try to run it immediately.
|
||||
WAITING => {
|
||||
match self.status.compare_exchange(WAITING, POLLING, SeqCst, SeqCst) {
|
||||
Ok(_) => {
|
||||
let data = unsafe {
|
||||
// SAFETY: we've ensured mutual exclusion via
|
||||
// the status protocol; we are the only thread
|
||||
// that has transitioned to the POLLING state,
|
||||
// and we won't transition back to QUEUED until
|
||||
// the lock is "released" by this thread. See
|
||||
// the protocol diagram above.
|
||||
(*self.inner.get()).take().unwrap()
|
||||
};
|
||||
return Ok(data);
|
||||
}
|
||||
Err(cur) => status = cur,
|
||||
}
|
||||
}
|
||||
|
||||
// The task is being polled, so we need to record that it should
|
||||
// be *repolled* when complete.
|
||||
POLLING => match self.status.compare_exchange(POLLING, REPOLL, SeqCst, SeqCst) {
|
||||
Ok(_) => return Err(()),
|
||||
Err(cur) => status = cur,
|
||||
},
|
||||
|
||||
// The task is already scheduled for polling, or is complete, so
|
||||
// we've got nothing to do.
|
||||
_ => return Err(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Alert the mutex that polling is about to begin, clearing any accumulated
|
||||
/// re-poll requests.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Callable only from the `POLLING`/`REPOLL` states, i.e. between
|
||||
/// successful calls to `notify` and `wait`/`complete`.
|
||||
pub(crate) unsafe fn start_poll(&self) {
|
||||
self.status.store(POLLING, SeqCst);
|
||||
}
|
||||
|
||||
/// Alert the mutex that polling completed with `Pending`.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Callable only from the `POLLING`/`REPOLL` states, i.e. between
|
||||
/// successful calls to `notify` and `wait`/`complete`.
|
||||
pub(crate) unsafe fn wait(&self, data: D) -> Result<(), D> {
|
||||
*self.inner.get() = Some(data);
|
||||
|
||||
match self.status.compare_exchange(POLLING, WAITING, SeqCst, SeqCst) {
|
||||
// no unparks came in while we were running
|
||||
Ok(_) => Ok(()),
|
||||
|
||||
// guaranteed to be in REPOLL state; just clobber the
|
||||
// state and run again.
|
||||
Err(status) => {
|
||||
assert_eq!(status, REPOLL);
|
||||
self.status.store(POLLING, SeqCst);
|
||||
Err((*self.inner.get()).take().unwrap())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Alert the mutex that the task has completed execution and should not be
|
||||
/// notified again.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Callable only from the `POLLING`/`REPOLL` states, i.e. between
|
||||
/// successful calls to `notify` and `wait`/`complete`.
|
||||
pub(crate) unsafe fn complete(&self) {
|
||||
self.status.store(COMPLETE, SeqCst);
|
||||
}
|
||||
}
|
434
third_party/rust/futures-executor/tests/local_pool.rs
vendored
Normal file
434
third_party/rust/futures-executor/tests/local_pool.rs
vendored
Normal file
@ -0,0 +1,434 @@
|
||||
use futures::channel::oneshot;
|
||||
use futures::executor::LocalPool;
|
||||
use futures::future::{self, lazy, poll_fn, Future};
|
||||
use futures::task::{Context, LocalSpawn, Poll, Spawn, Waker};
|
||||
use std::cell::{Cell, RefCell};
|
||||
use std::pin::Pin;
|
||||
use std::rc::Rc;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::Arc;
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
|
||||
struct Pending(Rc<()>);
|
||||
|
||||
impl Future for Pending {
|
||||
type Output = ();
|
||||
|
||||
fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<()> {
|
||||
Poll::Pending
|
||||
}
|
||||
}
|
||||
|
||||
fn pending() -> Pending {
|
||||
Pending(Rc::new(()))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn run_until_single_future() {
|
||||
let mut cnt = 0;
|
||||
|
||||
{
|
||||
let mut pool = LocalPool::new();
|
||||
let fut = lazy(|_| {
|
||||
cnt += 1;
|
||||
});
|
||||
pool.run_until(fut);
|
||||
}
|
||||
|
||||
assert_eq!(cnt, 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn run_until_ignores_spawned() {
|
||||
let mut pool = LocalPool::new();
|
||||
let spawn = pool.spawner();
|
||||
spawn.spawn_local_obj(Box::pin(pending()).into()).unwrap();
|
||||
pool.run_until(lazy(|_| ()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn run_until_executes_spawned() {
|
||||
let (tx, rx) = oneshot::channel();
|
||||
let mut pool = LocalPool::new();
|
||||
let spawn = pool.spawner();
|
||||
spawn
|
||||
.spawn_local_obj(
|
||||
Box::pin(lazy(move |_| {
|
||||
tx.send(()).unwrap();
|
||||
}))
|
||||
.into(),
|
||||
)
|
||||
.unwrap();
|
||||
pool.run_until(rx).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn run_returns_if_empty() {
|
||||
let mut pool = LocalPool::new();
|
||||
pool.run();
|
||||
pool.run();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn run_executes_spawned() {
|
||||
let cnt = Rc::new(Cell::new(0));
|
||||
let cnt2 = cnt.clone();
|
||||
|
||||
let mut pool = LocalPool::new();
|
||||
let spawn = pool.spawner();
|
||||
let spawn2 = pool.spawner();
|
||||
|
||||
spawn
|
||||
.spawn_local_obj(
|
||||
Box::pin(lazy(move |_| {
|
||||
spawn2
|
||||
.spawn_local_obj(
|
||||
Box::pin(lazy(move |_| {
|
||||
cnt2.set(cnt2.get() + 1);
|
||||
}))
|
||||
.into(),
|
||||
)
|
||||
.unwrap();
|
||||
}))
|
||||
.into(),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
pool.run();
|
||||
|
||||
assert_eq!(cnt.get(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn run_spawn_many() {
|
||||
const ITER: usize = 200;
|
||||
|
||||
let cnt = Rc::new(Cell::new(0));
|
||||
|
||||
let mut pool = LocalPool::new();
|
||||
let spawn = pool.spawner();
|
||||
|
||||
for _ in 0..ITER {
|
||||
let cnt = cnt.clone();
|
||||
spawn
|
||||
.spawn_local_obj(
|
||||
Box::pin(lazy(move |_| {
|
||||
cnt.set(cnt.get() + 1);
|
||||
}))
|
||||
.into(),
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
pool.run();
|
||||
|
||||
assert_eq!(cnt.get(), ITER);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn try_run_one_returns_if_empty() {
|
||||
let mut pool = LocalPool::new();
|
||||
assert!(!pool.try_run_one());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn try_run_one_executes_one_ready() {
|
||||
const ITER: usize = 200;
|
||||
|
||||
let cnt = Rc::new(Cell::new(0));
|
||||
|
||||
let mut pool = LocalPool::new();
|
||||
let spawn = pool.spawner();
|
||||
|
||||
for _ in 0..ITER {
|
||||
spawn.spawn_local_obj(Box::pin(pending()).into()).unwrap();
|
||||
|
||||
let cnt = cnt.clone();
|
||||
spawn
|
||||
.spawn_local_obj(
|
||||
Box::pin(lazy(move |_| {
|
||||
cnt.set(cnt.get() + 1);
|
||||
}))
|
||||
.into(),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
spawn.spawn_local_obj(Box::pin(pending()).into()).unwrap();
|
||||
}
|
||||
|
||||
for i in 0..ITER {
|
||||
assert_eq!(cnt.get(), i);
|
||||
assert!(pool.try_run_one());
|
||||
assert_eq!(cnt.get(), i + 1);
|
||||
}
|
||||
assert!(!pool.try_run_one());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn try_run_one_returns_on_no_progress() {
|
||||
const ITER: usize = 10;
|
||||
|
||||
let cnt = Rc::new(Cell::new(0));
|
||||
|
||||
let mut pool = LocalPool::new();
|
||||
let spawn = pool.spawner();
|
||||
|
||||
let waker: Rc<Cell<Option<Waker>>> = Rc::new(Cell::new(None));
|
||||
{
|
||||
let cnt = cnt.clone();
|
||||
let waker = waker.clone();
|
||||
spawn
|
||||
.spawn_local_obj(
|
||||
Box::pin(poll_fn(move |ctx| {
|
||||
cnt.set(cnt.get() + 1);
|
||||
waker.set(Some(ctx.waker().clone()));
|
||||
if cnt.get() == ITER {
|
||||
Poll::Ready(())
|
||||
} else {
|
||||
Poll::Pending
|
||||
}
|
||||
}))
|
||||
.into(),
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
for i in 0..ITER - 1 {
|
||||
assert_eq!(cnt.get(), i);
|
||||
assert!(!pool.try_run_one());
|
||||
assert_eq!(cnt.get(), i + 1);
|
||||
let w = waker.take();
|
||||
assert!(w.is_some());
|
||||
w.unwrap().wake();
|
||||
}
|
||||
assert!(pool.try_run_one());
|
||||
assert_eq!(cnt.get(), ITER);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn try_run_one_runs_sub_futures() {
|
||||
let mut pool = LocalPool::new();
|
||||
let spawn = pool.spawner();
|
||||
let cnt = Rc::new(Cell::new(0));
|
||||
|
||||
let inner_spawner = spawn.clone();
|
||||
let cnt1 = cnt.clone();
|
||||
spawn
|
||||
.spawn_local_obj(
|
||||
Box::pin(poll_fn(move |_| {
|
||||
cnt1.set(cnt1.get() + 1);
|
||||
|
||||
let cnt2 = cnt1.clone();
|
||||
inner_spawner
|
||||
.spawn_local_obj(Box::pin(lazy(move |_| cnt2.set(cnt2.get() + 1))).into())
|
||||
.unwrap();
|
||||
|
||||
Poll::Pending
|
||||
}))
|
||||
.into(),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
pool.try_run_one();
|
||||
assert_eq!(cnt.get(), 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn run_until_stalled_returns_if_empty() {
|
||||
let mut pool = LocalPool::new();
|
||||
pool.run_until_stalled();
|
||||
pool.run_until_stalled();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn run_until_stalled_returns_multiple_times() {
|
||||
let mut pool = LocalPool::new();
|
||||
let spawn = pool.spawner();
|
||||
let cnt = Rc::new(Cell::new(0));
|
||||
|
||||
let cnt1 = cnt.clone();
|
||||
spawn.spawn_local_obj(Box::pin(lazy(move |_| cnt1.set(cnt1.get() + 1))).into()).unwrap();
|
||||
pool.run_until_stalled();
|
||||
assert_eq!(cnt.get(), 1);
|
||||
|
||||
let cnt2 = cnt.clone();
|
||||
spawn.spawn_local_obj(Box::pin(lazy(move |_| cnt2.set(cnt2.get() + 1))).into()).unwrap();
|
||||
pool.run_until_stalled();
|
||||
assert_eq!(cnt.get(), 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn run_until_stalled_runs_spawned_sub_futures() {
|
||||
let mut pool = LocalPool::new();
|
||||
let spawn = pool.spawner();
|
||||
let cnt = Rc::new(Cell::new(0));
|
||||
|
||||
let inner_spawner = spawn.clone();
|
||||
let cnt1 = cnt.clone();
|
||||
spawn
|
||||
.spawn_local_obj(
|
||||
Box::pin(poll_fn(move |_| {
|
||||
cnt1.set(cnt1.get() + 1);
|
||||
|
||||
let cnt2 = cnt1.clone();
|
||||
inner_spawner
|
||||
.spawn_local_obj(Box::pin(lazy(move |_| cnt2.set(cnt2.get() + 1))).into())
|
||||
.unwrap();
|
||||
|
||||
Poll::Pending
|
||||
}))
|
||||
.into(),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
pool.run_until_stalled();
|
||||
assert_eq!(cnt.get(), 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn run_until_stalled_executes_all_ready() {
|
||||
const ITER: usize = 200;
|
||||
const PER_ITER: usize = 3;
|
||||
|
||||
let cnt = Rc::new(Cell::new(0));
|
||||
|
||||
let mut pool = LocalPool::new();
|
||||
let spawn = pool.spawner();
|
||||
|
||||
for i in 0..ITER {
|
||||
for _ in 0..PER_ITER {
|
||||
spawn.spawn_local_obj(Box::pin(pending()).into()).unwrap();
|
||||
|
||||
let cnt = cnt.clone();
|
||||
spawn
|
||||
.spawn_local_obj(
|
||||
Box::pin(lazy(move |_| {
|
||||
cnt.set(cnt.get() + 1);
|
||||
}))
|
||||
.into(),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// also add some pending tasks to test if they are ignored
|
||||
spawn.spawn_local_obj(Box::pin(pending()).into()).unwrap();
|
||||
}
|
||||
assert_eq!(cnt.get(), i * PER_ITER);
|
||||
pool.run_until_stalled();
|
||||
assert_eq!(cnt.get(), (i + 1) * PER_ITER);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn nesting_run() {
|
||||
let mut pool = LocalPool::new();
|
||||
let spawn = pool.spawner();
|
||||
|
||||
spawn
|
||||
.spawn_obj(
|
||||
Box::pin(lazy(|_| {
|
||||
let mut pool = LocalPool::new();
|
||||
pool.run();
|
||||
}))
|
||||
.into(),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
pool.run();
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn nesting_run_run_until_stalled() {
|
||||
let mut pool = LocalPool::new();
|
||||
let spawn = pool.spawner();
|
||||
|
||||
spawn
|
||||
.spawn_obj(
|
||||
Box::pin(lazy(|_| {
|
||||
let mut pool = LocalPool::new();
|
||||
pool.run_until_stalled();
|
||||
}))
|
||||
.into(),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
pool.run();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tasks_are_scheduled_fairly() {
|
||||
let state = Rc::new(RefCell::new([0, 0]));
|
||||
|
||||
struct Spin {
|
||||
state: Rc<RefCell<[i32; 2]>>,
|
||||
idx: usize,
|
||||
}
|
||||
|
||||
impl Future for Spin {
|
||||
type Output = ();
|
||||
|
||||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> {
|
||||
let mut state = self.state.borrow_mut();
|
||||
|
||||
if self.idx == 0 {
|
||||
let diff = state[0] - state[1];
|
||||
|
||||
assert!(diff.abs() <= 1);
|
||||
|
||||
if state[0] >= 50 {
|
||||
return Poll::Ready(());
|
||||
}
|
||||
}
|
||||
|
||||
state[self.idx] += 1;
|
||||
|
||||
if state[self.idx] >= 100 {
|
||||
return Poll::Ready(());
|
||||
}
|
||||
|
||||
cx.waker().wake_by_ref();
|
||||
Poll::Pending
|
||||
}
|
||||
}
|
||||
|
||||
let mut pool = LocalPool::new();
|
||||
let spawn = pool.spawner();
|
||||
|
||||
spawn.spawn_local_obj(Box::pin(Spin { state: state.clone(), idx: 0 }).into()).unwrap();
|
||||
|
||||
spawn.spawn_local_obj(Box::pin(Spin { state, idx: 1 }).into()).unwrap();
|
||||
|
||||
pool.run();
|
||||
}
|
||||
|
||||
// Tests that the use of park/unpark in user-code has no
|
||||
// effect on the expected behaviour of the executor.
|
||||
#[test]
|
||||
fn park_unpark_independence() {
|
||||
let mut done = false;
|
||||
|
||||
let future = future::poll_fn(move |cx| {
|
||||
if done {
|
||||
return Poll::Ready(());
|
||||
}
|
||||
done = true;
|
||||
cx.waker().clone().wake(); // (*)
|
||||
// some user-code that temporarily parks the thread
|
||||
let test = thread::current();
|
||||
let latch = Arc::new(AtomicBool::new(false));
|
||||
let signal = latch.clone();
|
||||
thread::spawn(move || {
|
||||
thread::sleep(Duration::from_millis(10));
|
||||
signal.store(true, Ordering::SeqCst);
|
||||
test.unpark()
|
||||
});
|
||||
while !latch.load(Ordering::Relaxed) {
|
||||
thread::park();
|
||||
}
|
||||
Poll::Pending // Expect to be called again due to (*).
|
||||
});
|
||||
|
||||
futures::executor::block_on(future)
|
||||
}
|
1
third_party/rust/futures-macro/.cargo-checksum.json
vendored
Normal file
1
third_party/rust/futures-macro/.cargo-checksum.json
vendored
Normal file
@ -0,0 +1 @@
|
||||
{"files":{"Cargo.toml":"e880238cae1360867ddb0456ec73c688d36a0f2c5261bc958bd0c027c44ed797","LICENSE-APACHE":"275c491d6d1160553c32fd6127061d7f9606c3ea25abfad6ca3f6ed088785427","LICENSE-MIT":"6652c868f35dfe5e8ef636810a4e576b9d663f3a17fb0f5613ad73583e1b88fd","build.rs":"d3c815ba8084078e529ecf5a1232df2995bed4f2ff2c408d16c1f2388c39081a","src/executor.rs":"2a6c40ebf1fb70ac5bd0dfb991c7b945210c731b558b546f2ecb6d7a8976f3f6","src/join.rs":"e0d286558bd944fd02c1bd2501d13e62de2aa65e6bd3a2e0567488ac1a2374ed","src/lib.rs":"aeb54392a278117b959e1d49604ea1873438a77025dca9a4c36b03bd7e8522f4","src/select.rs":"a7ed344932225fbe1b070d132a937184250c31385ac6764a8a6e6817413c7538"},"package":"a4c40298486cdf52cc00cd6d6987892ba502c7656a16a4192a9992b1ccedd121"}
|
41
third_party/rust/futures-macro/Cargo.toml
vendored
Normal file
41
third_party/rust/futures-macro/Cargo.toml
vendored
Normal file
@ -0,0 +1,41 @@
|
||||
# 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 believe there's an error in this file please file an
|
||||
# issue against the rust-lang/cargo repository. If you're
|
||||
# editing this file be aware that the upstream Cargo.toml
|
||||
# will likely look very different (and much more reasonable)
|
||||
|
||||
[package]
|
||||
edition = "2018"
|
||||
name = "futures-macro"
|
||||
version = "0.3.15"
|
||||
authors = ["Taylor Cramer <cramertj@google.com>", "Taiki Endo <te316e89@gmail.com>"]
|
||||
description = "The futures-rs procedural macro implementations.\n"
|
||||
homepage = "https://rust-lang.github.io/futures-rs"
|
||||
documentation = "https://docs.rs/futures-macro/0.3"
|
||||
license = "MIT OR Apache-2.0"
|
||||
repository = "https://github.com/rust-lang/futures-rs"
|
||||
|
||||
[lib]
|
||||
proc-macro = true
|
||||
[dependencies.proc-macro-hack]
|
||||
version = "0.5.19"
|
||||
|
||||
[dependencies.proc-macro2]
|
||||
version = "1.0"
|
||||
|
||||
[dependencies.quote]
|
||||
version = "1.0"
|
||||
|
||||
[dependencies.syn]
|
||||
version = "1.0.56"
|
||||
features = ["full"]
|
||||
[build-dependencies.autocfg]
|
||||
version = "1"
|
||||
|
||||
[features]
|
202
third_party/rust/futures-macro/LICENSE-APACHE
vendored
Normal file
202
third_party/rust/futures-macro/LICENSE-APACHE
vendored
Normal file
@ -0,0 +1,202 @@
|
||||
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 (c) 2016 Alex Crichton
|
||||
Copyright (c) 2017 The Tokio Authors
|
||||
|
||||
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.
|
26
third_party/rust/futures-macro/LICENSE-MIT
vendored
Normal file
26
third_party/rust/futures-macro/LICENSE-MIT
vendored
Normal file
@ -0,0 +1,26 @@
|
||||
Copyright (c) 2016 Alex Crichton
|
||||
Copyright (c) 2017 The Tokio Authors
|
||||
|
||||
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.
|
28
third_party/rust/futures-macro/build.rs
vendored
Normal file
28
third_party/rust/futures-macro/build.rs
vendored
Normal file
@ -0,0 +1,28 @@
|
||||
#![warn(rust_2018_idioms, single_use_lifetimes)]
|
||||
|
||||
use autocfg::AutoCfg;
|
||||
|
||||
// The rustc-cfg strings below are *not* public API. Please let us know by
|
||||
// opening a GitHub issue if your build environment requires some way to enable
|
||||
// these cfgs other than by executing our build script.
|
||||
fn main() {
|
||||
let cfg = match AutoCfg::new() {
|
||||
Ok(cfg) => cfg,
|
||||
Err(e) => {
|
||||
println!(
|
||||
"cargo:warning={}: unable to determine rustc version: {}",
|
||||
env!("CARGO_PKG_NAME"),
|
||||
e
|
||||
);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
// Function like procedural macros in expressions patterns statements stabilized in Rust 1.45:
|
||||
// https://blog.rust-lang.org/2020/07/16/Rust-1.45.0.html#stabilizing-function-like-procedural-macros-in-expressions-patterns-and-statements
|
||||
if cfg.probe_rustc_version(1, 45) {
|
||||
println!("cargo:rustc-cfg=fn_like_proc_macro");
|
||||
}
|
||||
|
||||
println!("cargo:rerun-if-changed=build.rs");
|
||||
}
|
55
third_party/rust/futures-macro/src/executor.rs
vendored
Normal file
55
third_party/rust/futures-macro/src/executor.rs
vendored
Normal file
@ -0,0 +1,55 @@
|
||||
use proc_macro::TokenStream;
|
||||
use proc_macro2::Span;
|
||||
use quote::{quote, quote_spanned, ToTokens};
|
||||
|
||||
pub(crate) fn test(args: TokenStream, item: TokenStream) -> TokenStream {
|
||||
if !args.is_empty() {
|
||||
return syn::Error::new_spanned(proc_macro2::TokenStream::from(args), "invalid argument")
|
||||
.to_compile_error()
|
||||
.into();
|
||||
}
|
||||
|
||||
let mut input = syn::parse_macro_input!(item as syn::ItemFn);
|
||||
|
||||
if input.sig.asyncness.take().is_none() {
|
||||
return syn::Error::new_spanned(input.sig.fn_token, "Only async functions are supported")
|
||||
.to_compile_error()
|
||||
.into();
|
||||
}
|
||||
|
||||
// If type mismatch occurs, the current rustc points to the last statement.
|
||||
let (last_stmt_start_span, last_stmt_end_span) = {
|
||||
let mut last_stmt = input
|
||||
.block
|
||||
.stmts
|
||||
.last()
|
||||
.map(ToTokens::into_token_stream)
|
||||
.unwrap_or_default()
|
||||
.into_iter();
|
||||
// `Span` on stable Rust has a limitation that only points to the first
|
||||
// token, not the whole tokens. We can work around this limitation by
|
||||
// using the first/last span of the tokens like
|
||||
// `syn::Error::new_spanned` does.
|
||||
let start = last_stmt.next().map_or_else(Span::call_site, |t| t.span());
|
||||
let end = last_stmt.last().map_or(start, |t| t.span());
|
||||
(start, end)
|
||||
};
|
||||
|
||||
let path = quote_spanned! {last_stmt_start_span=>
|
||||
::futures_test::__private
|
||||
};
|
||||
let body = &input.block;
|
||||
input.block.stmts = vec![syn::Stmt::Expr(
|
||||
syn::parse2(quote_spanned! {last_stmt_end_span=>
|
||||
#path::block_on(async #body)
|
||||
})
|
||||
.unwrap(),
|
||||
)];
|
||||
|
||||
let gen = quote! {
|
||||
#[::core::prelude::v1::test]
|
||||
#input
|
||||
};
|
||||
|
||||
gen.into()
|
||||
}
|
143
third_party/rust/futures-macro/src/join.rs
vendored
Normal file
143
third_party/rust/futures-macro/src/join.rs
vendored
Normal file
@ -0,0 +1,143 @@
|
||||
//! The futures-rs `join! macro implementation.
|
||||
|
||||
use proc_macro::TokenStream;
|
||||
use proc_macro2::{Span, TokenStream as TokenStream2};
|
||||
use quote::{format_ident, quote};
|
||||
use syn::parse::{Parse, ParseStream};
|
||||
use syn::{Expr, Ident, Token};
|
||||
|
||||
#[derive(Default)]
|
||||
struct Join {
|
||||
fut_exprs: Vec<Expr>,
|
||||
}
|
||||
|
||||
impl Parse for Join {
|
||||
fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
|
||||
let mut join = Self::default();
|
||||
|
||||
while !input.is_empty() {
|
||||
join.fut_exprs.push(input.parse::<Expr>()?);
|
||||
|
||||
if !input.is_empty() {
|
||||
input.parse::<Token![,]>()?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(join)
|
||||
}
|
||||
}
|
||||
|
||||
fn bind_futures(fut_exprs: Vec<Expr>, span: Span) -> (Vec<TokenStream2>, Vec<Ident>) {
|
||||
let mut future_let_bindings = Vec::with_capacity(fut_exprs.len());
|
||||
let future_names: Vec<_> = fut_exprs
|
||||
.into_iter()
|
||||
.enumerate()
|
||||
.map(|(i, expr)| {
|
||||
let name = format_ident!("_fut{}", i, span = span);
|
||||
future_let_bindings.push(quote! {
|
||||
// Move future into a local so that it is pinned in one place and
|
||||
// is no longer accessible by the end user.
|
||||
let mut #name = __futures_crate::future::maybe_done(#expr);
|
||||
});
|
||||
name
|
||||
})
|
||||
.collect();
|
||||
|
||||
(future_let_bindings, future_names)
|
||||
}
|
||||
|
||||
/// The `join!` macro.
|
||||
pub(crate) fn join(input: TokenStream) -> TokenStream {
|
||||
let parsed = syn::parse_macro_input!(input as Join);
|
||||
|
||||
// should be def_site, but that's unstable
|
||||
let span = Span::call_site();
|
||||
|
||||
let (future_let_bindings, future_names) = bind_futures(parsed.fut_exprs, span);
|
||||
|
||||
let poll_futures = future_names.iter().map(|fut| {
|
||||
quote! {
|
||||
__all_done &= __futures_crate::future::Future::poll(
|
||||
unsafe { __futures_crate::Pin::new_unchecked(&mut #fut) }, __cx).is_ready();
|
||||
}
|
||||
});
|
||||
let take_outputs = future_names.iter().map(|fut| {
|
||||
quote! {
|
||||
unsafe { __futures_crate::Pin::new_unchecked(&mut #fut) }.take_output().unwrap(),
|
||||
}
|
||||
});
|
||||
|
||||
TokenStream::from(quote! { {
|
||||
#( #future_let_bindings )*
|
||||
|
||||
__futures_crate::future::poll_fn(move |__cx: &mut __futures_crate::task::Context<'_>| {
|
||||
let mut __all_done = true;
|
||||
#( #poll_futures )*
|
||||
if __all_done {
|
||||
__futures_crate::task::Poll::Ready((
|
||||
#( #take_outputs )*
|
||||
))
|
||||
} else {
|
||||
__futures_crate::task::Poll::Pending
|
||||
}
|
||||
}).await
|
||||
} })
|
||||
}
|
||||
|
||||
/// The `try_join!` macro.
|
||||
pub(crate) fn try_join(input: TokenStream) -> TokenStream {
|
||||
let parsed = syn::parse_macro_input!(input as Join);
|
||||
|
||||
// should be def_site, but that's unstable
|
||||
let span = Span::call_site();
|
||||
|
||||
let (future_let_bindings, future_names) = bind_futures(parsed.fut_exprs, span);
|
||||
|
||||
let poll_futures = future_names.iter().map(|fut| {
|
||||
quote! {
|
||||
if __futures_crate::future::Future::poll(
|
||||
unsafe { __futures_crate::Pin::new_unchecked(&mut #fut) }, __cx).is_pending()
|
||||
{
|
||||
__all_done = false;
|
||||
} else if unsafe { __futures_crate::Pin::new_unchecked(&mut #fut) }.output_mut().unwrap().is_err() {
|
||||
// `.err().unwrap()` rather than `.unwrap_err()` so that we don't introduce
|
||||
// a `T: Debug` bound.
|
||||
// Also, for an error type of ! any code after `err().unwrap()` is unreachable.
|
||||
#[allow(unreachable_code)]
|
||||
return __futures_crate::task::Poll::Ready(
|
||||
__futures_crate::Err(
|
||||
unsafe { __futures_crate::Pin::new_unchecked(&mut #fut) }.take_output().unwrap().err().unwrap()
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
let take_outputs = future_names.iter().map(|fut| {
|
||||
quote! {
|
||||
// `.ok().unwrap()` rather than `.unwrap()` so that we don't introduce
|
||||
// an `E: Debug` bound.
|
||||
// Also, for an ok type of ! any code after `ok().unwrap()` is unreachable.
|
||||
#[allow(unreachable_code)]
|
||||
unsafe { __futures_crate::Pin::new_unchecked(&mut #fut) }.take_output().unwrap().ok().unwrap(),
|
||||
}
|
||||
});
|
||||
|
||||
TokenStream::from(quote! { {
|
||||
#( #future_let_bindings )*
|
||||
|
||||
#[allow(clippy::diverging_sub_expression)]
|
||||
__futures_crate::future::poll_fn(move |__cx: &mut __futures_crate::task::Context<'_>| {
|
||||
let mut __all_done = true;
|
||||
#( #poll_futures )*
|
||||
if __all_done {
|
||||
__futures_crate::task::Poll::Ready(
|
||||
__futures_crate::Ok((
|
||||
#( #take_outputs )*
|
||||
))
|
||||
)
|
||||
} else {
|
||||
__futures_crate::task::Poll::Pending
|
||||
}
|
||||
}).await
|
||||
} })
|
||||
}
|
54
third_party/rust/futures-macro/src/lib.rs
vendored
Normal file
54
third_party/rust/futures-macro/src/lib.rs
vendored
Normal file
@ -0,0 +1,54 @@
|
||||
//! The futures-rs procedural macro implementations.
|
||||
|
||||
#![recursion_limit = "128"]
|
||||
#![warn(rust_2018_idioms, unreachable_pub)]
|
||||
// It cannot be included in the published code because this lints have false positives in the minimum required version.
|
||||
#![cfg_attr(test, warn(single_use_lifetimes))]
|
||||
#![warn(clippy::all)]
|
||||
#![doc(test(attr(deny(warnings), allow(dead_code, unused_assignments, unused_variables))))]
|
||||
|
||||
// Since https://github.com/rust-lang/cargo/pull/7700 `proc_macro` is part of the prelude for
|
||||
// proc-macro crates, but to support older compilers we still need this explicit `extern crate`.
|
||||
#[allow(unused_extern_crates)]
|
||||
extern crate proc_macro;
|
||||
|
||||
use proc_macro::TokenStream;
|
||||
|
||||
mod executor;
|
||||
mod join;
|
||||
mod select;
|
||||
|
||||
/// The `join!` macro.
|
||||
#[cfg_attr(fn_like_proc_macro, proc_macro)]
|
||||
#[cfg_attr(not(fn_like_proc_macro), proc_macro_hack::proc_macro_hack)]
|
||||
pub fn join_internal(input: TokenStream) -> TokenStream {
|
||||
crate::join::join(input)
|
||||
}
|
||||
|
||||
/// The `try_join!` macro.
|
||||
#[cfg_attr(fn_like_proc_macro, proc_macro)]
|
||||
#[cfg_attr(not(fn_like_proc_macro), proc_macro_hack::proc_macro_hack)]
|
||||
pub fn try_join_internal(input: TokenStream) -> TokenStream {
|
||||
crate::join::try_join(input)
|
||||
}
|
||||
|
||||
/// The `select!` macro.
|
||||
#[cfg_attr(fn_like_proc_macro, proc_macro)]
|
||||
#[cfg_attr(not(fn_like_proc_macro), proc_macro_hack::proc_macro_hack)]
|
||||
pub fn select_internal(input: TokenStream) -> TokenStream {
|
||||
crate::select::select(input)
|
||||
}
|
||||
|
||||
/// The `select_biased!` macro.
|
||||
#[cfg_attr(fn_like_proc_macro, proc_macro)]
|
||||
#[cfg_attr(not(fn_like_proc_macro), proc_macro_hack::proc_macro_hack)]
|
||||
pub fn select_biased_internal(input: TokenStream) -> TokenStream {
|
||||
crate::select::select_biased(input)
|
||||
}
|
||||
|
||||
// TODO: Change this to doc comment once rustdoc bug fixed.
|
||||
// The `test` attribute.
|
||||
#[proc_macro_attribute]
|
||||
pub fn test_internal(input: TokenStream, item: TokenStream) -> TokenStream {
|
||||
crate::executor::test(input, item)
|
||||
}
|
330
third_party/rust/futures-macro/src/select.rs
vendored
Normal file
330
third_party/rust/futures-macro/src/select.rs
vendored
Normal file
@ -0,0 +1,330 @@
|
||||
//! The futures-rs `select! macro implementation.
|
||||
|
||||
use proc_macro::TokenStream;
|
||||
use proc_macro2::Span;
|
||||
use quote::{format_ident, quote};
|
||||
use syn::parse::{Parse, ParseStream};
|
||||
use syn::{parse_quote, Expr, Ident, Pat, Token};
|
||||
|
||||
mod kw {
|
||||
syn::custom_keyword!(complete);
|
||||
}
|
||||
|
||||
struct Select {
|
||||
// span of `complete`, then expression after `=> ...`
|
||||
complete: Option<Expr>,
|
||||
default: Option<Expr>,
|
||||
normal_fut_exprs: Vec<Expr>,
|
||||
normal_fut_handlers: Vec<(Pat, Expr)>,
|
||||
}
|
||||
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
enum CaseKind {
|
||||
Complete,
|
||||
Default,
|
||||
Normal(Pat, Expr),
|
||||
}
|
||||
|
||||
impl Parse for Select {
|
||||
fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
|
||||
let mut select = Self {
|
||||
complete: None,
|
||||
default: None,
|
||||
normal_fut_exprs: vec![],
|
||||
normal_fut_handlers: vec![],
|
||||
};
|
||||
|
||||
while !input.is_empty() {
|
||||
let case_kind = if input.peek(kw::complete) {
|
||||
// `complete`
|
||||
if select.complete.is_some() {
|
||||
return Err(input.error("multiple `complete` cases found, only one allowed"));
|
||||
}
|
||||
input.parse::<kw::complete>()?;
|
||||
CaseKind::Complete
|
||||
} else if input.peek(Token![default]) {
|
||||
// `default`
|
||||
if select.default.is_some() {
|
||||
return Err(input.error("multiple `default` cases found, only one allowed"));
|
||||
}
|
||||
input.parse::<Ident>()?;
|
||||
CaseKind::Default
|
||||
} else {
|
||||
// `<pat> = <expr>`
|
||||
let pat = input.parse()?;
|
||||
input.parse::<Token![=]>()?;
|
||||
let expr = input.parse()?;
|
||||
CaseKind::Normal(pat, expr)
|
||||
};
|
||||
|
||||
// `=> <expr>`
|
||||
input.parse::<Token![=>]>()?;
|
||||
let expr = input.parse::<Expr>()?;
|
||||
|
||||
// Commas after the expression are only optional if it's a `Block`
|
||||
// or it is the last branch in the `match`.
|
||||
let is_block = match expr {
|
||||
Expr::Block(_) => true,
|
||||
_ => false,
|
||||
};
|
||||
if is_block || input.is_empty() {
|
||||
input.parse::<Option<Token![,]>>()?;
|
||||
} else {
|
||||
input.parse::<Token![,]>()?;
|
||||
}
|
||||
|
||||
match case_kind {
|
||||
CaseKind::Complete => select.complete = Some(expr),
|
||||
CaseKind::Default => select.default = Some(expr),
|
||||
CaseKind::Normal(pat, fut_expr) => {
|
||||
select.normal_fut_exprs.push(fut_expr);
|
||||
select.normal_fut_handlers.push((pat, expr));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(select)
|
||||
}
|
||||
}
|
||||
|
||||
// Enum over all the cases in which the `select!` waiting has completed and the result
|
||||
// can be processed.
|
||||
//
|
||||
// `enum __PrivResult<_1, _2, ...> { _1(_1), _2(_2), ..., Complete }`
|
||||
fn declare_result_enum(
|
||||
result_ident: Ident,
|
||||
variants: usize,
|
||||
complete: bool,
|
||||
span: Span,
|
||||
) -> (Vec<Ident>, syn::ItemEnum) {
|
||||
// "_0", "_1", "_2"
|
||||
let variant_names: Vec<Ident> =
|
||||
(0..variants).map(|num| format_ident!("_{}", num, span = span)).collect();
|
||||
|
||||
let type_parameters = &variant_names;
|
||||
let variants = &variant_names;
|
||||
|
||||
let complete_variant = if complete { Some(quote!(Complete)) } else { None };
|
||||
|
||||
let enum_item = parse_quote! {
|
||||
enum #result_ident<#(#type_parameters,)*> {
|
||||
#(
|
||||
#variants(#type_parameters),
|
||||
)*
|
||||
#complete_variant
|
||||
}
|
||||
};
|
||||
|
||||
(variant_names, enum_item)
|
||||
}
|
||||
|
||||
/// The `select!` macro.
|
||||
pub(crate) fn select(input: TokenStream) -> TokenStream {
|
||||
select_inner(input, true)
|
||||
}
|
||||
|
||||
/// The `select_biased!` macro.
|
||||
pub(crate) fn select_biased(input: TokenStream) -> TokenStream {
|
||||
select_inner(input, false)
|
||||
}
|
||||
|
||||
fn select_inner(input: TokenStream, random: bool) -> TokenStream {
|
||||
let parsed = syn::parse_macro_input!(input as Select);
|
||||
|
||||
// should be def_site, but that's unstable
|
||||
let span = Span::call_site();
|
||||
|
||||
let enum_ident = Ident::new("__PrivResult", span);
|
||||
|
||||
let (variant_names, enum_item) = declare_result_enum(
|
||||
enum_ident.clone(),
|
||||
parsed.normal_fut_exprs.len(),
|
||||
parsed.complete.is_some(),
|
||||
span,
|
||||
);
|
||||
|
||||
// bind non-`Ident` future exprs w/ `let`
|
||||
let mut future_let_bindings = Vec::with_capacity(parsed.normal_fut_exprs.len());
|
||||
let bound_future_names: Vec<_> = parsed
|
||||
.normal_fut_exprs
|
||||
.into_iter()
|
||||
.zip(variant_names.iter())
|
||||
.map(|(expr, variant_name)| {
|
||||
match expr {
|
||||
syn::Expr::Path(path) => {
|
||||
// Don't bind futures that are already a path.
|
||||
// This prevents creating redundant stack space
|
||||
// for them.
|
||||
// Passing Futures by path requires those Futures to implement Unpin.
|
||||
// We check for this condition here in order to be able to
|
||||
// safely use Pin::new_unchecked(&mut #path) later on.
|
||||
future_let_bindings.push(quote! {
|
||||
__futures_crate::async_await::assert_fused_future(&#path);
|
||||
__futures_crate::async_await::assert_unpin(&#path);
|
||||
});
|
||||
path
|
||||
}
|
||||
_ => {
|
||||
// Bind and pin the resulting Future on the stack. This is
|
||||
// necessary to support direct select! calls on !Unpin
|
||||
// Futures. The Future is not explicitly pinned here with
|
||||
// a Pin call, but assumed as pinned. The actual Pin is
|
||||
// created inside the poll() function below to defer the
|
||||
// creation of the temporary pointer, which would otherwise
|
||||
// increase the size of the generated Future.
|
||||
// Safety: This is safe since the lifetime of the Future
|
||||
// is totally constraint to the lifetime of the select!
|
||||
// expression, and the Future can't get moved inside it
|
||||
// (it is shadowed).
|
||||
future_let_bindings.push(quote! {
|
||||
let mut #variant_name = #expr;
|
||||
});
|
||||
parse_quote! { #variant_name }
|
||||
}
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
// For each future, make an `&mut dyn FnMut(&mut Context<'_>) -> Option<Poll<__PrivResult<...>>`
|
||||
// to use for polling that individual future. These will then be put in an array.
|
||||
let poll_functions = bound_future_names.iter().zip(variant_names.iter()).map(
|
||||
|(bound_future_name, variant_name)| {
|
||||
// Below we lazily create the Pin on the Future below.
|
||||
// This is done in order to avoid allocating memory in the generator
|
||||
// for the Pin variable.
|
||||
// Safety: This is safe because one of the following condition applies:
|
||||
// 1. The Future is passed by the caller by name, and we assert that
|
||||
// it implements Unpin.
|
||||
// 2. The Future is created in scope of the select! function and will
|
||||
// not be moved for the duration of it. It is thereby stack-pinned
|
||||
quote! {
|
||||
let mut #variant_name = |__cx: &mut __futures_crate::task::Context<'_>| {
|
||||
let mut #bound_future_name = unsafe {
|
||||
__futures_crate::Pin::new_unchecked(&mut #bound_future_name)
|
||||
};
|
||||
if __futures_crate::future::FusedFuture::is_terminated(&#bound_future_name) {
|
||||
__futures_crate::None
|
||||
} else {
|
||||
__futures_crate::Some(__futures_crate::future::FutureExt::poll_unpin(
|
||||
&mut #bound_future_name,
|
||||
__cx,
|
||||
).map(#enum_ident::#variant_name))
|
||||
}
|
||||
};
|
||||
let #variant_name: &mut dyn FnMut(
|
||||
&mut __futures_crate::task::Context<'_>
|
||||
) -> __futures_crate::Option<__futures_crate::task::Poll<_>> = &mut #variant_name;
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
let none_polled = if parsed.complete.is_some() {
|
||||
quote! {
|
||||
__futures_crate::task::Poll::Ready(#enum_ident::Complete)
|
||||
}
|
||||
} else {
|
||||
quote! {
|
||||
panic!("all futures in select! were completed,\
|
||||
but no `complete =>` handler was provided")
|
||||
}
|
||||
};
|
||||
|
||||
let branches = parsed.normal_fut_handlers.into_iter().zip(variant_names.iter()).map(
|
||||
|((pat, expr), variant_name)| {
|
||||
quote! {
|
||||
#enum_ident::#variant_name(#pat) => { #expr },
|
||||
}
|
||||
},
|
||||
);
|
||||
let branches = quote! { #( #branches )* };
|
||||
|
||||
let complete_branch = parsed.complete.map(|complete_expr| {
|
||||
quote! {
|
||||
#enum_ident::Complete => { #complete_expr },
|
||||
}
|
||||
});
|
||||
|
||||
let branches = quote! {
|
||||
#branches
|
||||
#complete_branch
|
||||
};
|
||||
|
||||
let await_select_fut = if parsed.default.is_some() {
|
||||
// For select! with default this returns the Poll result
|
||||
quote! {
|
||||
__poll_fn(&mut __futures_crate::task::Context::from_waker(
|
||||
__futures_crate::task::noop_waker_ref()
|
||||
))
|
||||
}
|
||||
} else {
|
||||
quote! {
|
||||
__futures_crate::future::poll_fn(__poll_fn).await
|
||||
}
|
||||
};
|
||||
|
||||
let execute_result_expr = if let Some(default_expr) = &parsed.default {
|
||||
// For select! with default __select_result is a Poll, otherwise not
|
||||
quote! {
|
||||
match __select_result {
|
||||
__futures_crate::task::Poll::Ready(result) => match result {
|
||||
#branches
|
||||
},
|
||||
_ => #default_expr
|
||||
}
|
||||
}
|
||||
} else {
|
||||
quote! {
|
||||
match __select_result {
|
||||
#branches
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let shuffle = if random {
|
||||
quote! {
|
||||
__futures_crate::async_await::shuffle(&mut __select_arr);
|
||||
}
|
||||
} else {
|
||||
quote!()
|
||||
};
|
||||
|
||||
TokenStream::from(quote! { {
|
||||
#enum_item
|
||||
|
||||
let __select_result = {
|
||||
#( #future_let_bindings )*
|
||||
|
||||
let mut __poll_fn = |__cx: &mut __futures_crate::task::Context<'_>| {
|
||||
let mut __any_polled = false;
|
||||
|
||||
#( #poll_functions )*
|
||||
|
||||
let mut __select_arr = [#( #variant_names ),*];
|
||||
#shuffle
|
||||
for poller in &mut __select_arr {
|
||||
let poller: &mut &mut dyn FnMut(
|
||||
&mut __futures_crate::task::Context<'_>
|
||||
) -> __futures_crate::Option<__futures_crate::task::Poll<_>> = poller;
|
||||
match poller(__cx) {
|
||||
__futures_crate::Some(x @ __futures_crate::task::Poll::Ready(_)) =>
|
||||
return x,
|
||||
__futures_crate::Some(__futures_crate::task::Poll::Pending) => {
|
||||
__any_polled = true;
|
||||
}
|
||||
__futures_crate::None => {}
|
||||
}
|
||||
}
|
||||
|
||||
if !__any_polled {
|
||||
#none_polled
|
||||
} else {
|
||||
__futures_crate::task::Poll::Pending
|
||||
}
|
||||
};
|
||||
|
||||
#await_select_fut
|
||||
};
|
||||
|
||||
#execute_result_expr
|
||||
} })
|
||||
}
|
1
third_party/rust/l10nregistry/.cargo-checksum.json
vendored
Normal file
1
third_party/rust/l10nregistry/.cargo-checksum.json
vendored
Normal file
@ -0,0 +1 @@
|
||||
{"files":{"Cargo.toml":"5874fa7dbce795209a9312250fee6db8c807ddfa53df4926bebcc9b718bc2281","LICENSE-APACHE":"5db2b182453ff32ed40f7da63589c9667a3f8bd8b16b1471b152caae56f77e45","LICENSE-MIT":"49c0b000c03731d9e3970dc059ad4ca345d773681f4a612b0024435b663e0220","benches/localization.rs":"879b46b5cc6b9c8bade51796acb0de0c5dbaa197c24b644592db06f976d39208","benches/preferences.rs":"8f5242b2166896da6219be57c5cb48f5b995a884b3bafa08358ce5206b3ad098","benches/solver.rs":"dbffd67c583b76b64499b9bf5163e40b7238afafa7edee5aabb2f04e956a15a6","benches/source.rs":"5efb5e562509aeab6a643739251d2b31994e0240316ecf407ca2188f14cbcf5d","src/env.rs":"213679dd9ef5d2aa8049f0351c2caa00e8890f788ff8a237cd8fccb01d061cdb","src/errors.rs":"f025bd1674c16b85b3097bf86831f0e8a0860f00b7151293e94206e456bd6959","src/fluent.rs":"ccf3ed58e1281e28ab36e75157778452d7c5323f5b08ff48fbea7bb45b70d361","src/lib.rs":"c2bba574aa4c1258d88695f7410175ccf04c3adf1ea3fb10769a0c9f4e07b133","src/registry/asynchronous.rs":"a392c5463caf7eeb9dc27c311d08361337b14fc6b05e83fedbe5683c66967a94","src/registry/mod.rs":"8b5077b30d3a0bde13270552009eaad0aece9c447f3c5fe210b56a1aedc6cddf","src/registry/synchronous.rs":"2c5be6ed23c2b365534d721e88e7a4bae78df22e3c1aeb70172393a226d01d97","src/solver/README.md":"bd93b3faef6adec5e71f06a7e06ca64b32f3877b513de099638fa86acf1e2d9e","src/solver/mod.rs":"f763519feca67ce6ce42c4387482fb10313f9f33f7d0fe687e0db51ef9592a6b","src/solver/parallel.rs":"c4c4edc38d8f639b0df057c12e0f4f0e0026e3f2fb3ad9563689d2620dd99fea","src/solver/serial.rs":"dcac1128fc87295b955c11b40b7adca5106a585de9adc2e4361cc6a92c968e27","src/solver/testing/mod.rs":"9f8b3d5b3436088d410d5544d2c2e33320bb56b0d364f1862790e4ab66fb7764","src/solver/testing/scenarios.rs":"2a11b2f3a6bbfe0055ea6125cae498391e28a4811c019e2f9479f959798e5767","src/source/fetcher.rs":"6220bf5010df94eb92f1adc56f76a65b41227b59312e27963d75b16ea0ee6393","src/source/mod.rs":"3e507f5775f71abc55acdd3e9a4be34f241c4e889244caae0142e0bc2ecae109","src/testing.rs":"4408fc78efb0d3dd52d4f8cb669914ef96531fd94796a5a2f4e1f65bc54f0e5f","tests/localization.rs":"5c2f4e75f03d4e42880adde4a6902e60b0c87d14160663e495cc6ed3091b4285","tests/registry.rs":"f9da785b408f0d54b2ca8f0bd7f52e86e75e9a642fbb91f57be870160024cbb6","tests/scenarios.rs":"ffe16cb382895bdbe809f63233fc1ac9a4be9022bc7b60d2f3210b49653569e8","tests/source.rs":"c43bbcc6f2e0c34766785cd1be94a3ed34f7f6327a1693f5dce62bcae7ac1d39"},"package":null}
|
63
third_party/rust/l10nregistry/Cargo.toml
vendored
Normal file
63
third_party/rust/l10nregistry/Cargo.toml
vendored
Normal file
@ -0,0 +1,63 @@
|
||||
[package]
|
||||
name = "l10nregistry"
|
||||
version = "0.2.0"
|
||||
authors = ["Zibi Braniecki <gandalf@mozilla.com>"]
|
||||
license = "Apache-2.0/MIT"
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
async-trait = "0.1"
|
||||
fluent-bundle = "0.15"
|
||||
fluent-fallback = "0.5"
|
||||
fluent-testing = { git = "https://github.com/projectfluent/fluent-rs", optional = true, features = ["sync", "async"] }
|
||||
futures = "0.3"
|
||||
pin-project-lite = "0.2"
|
||||
unic-langid = "0.9"
|
||||
tokio = { version = "1.0", optional = true, features = ["rt-multi-thread", "macros"] }
|
||||
replace_with = "0.1"
|
||||
rustc-hash = "1"
|
||||
|
||||
[dev-dependencies]
|
||||
unic-langid = { version = "0.9", features = ["macros"] }
|
||||
serial_test = "0.5"
|
||||
criterion = "0.3"
|
||||
|
||||
[features]
|
||||
default = []
|
||||
tokio-io = ["tokio"]
|
||||
|
||||
[[bench]]
|
||||
name = "preferences"
|
||||
harness = false
|
||||
|
||||
[[bench]]
|
||||
name = "localization"
|
||||
harness = false
|
||||
|
||||
[[bench]]
|
||||
name = "source"
|
||||
harness = false
|
||||
|
||||
[[bench]]
|
||||
name = "solver"
|
||||
harness = false
|
||||
|
||||
[[test]]
|
||||
name = "source"
|
||||
path = "tests/source.rs"
|
||||
required-features = ["tokio", "fluent-testing"]
|
||||
|
||||
[[test]]
|
||||
name = "registry"
|
||||
path = "tests/registry.rs"
|
||||
required-features = ["tokio", "fluent-testing"]
|
||||
|
||||
[[test]]
|
||||
name = "localization"
|
||||
path = "tests/localization.rs"
|
||||
required-features = ["tokio", "fluent-testing"]
|
||||
|
||||
[[test]]
|
||||
name = "scenarios"
|
||||
path = "tests/scenarios.rs"
|
||||
required-features = ["tokio", "fluent-testing"]
|
201
third_party/rust/l10nregistry/LICENSE-APACHE
vendored
Normal file
201
third_party/rust/l10nregistry/LICENSE-APACHE
vendored
Normal 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 2017 Mozilla
|
||||
|
||||
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.
|
19
third_party/rust/l10nregistry/LICENSE-MIT
vendored
Normal file
19
third_party/rust/l10nregistry/LICENSE-MIT
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
Copyright 2017 Mozilla
|
||||
|
||||
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.
|
70
third_party/rust/l10nregistry/benches/localization.rs
vendored
Normal file
70
third_party/rust/l10nregistry/benches/localization.rs
vendored
Normal file
@ -0,0 +1,70 @@
|
||||
use criterion::criterion_group;
|
||||
use criterion::criterion_main;
|
||||
use criterion::Criterion;
|
||||
|
||||
use fluent_bundle::FluentArgs;
|
||||
use fluent_fallback::{types::L10nKey, Localization};
|
||||
use fluent_testing::get_scenarios;
|
||||
use l10nregistry::testing::TestFileFetcher;
|
||||
|
||||
fn preferences_bench(c: &mut Criterion) {
|
||||
let fetcher = TestFileFetcher::new();
|
||||
|
||||
let mut group = c.benchmark_group("localization/scenarios");
|
||||
|
||||
for scenario in get_scenarios() {
|
||||
let res_ids = scenario.res_ids.clone();
|
||||
let l10n_keys: Vec<(String, Option<FluentArgs>)> = scenario
|
||||
.queries
|
||||
.iter()
|
||||
.map(|q| {
|
||||
(
|
||||
q.input.id.clone(),
|
||||
q.input.args.as_ref().map(|args| {
|
||||
let mut result = FluentArgs::new();
|
||||
for arg in args.as_slice() {
|
||||
result.set(arg.id.clone(), arg.value.clone());
|
||||
}
|
||||
result
|
||||
}),
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
|
||||
group.bench_function(format!("{}/format_value_sync", scenario.name), |b| {
|
||||
b.iter(|| {
|
||||
let (env, reg) = fetcher.get_registry_and_environment(&scenario);
|
||||
let mut errors = vec![];
|
||||
|
||||
let loc = Localization::with_env(res_ids.clone(), true, env.clone(), reg.clone());
|
||||
let bundles = loc.bundles();
|
||||
|
||||
for key in l10n_keys.iter() {
|
||||
bundles.format_value_sync(&key.0, key.1.as_ref(), &mut errors);
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
let keys: Vec<L10nKey> = l10n_keys
|
||||
.into_iter()
|
||||
.map(|key| L10nKey {
|
||||
id: key.0.into(),
|
||||
args: key.1,
|
||||
})
|
||||
.collect();
|
||||
group.bench_function(format!("{}/format_messages_sync", scenario.name), |b| {
|
||||
b.iter(|| {
|
||||
let (env, reg) = fetcher.get_registry_and_environment(&scenario);
|
||||
let mut errors = vec![];
|
||||
let loc = Localization::with_env(res_ids.clone(), true, env.clone(), reg.clone());
|
||||
let bundles = loc.bundles();
|
||||
bundles.format_messages_sync(&keys, &mut errors);
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
group.finish();
|
||||
}
|
||||
|
||||
criterion_group!(benches, preferences_bench);
|
||||
criterion_main!(benches);
|
57
third_party/rust/l10nregistry/benches/preferences.rs
vendored
Normal file
57
third_party/rust/l10nregistry/benches/preferences.rs
vendored
Normal file
@ -0,0 +1,57 @@
|
||||
use criterion::criterion_group;
|
||||
use criterion::criterion_main;
|
||||
use criterion::Criterion;
|
||||
|
||||
use fluent_testing::get_scenarios;
|
||||
use l10nregistry::testing::TestFileFetcher;
|
||||
|
||||
use unic_langid::LanguageIdentifier;
|
||||
|
||||
fn preferences_bench(c: &mut Criterion) {
|
||||
let fetcher = TestFileFetcher::new();
|
||||
|
||||
let mut group = c.benchmark_group("registry/scenarios");
|
||||
|
||||
for scenario in get_scenarios() {
|
||||
let res_ids = scenario.res_ids.clone();
|
||||
|
||||
let locales: Vec<LanguageIdentifier> = scenario
|
||||
.locales
|
||||
.iter()
|
||||
.map(|l| l.parse().unwrap())
|
||||
.collect();
|
||||
|
||||
group.bench_function(format!("{}/sync/first_bundle", scenario.name), |b| {
|
||||
b.iter(|| {
|
||||
let reg = fetcher.get_registry(&scenario);
|
||||
let mut bundles =
|
||||
reg.generate_bundles_sync(locales.clone().into_iter(), res_ids.clone());
|
||||
assert!(bundles.next().is_some());
|
||||
})
|
||||
});
|
||||
|
||||
#[cfg(feature = "tokio")]
|
||||
{
|
||||
use futures::stream::StreamExt;
|
||||
|
||||
let rt = tokio::runtime::Runtime::new().unwrap();
|
||||
|
||||
group.bench_function(&format!("{}/async/first_bundle", scenario.name), |b| {
|
||||
b.iter(|| {
|
||||
rt.block_on(async {
|
||||
let reg = fetcher.get_registry(&scenario);
|
||||
|
||||
let mut bundles =
|
||||
reg.generate_bundles(locales.clone().into_iter(), res_ids.clone());
|
||||
assert!(bundles.next().await.is_some());
|
||||
});
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
group.finish();
|
||||
}
|
||||
|
||||
criterion_group!(benches, preferences_bench);
|
||||
criterion_main!(benches);
|
120
third_party/rust/l10nregistry/benches/solver.rs
vendored
Normal file
120
third_party/rust/l10nregistry/benches/solver.rs
vendored
Normal file
@ -0,0 +1,120 @@
|
||||
use criterion::criterion_group;
|
||||
use criterion::criterion_main;
|
||||
use criterion::Criterion;
|
||||
|
||||
use futures::stream::Collect;
|
||||
use futures::stream::FuturesOrdered;
|
||||
use futures::StreamExt;
|
||||
use l10nregistry::solver::testing::get_scenarios;
|
||||
use l10nregistry::solver::{AsyncTester, ParallelProblemSolver, SerialProblemSolver, SyncTester};
|
||||
use std::future::Future;
|
||||
use std::pin::Pin;
|
||||
use std::task::{Context, Poll};
|
||||
|
||||
pub struct MockTester {
|
||||
values: Vec<Vec<bool>>,
|
||||
}
|
||||
|
||||
impl SyncTester for MockTester {
|
||||
fn test_sync(&self, res_idx: usize, source_idx: usize) -> bool {
|
||||
self.values[res_idx][source_idx]
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SingleTestResult(bool);
|
||||
|
||||
impl Future for SingleTestResult {
|
||||
type Output = bool;
|
||||
|
||||
fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
self.0.into()
|
||||
}
|
||||
}
|
||||
|
||||
pub type ResourceSetStream = Collect<FuturesOrdered<SingleTestResult>, Vec<bool>>;
|
||||
pub struct TestResult(ResourceSetStream);
|
||||
|
||||
impl std::marker::Unpin for TestResult {}
|
||||
|
||||
impl Future for TestResult {
|
||||
type Output = Vec<bool>;
|
||||
|
||||
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
let pinned = Pin::new(&mut self.0);
|
||||
pinned.poll(cx)
|
||||
}
|
||||
}
|
||||
|
||||
impl AsyncTester for MockTester {
|
||||
type Result = TestResult;
|
||||
|
||||
fn test_async(&self, query: Vec<(usize, usize)>) -> Self::Result {
|
||||
let futures = query
|
||||
.into_iter()
|
||||
.map(|(res_idx, source_idx)| SingleTestResult(self.test_sync(res_idx, source_idx)))
|
||||
.collect::<Vec<_>>();
|
||||
TestResult(futures.into_iter().collect::<FuturesOrdered<_>>().collect())
|
||||
}
|
||||
}
|
||||
|
||||
struct TestStream<'t> {
|
||||
solver: ParallelProblemSolver<MockTester>,
|
||||
tester: &'t MockTester,
|
||||
}
|
||||
|
||||
impl<'t> TestStream<'t> {
|
||||
pub fn new(solver: ParallelProblemSolver<MockTester>, tester: &'t MockTester) -> Self {
|
||||
Self { solver, tester }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'t> futures::stream::Stream for TestStream<'t> {
|
||||
type Item = Vec<usize>;
|
||||
|
||||
fn poll_next(
|
||||
mut self: std::pin::Pin<&mut Self>,
|
||||
cx: &mut std::task::Context<'_>,
|
||||
) -> std::task::Poll<Option<Self::Item>> {
|
||||
let tester = self.tester;
|
||||
let solver = &mut self.solver;
|
||||
let pinned = std::pin::Pin::new(solver);
|
||||
pinned
|
||||
.try_poll_next(cx, tester, false)
|
||||
.map(|v| v.ok().flatten())
|
||||
}
|
||||
}
|
||||
|
||||
fn solver_bench(c: &mut Criterion) {
|
||||
let scenarios = get_scenarios();
|
||||
|
||||
let mut group = c.benchmark_group("solver");
|
||||
|
||||
for scenario in scenarios {
|
||||
let tester = MockTester {
|
||||
values: scenario.values.clone(),
|
||||
};
|
||||
|
||||
group.bench_function(&format!("serial/{}", &scenario.name), |b| {
|
||||
b.iter(|| {
|
||||
let mut gen = SerialProblemSolver::new(scenario.width, scenario.depth);
|
||||
while let Ok(Some(_)) = gen.try_next(&tester, false) {}
|
||||
})
|
||||
});
|
||||
|
||||
{
|
||||
let rt = tokio::runtime::Runtime::new().unwrap();
|
||||
|
||||
group.bench_function(&format!("parallel/{}", &scenario.name), |b| {
|
||||
b.iter(|| {
|
||||
let gen = ParallelProblemSolver::new(scenario.width, scenario.depth);
|
||||
let mut t = TestStream::new(gen, &tester);
|
||||
rt.block_on(async { while let Some(_) = t.next().await {} });
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
group.finish();
|
||||
}
|
||||
|
||||
criterion_group!(benches, solver_bench);
|
||||
criterion_main!(benches);
|
58
third_party/rust/l10nregistry/benches/source.rs
vendored
Normal file
58
third_party/rust/l10nregistry/benches/source.rs
vendored
Normal file
@ -0,0 +1,58 @@
|
||||
use criterion::criterion_group;
|
||||
use criterion::criterion_main;
|
||||
use criterion::Criterion;
|
||||
|
||||
use fluent_testing::get_scenarios;
|
||||
use l10nregistry::testing::TestFileFetcher;
|
||||
|
||||
use unic_langid::LanguageIdentifier;
|
||||
|
||||
fn get_locales<S>(input: &[S]) -> Vec<LanguageIdentifier>
|
||||
where
|
||||
S: AsRef<str>,
|
||||
{
|
||||
input.iter().map(|s| s.as_ref().parse().unwrap()).collect()
|
||||
}
|
||||
|
||||
fn source_bench(c: &mut Criterion) {
|
||||
let fetcher = TestFileFetcher::new();
|
||||
|
||||
let mut group = c.benchmark_group("source/scenarios");
|
||||
|
||||
for scenario in get_scenarios() {
|
||||
let res_ids = scenario.res_ids.clone();
|
||||
|
||||
let locales: Vec<LanguageIdentifier> = get_locales(&scenario.locales);
|
||||
|
||||
let sources: Vec<_> = scenario
|
||||
.file_sources
|
||||
.iter()
|
||||
.map(|s| fetcher.get_test_file_source(&s.name, get_locales(&s.locales), &s.path_scheme))
|
||||
.collect();
|
||||
|
||||
group.bench_function(format!("{}/has_file", scenario.name), |b| {
|
||||
b.iter(|| {
|
||||
for source in &sources {
|
||||
for res_id in &res_ids {
|
||||
source.has_file(&locales[0], &res_id);
|
||||
}
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
group.bench_function(format!("{}/sync/fetch_file_sync", scenario.name), |b| {
|
||||
b.iter(|| {
|
||||
for source in &sources {
|
||||
for res_id in &res_ids {
|
||||
source.fetch_file_sync(&locales[0], &res_id, false);
|
||||
}
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
group.finish();
|
||||
}
|
||||
|
||||
criterion_group!(benches, source_bench);
|
||||
criterion_main!(benches);
|
5
third_party/rust/l10nregistry/src/env.rs
vendored
Normal file
5
third_party/rust/l10nregistry/src/env.rs
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
use crate::errors::L10nRegistryError;
|
||||
|
||||
pub trait ErrorReporter {
|
||||
fn report_errors(&self, errors: Vec<L10nRegistryError>);
|
||||
}
|
62
third_party/rust/l10nregistry/src/errors.rs
vendored
Normal file
62
third_party/rust/l10nregistry/src/errors.rs
vendored
Normal file
@ -0,0 +1,62 @@
|
||||
use fluent_bundle::FluentError;
|
||||
use std::error::Error;
|
||||
use unic_langid::LanguageIdentifier;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum L10nRegistryError {
|
||||
FluentError {
|
||||
path: String,
|
||||
loc: Option<(usize, usize)>,
|
||||
error: FluentError,
|
||||
},
|
||||
MissingResource {
|
||||
locale: LanguageIdentifier,
|
||||
res_id: String,
|
||||
},
|
||||
}
|
||||
|
||||
impl std::fmt::Display for L10nRegistryError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::MissingResource { locale, res_id } => {
|
||||
write!(f, "Missing resource in locale {}: {}", locale, res_id)
|
||||
}
|
||||
Self::FluentError { path, loc, error } => {
|
||||
if let Some(loc) = loc {
|
||||
write!(
|
||||
f,
|
||||
"Fluent Error in {}[line: {}, col: {}]: {}",
|
||||
path, loc.0, loc.1, error
|
||||
)
|
||||
} else {
|
||||
write!(f, "Fluent Error in {}: {}", path, error)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Error for L10nRegistryError {}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum L10nRegistrySetupError {
|
||||
RegistryLocked,
|
||||
DuplicatedSource { name: String },
|
||||
MissingSource { name: String },
|
||||
}
|
||||
|
||||
impl std::fmt::Display for L10nRegistrySetupError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::RegistryLocked => write!(f, "Can't modify a registry when locked."),
|
||||
Self::DuplicatedSource { name } => {
|
||||
write!(f, "Source with a name {} is already registered.", &name)
|
||||
}
|
||||
Self::MissingSource { name } => {
|
||||
write!(f, "Cannot find a source with a name {}.", &name)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Error for L10nRegistrySetupError {}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user