From 0157246cee99520862c2b7df0a3f090c7520196d Mon Sep 17 00:00:00 2001 From: Alex Franchuk Date: Tue, 4 Jun 2024 19:35:35 +0000 Subject: [PATCH] Bug 1900504 - Update the time crate to version 0.3.36 r=glandium,supply-chain-reviewers Differential Revision: https://phabricator.services.mozilla.com/D212497 --- Cargo.lock | 50 +- supply-chain/audits.toml | 48 + .../rust/deranged/.cargo-checksum.json | 1 + third_party/rust/deranged/Cargo.toml | 83 + third_party/rust/deranged/LICENSE-Apache | 202 +++ third_party/rust/deranged/LICENSE-MIT | 19 + third_party/rust/deranged/README.md | 3 + third_party/rust/deranged/src/lib.rs | 1474 +++++++++++++++++ third_party/rust/deranged/src/tests.rs | 688 ++++++++ third_party/rust/deranged/src/traits.rs | 117 ++ .../rust/deranged/src/unsafe_wrapper.rs | 26 + .../rust/num-conv/.cargo-checksum.json | 1 + third_party/rust/num-conv/Cargo.toml | 55 + third_party/rust/num-conv/LICENSE-Apache | 202 +++ third_party/rust/num-conv/LICENSE-MIT | 19 + third_party/rust/num-conv/src/lib.rs | 329 ++++ .../rust/powerfmt/.cargo-checksum.json | 1 + third_party/rust/powerfmt/Cargo.toml | 59 + third_party/rust/powerfmt/LICENSE-Apache | 202 +++ third_party/rust/powerfmt/LICENSE-MIT | 19 + third_party/rust/powerfmt/README.md | 45 + third_party/rust/powerfmt/src/buf.rs | 198 +++ third_party/rust/powerfmt/src/ext.rs | 54 + third_party/rust/powerfmt/src/lib.rs | 15 + .../rust/powerfmt/src/smart_display.rs | 695 ++++++++ .../rust/powerfmt/src/smart_display_impls.rs | 303 ++++ .../rust/time-core/.cargo-checksum.json | 2 +- third_party/rust/time-core/Cargo.toml | 7 +- third_party/rust/time-core/src/convert.rs | 158 +- third_party/rust/time-core/src/lib.rs | 41 - .../rust/time-macros/.cargo-checksum.json | 2 +- third_party/rust/time-macros/Cargo.toml | 96 +- third_party/rust/time-macros/LICENSE-Apache | 2 +- third_party/rust/time-macros/LICENSE-MIT | 2 +- third_party/rust/time-macros/src/date.rs | 13 +- .../time-macros/src/format_description/ast.rs | 1 - .../src/format_description/format_item.rs | 14 +- .../src/format_description/lexer.rs | 2 +- .../time-macros/src/format_description/mod.rs | 6 +- .../format_description/public/component.rs | 1 + .../src/format_description/public/mod.rs | 10 +- .../src/format_description/public/modifier.rs | 12 +- .../rust/time-macros/src/helpers/mod.rs | 16 +- third_party/rust/time-macros/src/lib.rs | 45 +- third_party/rust/time-macros/src/offset.rs | 19 +- third_party/rust/time-macros/src/quote.rs | 3 +- third_party/rust/time-macros/src/time.rs | 22 +- third_party/rust/time-macros/src/to_tokens.rs | 1 + third_party/rust/time/.cargo-checksum.json | 2 +- third_party/rust/time/Cargo.toml | 135 +- third_party/rust/time/LICENSE-Apache | 2 +- third_party/rust/time/LICENSE-MIT | 2 +- third_party/rust/time/README.md | 2 +- third_party/rust/time/src/date.rs | 450 ++++- third_party/rust/time/src/date_time.rs | 1180 ------------- third_party/rust/time/src/duration.rs | 422 +++-- third_party/rust/time/src/error/mod.rs | 24 +- third_party/rust/time/src/error/parse.rs | 21 +- .../time/src/error/parse_from_description.rs | 6 + third_party/rust/time/src/ext.rs | 280 ---- third_party/rust/time/src/ext/digit_count.rs | 26 + third_party/rust/time/src/ext/instant.rs | 100 ++ third_party/rust/time/src/ext/mod.rs | 13 + .../rust/time/src/ext/numerical_duration.rs | 140 ++ .../time/src/ext/numerical_std_duration.rs | 192 +++ .../borrowed_format_item.rs | 17 +- .../time/src/format_description/component.rs | 3 + .../rust/time/src/format_description/mod.rs | 4 +- .../time/src/format_description/modifier.rs | 15 +- .../format_description/owned_format_item.rs | 32 +- .../time/src/format_description/parse/ast.rs | 1 + .../format_description/parse/format_item.rs | 17 +- .../src/format_description/parse/lexer.rs | 2 +- .../time/src/format_description/parse/mod.rs | 19 +- .../format_description/well_known/iso8601.rs | 82 +- .../well_known/iso8601/adt_hack.rs | 7 +- .../rust/time/src/formatting/formattable.rs | 30 +- .../rust/time/src/formatting/iso8601.rs | 22 +- third_party/rust/time/src/formatting/mod.rs | 139 +- third_party/rust/time/src/instant.rs | 12 + third_party/rust/time/src/internal_macros.rs | 200 +++ third_party/rust/time/src/lib.rs | 245 +-- third_party/rust/time/src/macros.rs | 4 +- third_party/rust/time/src/month.rs | 51 +- third_party/rust/time/src/offset_date_time.rs | 600 ++++++- .../rust/time/src/parsing/combinator/mod.rs | 4 +- .../src/parsing/combinator/rfc/iso8601.rs | 6 +- .../rust/time/src/parsing/component.rs | 51 +- third_party/rust/time/src/parsing/iso8601.rs | 36 +- third_party/rust/time/src/parsing/mod.rs | 7 + third_party/rust/time/src/parsing/parsable.rs | 169 +- third_party/rust/time/src/parsing/parsed.rs | 727 ++++---- .../rust/time/src/primitive_date_time.rs | 290 +++- third_party/rust/time/src/quickcheck.rs | 77 +- third_party/rust/time/src/rand.rs | 19 +- third_party/rust/time/src/serde/mod.rs | 80 +- .../time/src/serde/timestamp/microseconds.rs | 63 + .../time/src/serde/timestamp/milliseconds.rs | 63 + .../serde/{timestamp.rs => timestamp/mod.rs} | 4 + .../time/src/serde/timestamp/nanoseconds.rs | 61 + third_party/rust/time/src/serde/visitor.rs | 6 +- .../rust/time/src/sys/local_offset_at/unix.rs | 3 - .../time/src/sys/local_offset_at/wasm_js.rs | 5 +- .../time/src/sys/local_offset_at/windows.rs | 19 +- third_party/rust/time/src/tests.rs | 11 +- third_party/rust/time/src/time.rs | 551 +++--- third_party/rust/time/src/utc_offset.rs | 262 ++- third_party/rust/time/src/util.rs | 4 +- third_party/rust/time/src/weekday.rs | 50 +- 109 files changed, 9097 insertions(+), 3353 deletions(-) create mode 100644 third_party/rust/deranged/.cargo-checksum.json create mode 100644 third_party/rust/deranged/Cargo.toml create mode 100644 third_party/rust/deranged/LICENSE-Apache create mode 100644 third_party/rust/deranged/LICENSE-MIT create mode 100644 third_party/rust/deranged/README.md create mode 100644 third_party/rust/deranged/src/lib.rs create mode 100644 third_party/rust/deranged/src/tests.rs create mode 100644 third_party/rust/deranged/src/traits.rs create mode 100644 third_party/rust/deranged/src/unsafe_wrapper.rs create mode 100644 third_party/rust/num-conv/.cargo-checksum.json create mode 100644 third_party/rust/num-conv/Cargo.toml create mode 100644 third_party/rust/num-conv/LICENSE-Apache create mode 100644 third_party/rust/num-conv/LICENSE-MIT create mode 100644 third_party/rust/num-conv/src/lib.rs create mode 100644 third_party/rust/powerfmt/.cargo-checksum.json create mode 100644 third_party/rust/powerfmt/Cargo.toml create mode 100644 third_party/rust/powerfmt/LICENSE-Apache create mode 100644 third_party/rust/powerfmt/LICENSE-MIT create mode 100644 third_party/rust/powerfmt/README.md create mode 100644 third_party/rust/powerfmt/src/buf.rs create mode 100644 third_party/rust/powerfmt/src/ext.rs create mode 100644 third_party/rust/powerfmt/src/lib.rs create mode 100644 third_party/rust/powerfmt/src/smart_display.rs create mode 100644 third_party/rust/powerfmt/src/smart_display_impls.rs delete mode 100644 third_party/rust/time/src/date_time.rs delete mode 100644 third_party/rust/time/src/ext.rs create mode 100644 third_party/rust/time/src/ext/digit_count.rs create mode 100644 third_party/rust/time/src/ext/instant.rs create mode 100644 third_party/rust/time/src/ext/mod.rs create mode 100644 third_party/rust/time/src/ext/numerical_duration.rs create mode 100644 third_party/rust/time/src/ext/numerical_std_duration.rs create mode 100644 third_party/rust/time/src/internal_macros.rs create mode 100644 third_party/rust/time/src/serde/timestamp/microseconds.rs create mode 100644 third_party/rust/time/src/serde/timestamp/milliseconds.rs rename third_party/rust/time/src/serde/{timestamp.rs => timestamp/mod.rs} (97%) create mode 100644 third_party/rust/time/src/serde/timestamp/nanoseconds.rs diff --git a/Cargo.lock b/Cargo.lock index 082b60031d88..6b917309f978 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -857,7 +857,7 @@ version = "0.16.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e859cd57d0710d9e06c381b550c06e76992472a8c6d527aecd2fc673dcc231fb" dependencies = [ - "time 0.3.23", + "time 0.3.36", "version_check", ] @@ -1021,7 +1021,7 @@ dependencies = [ "serde", "serde_json", "sha2", - "time 0.3.23", + "time 0.3.36", "tokio", "unic-langid", "uuid", @@ -1337,6 +1337,16 @@ dependencies = [ "uuid", ] +[[package]] +name = "deranged" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +dependencies = [ + "powerfmt", + "serde", +] + [[package]] name = "derive_arbitrary" version = "1.3.1" @@ -3820,7 +3830,7 @@ dependencies = [ "smallvec", "stable_deref_trait", "syn", - "time 0.3.23", + "time 0.3.36", "time-macros", "tinystr", "tokio", @@ -3981,7 +3991,7 @@ dependencies = [ "env_logger", "log", "qlog", - "time 0.3.23", + "time 0.3.36", "winapi", ] @@ -4165,6 +4175,12 @@ dependencies = [ "nsstring", ] +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + [[package]] name = "num-derive" version = "0.4.2" @@ -4507,10 +4523,16 @@ dependencies = [ "indexmap 2.2.6", "line-wrap", "serde", - "time 0.3.23", + "time 0.3.36", "xml-rs", ] +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + [[package]] name = "ppv-lite86" version = "0.2.17" @@ -5761,11 +5783,14 @@ dependencies = [ [[package]] name = "time" -version = "0.3.23" +version = "0.3.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59e399c068f43a5d116fedaf73b203fa4f9c519f17e2b34f63221d3792f81446" +checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" dependencies = [ + "deranged", "itoa", + "num-conv", + "powerfmt", "serde", "time-core", "time-macros", @@ -5773,16 +5798,17 @@ dependencies = [ [[package]] name = "time-core" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7300fbefb4dadc1af235a9cef3737cea692a9d97e1b9cbcd4ebdae6f8868e6fb" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" [[package]] name = "time-macros" -version = "0.2.10" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96ba15a897f3c86766b757e5ac7221554c6750054d74d5b28844fce5fb36a6c4" +checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" dependencies = [ + "num-conv", "time-core", ] @@ -6494,7 +6520,7 @@ dependencies = [ "serde_derive", "serde_json", "thiserror", - "time 0.3.23", + "time 0.3.36", "tokio", "tokio-stream", "url", diff --git a/supply-chain/audits.toml b/supply-chain/audits.toml index c07a1c15d4ad..99402cb578a8 100644 --- a/supply-chain/audits.toml +++ b/supply-chain/audits.toml @@ -1498,6 +1498,16 @@ criteria = "safe-to-deploy" version = "0.8.0" notes = "This crates was written by Sentry and I've fully audited it as Firefox crash reporting machinery relies on it." +[[audits.deranged]] +who = "Alex Franchuk " +criteria = "safe-to-deploy" +version = "0.3.11" +notes = """ +This crate contains a decent bit of `unsafe` code, however all internal +unsafety is verified with copious assertions (many are compile-time), and +otherwise the unsafety is documented and left to the caller to verify. +""" + [[audits.derive_arbitrary]] who = "Mike Hommey " criteria = "safe-to-run" @@ -2975,6 +2985,15 @@ criteria = "safe-to-deploy" version = "0.4.2" notes = "All code written or reviewed by Josh Stone." +[[audits.num-conv]] +who = "Alex Franchuk " +criteria = "safe-to-deploy" +version = "0.1.0" +notes = """ +Very straightforward, simple crate. No dependencies, unsafe, extern, +side-effectful std functions, etc. +""" + [[audits.num-derive]] who = "Josh Stone " criteria = "safe-to-deploy" @@ -3249,6 +3268,15 @@ criteria = "safe-to-deploy" version = "0.18.0" notes = "Mozilla-developed package, no unsafe code, no access to file system, network or other far reaching APIs." +[[audits.powerfmt]] +who = "Alex Franchuk " +criteria = "safe-to-deploy" +version = "0.2.0" +notes = """ +A tiny bit of unsafe code to implement functionality that isn't in stable rust +yet, but it's all valid. Otherwise it's a pretty simple crate. +""" + [[audits.ppv-lite86]] who = "Mike Hommey " criteria = "safe-to-deploy" @@ -4085,6 +4113,16 @@ who = "Kershaw Chang " criteria = "safe-to-deploy" delta = "0.3.17 -> 0.3.23" +[[audits.time]] +who = "Alex Franchuk " +criteria = "safe-to-deploy" +delta = "0.3.23 -> 0.3.36" +notes = """ +There's a bit of new unsafe code that is self-imposed because they now assert +that ordinals are non-zero. All unsafe code was checked to ensure that the +invariants claimed were true. +""" + [[audits.time-core]] who = "Kershaw Chang " criteria = "safe-to-deploy" @@ -4100,6 +4138,11 @@ who = "Kershaw Chang " criteria = "safe-to-deploy" delta = "0.1.0 -> 0.1.1" +[[audits.time-core]] +who = "Alex Franchuk " +criteria = "safe-to-deploy" +delta = "0.1.1 -> 0.1.2" + [[audits.time-macros]] who = "Kershaw Chang " criteria = "safe-to-deploy" @@ -4115,6 +4158,11 @@ who = "Kershaw Chang " criteria = "safe-to-deploy" delta = "0.2.6 -> 0.2.10" +[[audits.time-macros]] +who = "Alex Franchuk " +criteria = "safe-to-deploy" +delta = "0.2.10 -> 0.2.18" + [[audits.tinystr]] who = "Zibi Braniecki " criteria = "safe-to-deploy" diff --git a/third_party/rust/deranged/.cargo-checksum.json b/third_party/rust/deranged/.cargo-checksum.json new file mode 100644 index 000000000000..f29abcd86f15 --- /dev/null +++ b/third_party/rust/deranged/.cargo-checksum.json @@ -0,0 +1 @@ +{"files":{"Cargo.toml":"d1ee03b7033e382279ff580d89a70a9aaf163f977400f0899ad9624e24744e6f","LICENSE-Apache":"b8929fea28678da67251fb2daf9438f67503814211051861612441806d8edb05","LICENSE-MIT":"04620bf27e4a643dd47bf27652320c205acdb776c1f9f24bb8c3bfaba10804c5","README.md":"fc4c9482d9e5225630da44e5371d6fa3f37220e2f4da2dac076cf4cd4f9592e7","src/lib.rs":"bc4b045c160d6f28726831d83f8389d9231410ae289a99950f63436219488dbb","src/tests.rs":"235e4f158084d12b0bfe85745c444d38bb134ebe584396d0a43154260f6576a7","src/traits.rs":"e3984e763afaa23dcf8ea686b473336472953b05abebc433acb26ab5f2237257","src/unsafe_wrapper.rs":"6e57697c2cd484cd60c1a50c4f4d32cb17526447c0f387d8ea3d89a2a89db688"},"package":"b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4"} \ No newline at end of file diff --git a/third_party/rust/deranged/Cargo.toml b/third_party/rust/deranged/Cargo.toml new file mode 100644 index 000000000000..ff660b538c7a --- /dev/null +++ b/third_party/rust/deranged/Cargo.toml @@ -0,0 +1,83 @@ +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO +# +# When uploading crates to the registry Cargo will automatically +# "normalize" Cargo.toml files for maximal compatibility +# with all versions of Cargo and also rewrite `path` dependencies +# to registry (e.g., crates.io) dependencies. +# +# If you are reading this file be aware that the original Cargo.toml +# will likely look very different (and much more reasonable). +# See Cargo.toml.orig for the original contents. + +[package] +edition = "2021" +rust-version = "1.67.0" +name = "deranged" +version = "0.3.11" +authors = ["Jacob Pratt "] +include = [ + "src/**/*", + "LICENSE-*", + "README.md", +] +description = "Ranged integers" +readme = "README.md" +keywords = [ + "integer", + "int", + "range", +] +license = "MIT OR Apache-2.0" +repository = "https://github.com/jhpratt/deranged" + +[package.metadata.docs.rs] +all-features = true +rustdoc-args = [ + "--cfg", + "docs_rs", +] +targets = ["x86_64-unknown-linux-gnu"] + +[dependencies.num-traits] +version = "0.2.15" +optional = true +default-features = false + +[dependencies.powerfmt] +version = "0.2.0" +optional = true +default-features = false + +[dependencies.quickcheck] +version = "1.0.3" +optional = true +default-features = false + +[dependencies.rand] +version = "0.8.4" +optional = true +default-features = false + +[dependencies.serde] +version = "1.0.126" +optional = true +default-features = false + +[dev-dependencies.rand] +version = "0.8.4" + +[dev-dependencies.serde_json] +version = "1.0.86" + +[features] +alloc = [] +default = ["std"] +num = ["dep:num-traits"] +powerfmt = ["dep:powerfmt"] +quickcheck = [ + "dep:quickcheck", + "alloc", +] +rand = ["dep:rand"] +serde = ["dep:serde"] +std = ["alloc"] diff --git a/third_party/rust/deranged/LICENSE-Apache b/third_party/rust/deranged/LICENSE-Apache new file mode 100644 index 000000000000..7646f21e37ed --- /dev/null +++ b/third_party/rust/deranged/LICENSE-Apache @@ -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 2022 Jacob Pratt et al. + + 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. diff --git a/third_party/rust/deranged/LICENSE-MIT b/third_party/rust/deranged/LICENSE-MIT new file mode 100644 index 000000000000..a11a755732ce --- /dev/null +++ b/third_party/rust/deranged/LICENSE-MIT @@ -0,0 +1,19 @@ +Copyright (c) 2022 Jacob Pratt et al. + +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. diff --git a/third_party/rust/deranged/README.md b/third_party/rust/deranged/README.md new file mode 100644 index 000000000000..eddc4b99afc9 --- /dev/null +++ b/third_party/rust/deranged/README.md @@ -0,0 +1,3 @@ +# Deranged + +This crate is a proof-of-concept implementation of ranged integers. diff --git a/third_party/rust/deranged/src/lib.rs b/third_party/rust/deranged/src/lib.rs new file mode 100644 index 000000000000..aee3ea99f6c4 --- /dev/null +++ b/third_party/rust/deranged/src/lib.rs @@ -0,0 +1,1474 @@ +#![cfg_attr(docs_rs, feature(doc_auto_cfg))] +#![cfg_attr(not(feature = "std"), no_std)] +#![deny( + anonymous_parameters, + clippy::all, + clippy::missing_safety_doc, + clippy::missing_safety_doc, + clippy::undocumented_unsafe_blocks, + illegal_floating_point_literal_pattern, + late_bound_lifetime_arguments, + patterns_in_fns_without_body, + rust_2018_idioms, + trivial_casts, + trivial_numeric_casts, + unreachable_pub, + unsafe_op_in_unsafe_fn, + unused_extern_crates +)] +#![warn( + clippy::dbg_macro, + clippy::decimal_literal_representation, + clippy::get_unwrap, + clippy::nursery, + clippy::pedantic, + clippy::todo, + clippy::unimplemented, + clippy::unwrap_used, + clippy::use_debug, + missing_copy_implementations, + missing_debug_implementations, + unused_qualifications, + variant_size_differences +)] +#![allow( + path_statements, // used for static assertions + clippy::inline_always, + clippy::missing_errors_doc, + clippy::must_use_candidate, + clippy::redundant_pub_crate, +)] +#![doc(test(attr(deny(warnings))))] + +#[cfg(test)] +mod tests; +mod traits; +mod unsafe_wrapper; + +#[cfg(feature = "alloc")] +#[allow(unused_extern_crates)] +extern crate alloc; + +use core::borrow::Borrow; +use core::cmp::Ordering; +use core::fmt; +use core::num::IntErrorKind; +use core::str::FromStr; +#[cfg(feature = "std")] +use std::error::Error; + +#[cfg(feature = "powerfmt")] +use powerfmt::smart_display; + +use crate::unsafe_wrapper::Unsafe; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct TryFromIntError; + +impl fmt::Display for TryFromIntError { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("out of range integral type conversion attempted") + } +} +#[cfg(feature = "std")] +impl Error for TryFromIntError {} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct ParseIntError { + kind: IntErrorKind, +} + +impl ParseIntError { + /// Outputs the detailed cause of parsing an integer failing. + // This function is not const because the counterpart of stdlib isn't + #[allow(clippy::missing_const_for_fn)] + #[inline(always)] + pub fn kind(&self) -> &IntErrorKind { + &self.kind + } +} + +impl fmt::Display for ParseIntError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self.kind { + IntErrorKind::Empty => "cannot parse integer from empty string", + IntErrorKind::InvalidDigit => "invalid digit found in string", + IntErrorKind::PosOverflow => "number too large to fit in target type", + IntErrorKind::NegOverflow => "number too small to fit in target type", + IntErrorKind::Zero => "number would be zero for non-zero type", + _ => "Unknown Int error kind", + } + .fmt(f) + } +} + +#[cfg(feature = "std")] +impl Error for ParseIntError {} + +macro_rules! const_try_opt { + ($e:expr) => { + match $e { + Some(value) => value, + None => return None, + } + }; +} + +macro_rules! if_signed { + (true $($x:tt)*) => { $($x)*}; + (false $($x:tt)*) => {}; +} + +macro_rules! if_unsigned { + (true $($x:tt)*) => {}; + (false $($x:tt)*) => { $($x)* }; +} + +macro_rules! article { + (true) => { + "An" + }; + (false) => { + "A" + }; +} + +macro_rules! unsafe_unwrap_unchecked { + ($e:expr) => {{ + let opt = $e; + debug_assert!(opt.is_some()); + match $e { + Some(value) => value, + None => core::hint::unreachable_unchecked(), + } + }}; +} + +/// Informs the optimizer that a condition is always true. If the condition is false, the behavior +/// is undefined. +/// +/// # Safety +/// +/// `b` must be `true`. +#[inline] +const unsafe fn assume(b: bool) { + debug_assert!(b); + if !b { + // Safety: The caller must ensure that `b` is true. + unsafe { core::hint::unreachable_unchecked() } + } +} + +macro_rules! impl_ranged { + ($( + $type:ident { + mod_name: $mod_name:ident + internal: $internal:ident + signed: $is_signed:ident + unsigned: $unsigned_type:ident + optional: $optional_type:ident + } + )*) => {$( + #[doc = concat!( + article!($is_signed), + " `", + stringify!($internal), + "` that is known to be in the range `MIN..=MAX`.", + )] + #[repr(transparent)] + #[derive(Clone, Copy, Eq, Ord, Hash)] + pub struct $type( + Unsafe<$internal>, + ); + + #[doc = concat!( + "A `", + stringify!($type), + "` that is optional. Equivalent to [`Option<", + stringify!($type), + ">`] with niche value optimization.", + )] + /// + #[doc = concat!( + "If `MIN` is [`", + stringify!($internal), + "::MIN`] _and_ `MAX` is [`", + stringify!($internal) + ,"::MAX`] then compilation will fail. This is because there is no way to represent \ + the niche value.", + )] + /// + /// This type is useful when you need to store an optional ranged value in a struct, but + /// do not want the overhead of an `Option` type. This reduces the size of the struct + /// overall, and is particularly useful when you have a large number of optional fields. + /// Note that most operations must still be performed on the [`Option`] type, which is + #[doc = concat!("obtained with [`", stringify!($optional_type), "::get`].")] + #[repr(transparent)] + #[derive(Clone, Copy, Eq, Hash)] + pub struct $optional_type( + $internal, + ); + + impl $type<0, 0> { + #[inline(always)] + pub const fn exact() -> $type { + // Safety: The value is the only one in range. + unsafe { $type::new_unchecked(VALUE) } + } + } + + impl $type { + /// The smallest value that can be represented by this type. + // Safety: `MIN` is in range by definition. + pub const MIN: Self = Self::new_static::(); + + /// The largest value that can be represented by this type. + // Safety: `MAX` is in range by definition. + pub const MAX: Self = Self::new_static::(); + + /// Creates a ranged integer without checking the value. + /// + /// # Safety + /// + /// The value must be within the range `MIN..=MAX`. + #[inline(always)] + pub const unsafe fn new_unchecked(value: $internal) -> Self { + ::ASSERT; + // Safety: The caller must ensure that the value is in range. + unsafe { + $crate::assume(MIN <= value && value <= MAX); + Self(Unsafe::new(value)) + } + } + + /// Returns the value as a primitive type. + #[inline(always)] + pub const fn get(self) -> $internal { + ::ASSERT; + // Safety: A stored value is always in range. + unsafe { $crate::assume(MIN <= *self.0.get() && *self.0.get() <= MAX) }; + *self.0.get() + } + + #[inline(always)] + pub(crate) const fn get_ref(&self) -> &$internal { + ::ASSERT; + let value = self.0.get(); + // Safety: A stored value is always in range. + unsafe { $crate::assume(MIN <= *value && *value <= MAX) }; + value + } + + /// Creates a ranged integer if the given value is in the range `MIN..=MAX`. + #[inline(always)] + pub const fn new(value: $internal) -> Option { + ::ASSERT; + if value < MIN || value > MAX { + None + } else { + // Safety: The value is in range. + Some(unsafe { Self::new_unchecked(value) }) + } + } + + /// Creates a ranged integer with a statically known value. **Fails to compile** if the + /// value is not in range. + #[inline(always)] + pub const fn new_static() -> Self { + <($type, $type) as $crate::traits::StaticIsValid>::ASSERT; + // Safety: The value is in range. + unsafe { Self::new_unchecked(VALUE) } + } + + /// Creates a ranged integer with the given value, saturating if it is out of range. + #[inline] + pub const fn new_saturating(value: $internal) -> Self { + ::ASSERT; + if value < MIN { + Self::MIN + } else if value > MAX { + Self::MAX + } else { + // Safety: The value is in range. + unsafe { Self::new_unchecked(value) } + } + } + + /// Expand the range that the value may be in. **Fails to compile** if the new range is + /// not a superset of the current range. + pub const fn expand( + self, + ) -> $type { + <$type as $crate::traits::RangeIsValid>::ASSERT; + <$type as $crate::traits::RangeIsValid>::ASSERT; + <($type, $type) as $crate::traits::ExpandIsValid> + ::ASSERT; + // Safety: The range is widened. + unsafe { $type::new_unchecked(self.get()) } + } + + /// Attempt to narrow the range that the value may be in. Returns `None` if the value + /// is outside the new range. **Fails to compile** if the new range is not a subset of + /// the current range. + pub const fn narrow< + const NEW_MIN: $internal, + const NEW_MAX: $internal, + >(self) -> Option<$type> { + <$type as $crate::traits::RangeIsValid>::ASSERT; + <$type as $crate::traits::RangeIsValid>::ASSERT; + <($type, $type) as $crate::traits::NarrowIsValid> + ::ASSERT; + $type::::new(self.get()) + } + + /// Converts a string slice in a given base to an integer. + /// + /// The string is expected to be an optional `+` or `-` sign followed by digits. Leading + /// and trailing whitespace represent an error. Digits are a subset of these characters, + /// depending on `radix`: + /// + /// - `0-9` + /// - `a-z` + /// - `A-Z` + /// + /// # Panics + /// + /// Panics if `radix` is not in the range `2..=36`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ```rust + #[doc = concat!("# use deranged::", stringify!($type), ";")] + #[doc = concat!( + "assert_eq!(", + stringify!($type), + "::<5, 10>::from_str_radix(\"A\", 16), Ok(", + stringify!($type), + "::new_static::<10>()));", + )] + /// ``` + #[inline] + pub fn from_str_radix(src: &str, radix: u32) -> Result { + ::ASSERT; + match $internal::from_str_radix(src, radix) { + Ok(value) if value > MAX => { + Err(ParseIntError { kind: IntErrorKind::PosOverflow }) + } + Ok(value) if value < MIN => { + Err(ParseIntError { kind: IntErrorKind::NegOverflow }) + } + // Safety: If the value was out of range, it would have been caught in a + // previous arm. + Ok(value) => Ok(unsafe { Self::new_unchecked(value) }), + Err(e) => Err(ParseIntError { kind: e.kind().clone() }), + } + } + + /// Checked integer addition. Computes `self + rhs`, returning `None` if the resulting + /// value is out of range. + #[must_use = "this returns the result of the operation, without modifying the original"] + #[inline] + pub const fn checked_add(self, rhs: $internal) -> Option { + ::ASSERT; + Self::new(const_try_opt!(self.get().checked_add(rhs))) + } + + /// Unchecked integer addition. Computes `self + rhs`, assuming that the result is in + /// range. + /// + /// # Safety + /// + /// The result of `self + rhs` must be in the range `MIN..=MAX`. + #[must_use = "this returns the result of the operation, without modifying the original"] + #[inline(always)] + pub const unsafe fn unchecked_add(self, rhs: $internal) -> Self { + ::ASSERT; + // Safety: The caller must ensure that the result is in range. + unsafe { + Self::new_unchecked(unsafe_unwrap_unchecked!(self.get().checked_add(rhs))) + } + } + + /// Checked integer addition. Computes `self - rhs`, returning `None` if the resulting + /// value is out of range. + #[must_use = "this returns the result of the operation, without modifying the original"] + #[inline] + pub const fn checked_sub(self, rhs: $internal) -> Option { + ::ASSERT; + Self::new(const_try_opt!(self.get().checked_sub(rhs))) + } + + /// Unchecked integer subtraction. Computes `self - rhs`, assuming that the result is in + /// range. + /// + /// # Safety + /// + /// The result of `self - rhs` must be in the range `MIN..=MAX`. + #[must_use = "this returns the result of the operation, without modifying the original"] + #[inline(always)] + pub const unsafe fn unchecked_sub(self, rhs: $internal) -> Self { + ::ASSERT; + // Safety: The caller must ensure that the result is in range. + unsafe { + Self::new_unchecked(unsafe_unwrap_unchecked!(self.get().checked_sub(rhs))) + } + } + + /// Checked integer addition. Computes `self * rhs`, returning `None` if the resulting + /// value is out of range. + #[must_use = "this returns the result of the operation, without modifying the original"] + #[inline] + pub const fn checked_mul(self, rhs: $internal) -> Option { + ::ASSERT; + Self::new(const_try_opt!(self.get().checked_mul(rhs))) + } + + /// Unchecked integer multiplication. Computes `self * rhs`, assuming that the result is + /// in range. + /// + /// # Safety + /// + /// The result of `self * rhs` must be in the range `MIN..=MAX`. + #[must_use = "this returns the result of the operation, without modifying the original"] + #[inline(always)] + pub const unsafe fn unchecked_mul(self, rhs: $internal) -> Self { + ::ASSERT; + // Safety: The caller must ensure that the result is in range. + unsafe { + Self::new_unchecked(unsafe_unwrap_unchecked!(self.get().checked_mul(rhs))) + } + } + + /// Checked integer addition. Computes `self / rhs`, returning `None` if `rhs == 0` or + /// if the resulting value is out of range. + #[must_use = "this returns the result of the operation, without modifying the original"] + #[inline] + pub const fn checked_div(self, rhs: $internal) -> Option { + ::ASSERT; + Self::new(const_try_opt!(self.get().checked_div(rhs))) + } + + /// Unchecked integer division. Computes `self / rhs`, assuming that `rhs != 0` and that + /// the result is in range. + /// + /// # Safety + /// + /// `self` must not be zero and the result of `self / rhs` must be in the range + /// `MIN..=MAX`. + #[must_use = "this returns the result of the operation, without modifying the original"] + #[inline(always)] + pub const unsafe fn unchecked_div(self, rhs: $internal) -> Self { + ::ASSERT; + // Safety: The caller must ensure that the result is in range and that `rhs` is not + // zero. + unsafe { + Self::new_unchecked(unsafe_unwrap_unchecked!(self.get().checked_div(rhs))) + } + } + + /// Checked Euclidean division. Computes `self.div_euclid(rhs)`, returning `None` if + /// `rhs == 0` or if the resulting value is out of range. + #[must_use = "this returns the result of the operation, without modifying the original"] + #[inline] + pub const fn checked_div_euclid(self, rhs: $internal) -> Option { + ::ASSERT; + Self::new(const_try_opt!(self.get().checked_div_euclid(rhs))) + } + + /// Unchecked Euclidean division. Computes `self.div_euclid(rhs)`, assuming that + /// `rhs != 0` and that the result is in range. + /// + /// # Safety + /// + /// `self` must not be zero and the result of `self.div_euclid(rhs)` must be in the + /// range `MIN..=MAX`. + #[must_use = "this returns the result of the operation, without modifying the original"] + #[inline(always)] + pub const unsafe fn unchecked_div_euclid(self, rhs: $internal) -> Self { + ::ASSERT; + // Safety: The caller must ensure that the result is in range and that `rhs` is not + // zero. + unsafe { + Self::new_unchecked( + unsafe_unwrap_unchecked!(self.get().checked_div_euclid(rhs)) + ) + } + } + + if_unsigned!($is_signed + /// Remainder. Computes `self % rhs`, statically guaranteeing that the returned value + /// is in range. + #[must_use = "this returns the result of the operation, without modifying the original"] + #[inline] + pub const fn rem( + self, + rhs: $type, + ) -> $type<0, RHS_VALUE> { + ::ASSERT; + // Safety: The result is guaranteed to be in range due to the nature of remainder on + // unsigned integers. + unsafe { $type::new_unchecked(self.get() % rhs.get()) } + }); + + /// Checked integer remainder. Computes `self % rhs`, returning `None` if `rhs == 0` or + /// if the resulting value is out of range. + #[must_use = "this returns the result of the operation, without modifying the original"] + #[inline] + pub const fn checked_rem(self, rhs: $internal) -> Option { + ::ASSERT; + Self::new(const_try_opt!(self.get().checked_rem(rhs))) + } + + /// Unchecked remainder. Computes `self % rhs`, assuming that `rhs != 0` and that the + /// result is in range. + /// + /// # Safety + /// + /// `self` must not be zero and the result of `self % rhs` must be in the range + /// `MIN..=MAX`. + #[must_use = "this returns the result of the operation, without modifying the original"] + #[inline(always)] + pub const unsafe fn unchecked_rem(self, rhs: $internal) -> Self { + ::ASSERT; + // Safety: The caller must ensure that the result is in range and that `rhs` is not + // zero. + unsafe { + Self::new_unchecked(unsafe_unwrap_unchecked!(self.get().checked_rem(rhs))) + } + } + + /// Checked Euclidean remainder. Computes `self.rem_euclid(rhs)`, returning `None` if + /// `rhs == 0` or if the resulting value is out of range. + #[must_use = "this returns the result of the operation, without modifying the original"] + #[inline] + pub const fn checked_rem_euclid(self, rhs: $internal) -> Option { + ::ASSERT; + Self::new(const_try_opt!(self.get().checked_rem_euclid(rhs))) + } + + /// Unchecked Euclidean remainder. Computes `self.rem_euclid(rhs)`, assuming that + /// `rhs != 0` and that the result is in range. + /// + /// # Safety + /// + /// `self` must not be zero and the result of `self.rem_euclid(rhs)` must be in the + /// range `MIN..=MAX`. + #[must_use = "this returns the result of the operation, without modifying the original"] + #[inline(always)] + pub const unsafe fn unchecked_rem_euclid(self, rhs: $internal) -> Self { + ::ASSERT; + // Safety: The caller must ensure that the result is in range and that `rhs` is not + // zero. + unsafe { + Self::new_unchecked( + unsafe_unwrap_unchecked!(self.get().checked_rem_euclid(rhs)) + ) + } + } + + /// Checked negation. Computes `-self`, returning `None` if the resulting value is out + /// of range. + #[must_use = "this returns the result of the operation, without modifying the original"] + #[inline] + pub const fn checked_neg(self) -> Option { + ::ASSERT; + Self::new(const_try_opt!(self.get().checked_neg())) + } + + /// Unchecked negation. Computes `-self`, assuming that `-self` is in range. + /// + /// # Safety + /// + /// The result of `-self` must be in the range `MIN..=MAX`. + #[must_use = "this returns the result of the operation, without modifying the original"] + #[inline(always)] + pub const unsafe fn unchecked_neg(self) -> Self { + ::ASSERT; + // Safety: The caller must ensure that the result is in range. + unsafe { Self::new_unchecked(unsafe_unwrap_unchecked!(self.get().checked_neg())) } + } + + /// Negation. Computes `self.neg()`, **failing to compile** if the result is not + /// guaranteed to be in range. + #[must_use = "this returns the result of the operation, without modifying the original"] + #[inline(always)] + pub const fn neg(self) -> Self { + ::ASSERT; + ::ASSERT; + // Safety: The compiler asserts that the result is in range. + unsafe { self.unchecked_neg() } + } + + /// Checked shift left. Computes `self << rhs`, returning `None` if the resulting value + /// is out of range. + #[must_use = "this returns the result of the operation, without modifying the original"] + #[inline] + pub const fn checked_shl(self, rhs: u32) -> Option { + ::ASSERT; + Self::new(const_try_opt!(self.get().checked_shl(rhs))) + } + + /// Unchecked shift left. Computes `self << rhs`, assuming that the result is in range. + /// + /// # Safety + /// + /// The result of `self << rhs` must be in the range `MIN..=MAX`. + #[must_use = "this returns the result of the operation, without modifying the original"] + #[inline(always)] + pub const unsafe fn unchecked_shl(self, rhs: u32) -> Self { + ::ASSERT; + // Safety: The caller must ensure that the result is in range. + unsafe { + Self::new_unchecked(unsafe_unwrap_unchecked!(self.get().checked_shl(rhs))) + } + } + + /// Checked shift right. Computes `self >> rhs`, returning `None` if + /// the resulting value is out of range. + #[must_use = "this returns the result of the operation, without modifying the original"] + #[inline] + pub const fn checked_shr(self, rhs: u32) -> Option { + ::ASSERT; + Self::new(const_try_opt!(self.get().checked_shr(rhs))) + } + + /// Unchecked shift right. Computes `self >> rhs`, assuming that the result is in range. + /// + /// # Safety + /// + /// The result of `self >> rhs` must be in the range `MIN..=MAX`. + #[must_use = "this returns the result of the operation, without modifying the original"] + #[inline(always)] + pub const unsafe fn unchecked_shr(self, rhs: u32) -> Self { + ::ASSERT; + // Safety: The caller must ensure that the result is in range. + unsafe { + Self::new_unchecked(unsafe_unwrap_unchecked!(self.get().checked_shr(rhs))) + } + } + + if_signed!($is_signed + /// Checked absolute value. Computes `self.abs()`, returning `None` if the resulting + /// value is out of range. + #[must_use = "this returns the result of the operation, without modifying the original"] + #[inline] + pub const fn checked_abs(self) -> Option { + ::ASSERT; + Self::new(const_try_opt!(self.get().checked_abs())) + } + + /// Unchecked absolute value. Computes `self.abs()`, assuming that the result is in + /// range. + /// + /// # Safety + /// + /// The result of `self.abs()` must be in the range `MIN..=MAX`. + #[must_use = "this returns the result of the operation, without modifying the original"] + #[inline(always)] + pub const unsafe fn unchecked_abs(self) -> Self { + ::ASSERT; + // Safety: The caller must ensure that the result is in range. + unsafe { Self::new_unchecked(unsafe_unwrap_unchecked!(self.get().checked_abs())) } + } + + /// Absolute value. Computes `self.abs()`, **failing to compile** if the result is not + /// guaranteed to be in range. + #[must_use = "this returns the result of the operation, without modifying the original"] + #[inline(always)] + pub const fn abs(self) -> Self { + ::ASSERT; + ::ASSERT; + // Safety: The compiler asserts that the result is in range. + unsafe { self.unchecked_abs() } + }); + + /// Checked exponentiation. Computes `self.pow(exp)`, returning `None` if the resulting + /// value is out of range. + #[must_use = "this returns the result of the operation, without modifying the original"] + #[inline] + pub const fn checked_pow(self, exp: u32) -> Option { + ::ASSERT; + Self::new(const_try_opt!(self.get().checked_pow(exp))) + } + + /// Unchecked exponentiation. Computes `self.pow(exp)`, assuming that the result is in + /// range. + /// + /// # Safety + /// + /// The result of `self.pow(exp)` must be in the range `MIN..=MAX`. + #[must_use = "this returns the result of the operation, without modifying the original"] + #[inline(always)] + pub const unsafe fn unchecked_pow(self, exp: u32) -> Self { + ::ASSERT; + // Safety: The caller must ensure that the result is in range. + unsafe { + Self::new_unchecked(unsafe_unwrap_unchecked!(self.get().checked_pow(exp))) + } + } + + /// Saturating integer addition. Computes `self + rhs`, saturating at the numeric + /// bounds. + #[must_use = "this returns the result of the operation, without modifying the original"] + #[inline] + pub const fn saturating_add(self, rhs: $internal) -> Self { + ::ASSERT; + Self::new_saturating(self.get().saturating_add(rhs)) + } + + /// Saturating integer subtraction. Computes `self - rhs`, saturating at the numeric + /// bounds. + #[must_use = "this returns the result of the operation, without modifying the original"] + #[inline] + pub const fn saturating_sub(self, rhs: $internal) -> Self { + ::ASSERT; + Self::new_saturating(self.get().saturating_sub(rhs)) + } + + if_signed!($is_signed + /// Saturating integer negation. Computes `self - rhs`, saturating at the numeric + /// bounds. + #[must_use = "this returns the result of the operation, without modifying the original"] + #[inline] + pub const fn saturating_neg(self) -> Self { + ::ASSERT; + Self::new_saturating(self.get().saturating_neg()) + }); + + if_signed!($is_signed + /// Saturating absolute value. Computes `self.abs()`, saturating at the numeric bounds. + #[must_use = "this returns the result of the operation, without modifying the original"] + #[inline] + pub const fn saturating_abs(self) -> Self { + ::ASSERT; + Self::new_saturating(self.get().saturating_abs()) + }); + + /// Saturating integer multiplication. Computes `self * rhs`, saturating at the numeric + /// bounds. + #[must_use = "this returns the result of the operation, without modifying the original"] + #[inline] + pub const fn saturating_mul(self, rhs: $internal) -> Self { + ::ASSERT; + Self::new_saturating(self.get().saturating_mul(rhs)) + } + + /// Saturating integer exponentiation. Computes `self.pow(exp)`, saturating at the + /// numeric bounds. + #[must_use = "this returns the result of the operation, without modifying the original"] + #[inline] + pub const fn saturating_pow(self, exp: u32) -> Self { + ::ASSERT; + Self::new_saturating(self.get().saturating_pow(exp)) + } + + /// Compute the `rem_euclid` of this type with its unsigned type equivalent + // Not public because it doesn't match stdlib's "method_unsigned implemented only for signed type" tradition. + // Also because this isn't implemented for normal types in std. + // TODO maybe make public anyway? It is useful. + #[must_use = "this returns the result of the operation, without modifying the original"] + #[inline] + #[allow(trivial_numeric_casts)] // needed since some casts have to send unsigned -> unsigned to handle signed -> unsigned + const fn rem_euclid_unsigned( + rhs: $internal, + range_len: $unsigned_type + ) -> $unsigned_type { + #[allow(unused_comparisons)] + if rhs >= 0 { + (rhs as $unsigned_type) % range_len + } else { + // Let ux refer to an n bit unsigned and ix refer to an n bit signed integer. + // Can't write -ux or ux::abs() method. This gets around compilation error. + // `wrapping_sub` is to handle rhs = ix::MIN since ix::MIN = -ix::MAX-1 + let rhs_abs = ($internal::wrapping_sub(0, rhs)) as $unsigned_type; + // Largest multiple of range_len <= type::MAX is lowest if range_len * 2 > ux::MAX -> range_len >= ux::MAX / 2 + 1 + // Also = 0 in mod range_len arithmetic. + // Sub from this large number rhs_abs (same as sub -rhs = -(-rhs) = add rhs) to get rhs % range_len + // ix::MIN = -2^(n-1) so 0 <= rhs_abs <= 2^(n-1) + // ux::MAX / 2 + 1 = 2^(n-1) so this subtraction will always be a >= 0 after subtraction + // Thus converting rhs signed negative to equivalent positive value in mod range_len arithmetic + ((($unsigned_type::MAX / range_len) * range_len) - (rhs_abs)) % range_len + } + } + + /// Wrapping integer addition. Computes `self + rhs`, wrapping around the numeric + /// bounds. + #[must_use = "this returns the result of the operation, without modifying the original"] + #[inline] + #[allow(trivial_numeric_casts)] // needed since some casts have to send unsigned -> unsigned to handle signed -> unsigned + pub const fn wrapping_add(self, rhs: $internal) -> Self { + ::ASSERT; + // Forward to internal type's impl if same as type. + if MIN == $internal::MIN && MAX == $internal::MAX { + // Safety: std's wrapping methods match ranged arithmetic when the range is the internal datatype's range. + return unsafe { Self::new_unchecked(self.get().wrapping_add(rhs)) } + } + + let inner = self.get(); + + // Won't overflow because of std impl forwarding. + let range_len = MAX.abs_diff(MIN) + 1; + + // Calculate the offset with proper handling for negative rhs + let offset = Self::rem_euclid_unsigned(rhs, range_len); + + let greater_vals = MAX.abs_diff(inner); + // No wrap + if offset <= greater_vals { + // Safety: + // if inner >= 0 -> No overflow beyond range (offset <= greater_vals) + // if inner < 0: Same as >=0 with caveat: + // `(signed as unsigned).wrapping_add(unsigned) as signed` is the same as + // `signed::checked_add_unsigned(unsigned).unwrap()` or `wrapping_add_unsigned` + // (the difference doesn't matter since it won't overflow), + // but unsigned integers don't have either method so it won't compile that way. + unsafe { Self::new_unchecked( + ((inner as $unsigned_type).wrapping_add(offset)) as $internal + ) } + } + // Wrap + else { + // Safety: + // - offset < range_len by rem_euclid (MIN + ... safe) + // - offset > greater_vals from if statement (offset - (greater_vals + 1) safe) + // + // again using `(signed as unsigned).wrapping_add(unsigned) as signed` = `checked_add_unsigned` trick + unsafe { Self::new_unchecked( + ((MIN as $unsigned_type).wrapping_add( + offset - (greater_vals + 1) + )) as $internal + ) } + } + } + + /// Wrapping integer subtraction. Computes `self - rhs`, wrapping around the numeric + /// bounds. + #[must_use = "this returns the result of the operation, without modifying the original"] + #[inline] + #[allow(trivial_numeric_casts)] // needed since some casts have to send unsigned -> unsigned to handle signed -> unsigned + pub const fn wrapping_sub(self, rhs: $internal) -> Self { + ::ASSERT; + // Forward to internal type's impl if same as type. + if MIN == $internal::MIN && MAX == $internal::MAX { + // Safety: std's wrapping methods match ranged arithmetic when the range is the internal datatype's range. + return unsafe { Self::new_unchecked(self.get().wrapping_sub(rhs)) } + } + + let inner = self.get(); + + // Won't overflow because of std impl forwarding. + let range_len = MAX.abs_diff(MIN) + 1; + + // Calculate the offset with proper handling for negative rhs + let offset = Self::rem_euclid_unsigned(rhs, range_len); + + let lesser_vals = MIN.abs_diff(inner); + // No wrap + if offset <= lesser_vals { + // Safety: + // if inner >= 0 -> No overflow beyond range (offset <= greater_vals) + // if inner < 0: Same as >=0 with caveat: + // `(signed as unsigned).wrapping_sub(unsigned) as signed` is the same as + // `signed::checked_sub_unsigned(unsigned).unwrap()` or `wrapping_sub_unsigned` + // (the difference doesn't matter since it won't overflow below 0), + // but unsigned integers don't have either method so it won't compile that way. + unsafe { Self::new_unchecked( + ((inner as $unsigned_type).wrapping_sub(offset)) as $internal + ) } + } + // Wrap + else { + // Safety: + // - offset < range_len by rem_euclid (MAX - ... safe) + // - offset > lesser_vals from if statement (offset - (lesser_vals + 1) safe) + // + // again using `(signed as unsigned).wrapping_sub(unsigned) as signed` = `checked_sub_unsigned` trick + unsafe { Self::new_unchecked( + ((MAX as $unsigned_type).wrapping_sub( + offset - (lesser_vals + 1) + )) as $internal + ) } + } + } + } + + impl $optional_type { + /// The value used as the niche. Must not be in the range `MIN..=MAX`. + const NICHE: $internal = match (MIN, MAX) { + ($internal::MIN, $internal::MAX) => panic!("type has no niche"), + ($internal::MIN, _) => $internal::MAX, + (_, _) => $internal::MIN, + }; + + /// An optional ranged value that is not present. + #[allow(non_upper_case_globals)] + pub const None: Self = Self(Self::NICHE); + + /// Creates an optional ranged value that is present. + #[allow(non_snake_case)] + #[inline(always)] + pub const fn Some(value: $type) -> Self { + <$type as $crate::traits::RangeIsValid>::ASSERT; + Self(value.get()) + } + + /// Returns the value as the standard library's [`Option`] type. + #[inline(always)] + pub const fn get(self) -> Option<$type> { + <$type as $crate::traits::RangeIsValid>::ASSERT; + if self.0 == Self::NICHE { + None + } else { + // Safety: A stored value that is not the niche is always in range. + Some(unsafe { $type::new_unchecked(self.0) }) + } + } + + /// Creates an optional ranged integer without checking the value. + /// + /// # Safety + /// + /// The value must be within the range `MIN..=MAX`. As the value used for niche + /// value optimization is unspecified, the provided value must not be the niche + /// value. + #[inline(always)] + pub const unsafe fn some_unchecked(value: $internal) -> Self { + <$type as $crate::traits::RangeIsValid>::ASSERT; + // Safety: The caller must ensure that the value is in range. + unsafe { $crate::assume(MIN <= value && value <= MAX) }; + Self(value) + } + + /// Obtain the inner value of the struct. This is useful for comparisons. + #[inline(always)] + pub(crate) const fn inner(self) -> $internal { + <$type as $crate::traits::RangeIsValid>::ASSERT; + self.0 + } + + #[inline(always)] + pub const fn get_primitive(self) -> Option<$internal> { + <$type as $crate::traits::RangeIsValid>::ASSERT; + Some(const_try_opt!(self.get()).get()) + } + + /// Returns `true` if the value is the niche value. + #[inline(always)] + pub const fn is_none(self) -> bool { + <$type as $crate::traits::RangeIsValid>::ASSERT; + self.get().is_none() + } + + /// Returns `true` if the value is not the niche value. + #[inline(always)] + pub const fn is_some(self) -> bool { + <$type as $crate::traits::RangeIsValid>::ASSERT; + self.get().is_some() + } + } + + impl fmt::Debug for $type { + #[inline(always)] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + ::ASSERT; + self.get().fmt(f) + } + } + + impl fmt::Debug for $optional_type { + #[inline(always)] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + <$type as $crate::traits::RangeIsValid>::ASSERT; + self.get().fmt(f) + } + } + + impl fmt::Display for $type { + #[inline(always)] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + ::ASSERT; + self.get().fmt(f) + } + } + + #[cfg(feature = "powerfmt")] + impl< + const MIN: $internal, + const MAX: $internal, + > smart_display::SmartDisplay for $type { + type Metadata = <$internal as smart_display::SmartDisplay>::Metadata; + + #[inline(always)] + fn metadata( + &self, + f: smart_display::FormatterOptions, + ) -> smart_display::Metadata<'_, Self> { + ::ASSERT; + self.get_ref().metadata(f).reuse() + } + + #[inline(always)] + fn fmt_with_metadata( + &self, + f: &mut fmt::Formatter<'_>, + metadata: smart_display::Metadata<'_, Self>, + ) -> fmt::Result { + ::ASSERT; + self.get().fmt_with_metadata(f, metadata.reuse()) + } + } + + impl Default for $optional_type { + #[inline(always)] + fn default() -> Self { + <$type as $crate::traits::RangeIsValid>::ASSERT; + Self::None + } + } + + impl AsRef<$internal> for $type { + #[inline(always)] + fn as_ref(&self) -> &$internal { + ::ASSERT; + &self.get_ref() + } + } + + impl Borrow<$internal> for $type { + #[inline(always)] + fn borrow(&self) -> &$internal { + ::ASSERT; + &self.get_ref() + } + } + + impl< + const MIN_A: $internal, + const MAX_A: $internal, + const MIN_B: $internal, + const MAX_B: $internal, + > PartialEq<$type> for $type { + #[inline(always)] + fn eq(&self, other: &$type) -> bool { + ::ASSERT; + <$type as $crate::traits::RangeIsValid>::ASSERT; + self.get() == other.get() + } + } + + impl< + const MIN_A: $internal, + const MAX_A: $internal, + const MIN_B: $internal, + const MAX_B: $internal, + > PartialEq<$optional_type> for $optional_type { + #[inline(always)] + fn eq(&self, other: &$optional_type) -> bool { + <$type as $crate::traits::RangeIsValid>::ASSERT; + <$type as $crate::traits::RangeIsValid>::ASSERT; + self.inner() == other.inner() + } + } + + impl< + const MIN_A: $internal, + const MAX_A: $internal, + const MIN_B: $internal, + const MAX_B: $internal, + > PartialOrd<$type> for $type { + #[inline(always)] + fn partial_cmp(&self, other: &$type) -> Option { + ::ASSERT; + <$type as $crate::traits::RangeIsValid>::ASSERT; + self.get().partial_cmp(&other.get()) + } + } + + impl< + const MIN_A: $internal, + const MAX_A: $internal, + const MIN_B: $internal, + const MAX_B: $internal, + > PartialOrd<$optional_type> for $optional_type { + #[inline] + fn partial_cmp(&self, other: &$optional_type) -> Option { + <$type as $crate::traits::RangeIsValid>::ASSERT; + <$type as $crate::traits::RangeIsValid>::ASSERT; + if self.is_none() && other.is_none() { + Some(Ordering::Equal) + } else if self.is_none() { + Some(Ordering::Less) + } else if other.is_none() { + Some(Ordering::Greater) + } else { + self.inner().partial_cmp(&other.inner()) + } + } + } + + impl< + const MIN: $internal, + const MAX: $internal, + > Ord for $optional_type { + #[inline] + fn cmp(&self, other: &Self) -> Ordering { + <$type as $crate::traits::RangeIsValid>::ASSERT; + if self.is_none() && other.is_none() { + Ordering::Equal + } else if self.is_none() { + Ordering::Less + } else if other.is_none() { + Ordering::Greater + } else { + self.inner().cmp(&other.inner()) + } + } + } + + impl fmt::Binary for $type { + #[inline(always)] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + ::ASSERT; + self.get().fmt(f) + } + } + + impl fmt::LowerHex for $type { + #[inline(always)] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + ::ASSERT; + self.get().fmt(f) + } + } + + impl fmt::UpperHex for $type { + #[inline(always)] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + ::ASSERT; + self.get().fmt(f) + } + } + + impl fmt::LowerExp for $type { + #[inline(always)] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + ::ASSERT; + self.get().fmt(f) + } + } + + impl fmt::UpperExp for $type { + #[inline(always)] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + ::ASSERT; + self.get().fmt(f) + } + } + + impl fmt::Octal for $type { + #[inline(always)] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + ::ASSERT; + self.get().fmt(f) + } + } + + impl From<$type> for $internal { + #[inline(always)] + fn from(value: $type) -> Self { + <$type as $crate::traits::RangeIsValid>::ASSERT; + value.get() + } + } + + impl< + const MIN: $internal, + const MAX: $internal, + > From<$type> for $optional_type { + #[inline(always)] + fn from(value: $type) -> Self { + <$type as $crate::traits::RangeIsValid>::ASSERT; + Self::Some(value) + } + } + + impl< + const MIN: $internal, + const MAX: $internal, + > From>> for $optional_type { + #[inline(always)] + fn from(value: Option<$type>) -> Self { + <$type as $crate::traits::RangeIsValid>::ASSERT; + match value { + Some(value) => Self::Some(value), + None => Self::None, + } + } + } + + impl< + const MIN: $internal, + const MAX: $internal, + > From<$optional_type> for Option<$type> { + #[inline(always)] + fn from(value: $optional_type) -> Self { + <$type as $crate::traits::RangeIsValid>::ASSERT; + value.get() + } + } + + impl TryFrom<$internal> for $type { + type Error = TryFromIntError; + + #[inline] + fn try_from(value: $internal) -> Result { + ::ASSERT; + Self::new(value).ok_or(TryFromIntError) + } + } + + impl FromStr for $type { + type Err = ParseIntError; + + #[inline] + fn from_str(s: &str) -> Result { + ::ASSERT; + let value = s.parse::<$internal>().map_err(|e| ParseIntError { + kind: e.kind().clone() + })?; + if value < MIN { + Err(ParseIntError { kind: IntErrorKind::NegOverflow }) + } else if value > MAX { + Err(ParseIntError { kind: IntErrorKind::PosOverflow }) + } else { + // Safety: The value was previously checked for validity. + Ok(unsafe { Self::new_unchecked(value) }) + } + } + } + + #[cfg(feature = "serde")] + impl serde::Serialize for $type { + #[inline(always)] + fn serialize(&self, serializer: S) -> Result { + ::ASSERT; + self.get().serialize(serializer) + } + } + + #[cfg(feature = "serde")] + impl< + const MIN: $internal, + const MAX: $internal, + > serde::Serialize for $optional_type { + #[inline(always)] + fn serialize(&self, serializer: S) -> Result { + <$type as $crate::traits::RangeIsValid>::ASSERT; + self.get().serialize(serializer) + } + } + + #[cfg(feature = "serde")] + impl< + 'de, + const MIN: $internal, + const MAX: $internal, + > serde::Deserialize<'de> for $type { + #[inline] + fn deserialize>(deserializer: D) -> Result { + ::ASSERT; + let internal = <$internal>::deserialize(deserializer)?; + Self::new(internal).ok_or_else(|| ::invalid_value( + serde::de::Unexpected::Other("integer"), + #[cfg(feature = "std")] { + &format!("an integer in the range {}..={}", MIN, MAX).as_ref() + }, + #[cfg(not(feature = "std"))] { + &"an integer in the valid range" + } + )) + } + } + + #[cfg(feature = "serde")] + impl< + 'de, + const MIN: $internal, + const MAX: $internal, + > serde::Deserialize<'de> for $optional_type { + #[inline] + fn deserialize>(deserializer: D) -> Result { + <$type as $crate::traits::RangeIsValid>::ASSERT; + Ok(Self::Some($type::::deserialize(deserializer)?)) + } + } + + #[cfg(feature = "rand")] + impl< + const MIN: $internal, + const MAX: $internal, + > rand::distributions::Distribution<$type> for rand::distributions::Standard { + #[inline] + fn sample(&self, rng: &mut R) -> $type { + <$type as $crate::traits::RangeIsValid>::ASSERT; + $type::new(rng.gen_range(MIN..=MAX)).expect("rand failed to generate a valid value") + } + } + + #[cfg(feature = "rand")] + impl< + const MIN: $internal, + const MAX: $internal, + > rand::distributions::Distribution<$optional_type> + for rand::distributions::Standard { + #[inline] + fn sample(&self, rng: &mut R) -> $optional_type { + <$type as $crate::traits::RangeIsValid>::ASSERT; + rng.gen::>>().into() + } + } + + #[cfg(feature = "num")] + impl num_traits::Bounded for $type { + #[inline(always)] + fn min_value() -> Self { + ::ASSERT; + Self::MIN + } + + #[inline(always)] + fn max_value() -> Self { + ::ASSERT; + Self::MAX + } + } + + #[cfg(feature = "quickcheck")] + impl quickcheck::Arbitrary for $type { + #[inline] + fn arbitrary(g: &mut quickcheck::Gen) -> Self { + ::ASSERT; + // Safety: The `rem_euclid` call and addition ensure that the value is in range. + unsafe { + Self::new_unchecked($internal::arbitrary(g).rem_euclid(MAX - MIN + 1) + MIN) + } + } + + #[inline] + fn shrink(&self) -> ::alloc::boxed::Box> { + ::alloc::boxed::Box::new( + self.get() + .shrink() + .filter_map(Self::new) + ) + } + } + + #[cfg(feature = "quickcheck")] + impl< + const MIN: $internal, + const MAX: $internal, + > quickcheck::Arbitrary for $optional_type { + #[inline] + fn arbitrary(g: &mut quickcheck::Gen) -> Self { + <$type as $crate::traits::RangeIsValid>::ASSERT; + Option::<$type>::arbitrary(g).into() + } + + #[inline] + fn shrink(&self) -> ::alloc::boxed::Box> { + ::alloc::boxed::Box::new(self.get().shrink().map(Self::from)) + } + } + )*}; +} + +impl_ranged! { + RangedU8 { + mod_name: ranged_u8 + internal: u8 + signed: false + unsigned: u8 + optional: OptionRangedU8 + } + RangedU16 { + mod_name: ranged_u16 + internal: u16 + signed: false + unsigned: u16 + optional: OptionRangedU16 + } + RangedU32 { + mod_name: ranged_u32 + internal: u32 + signed: false + unsigned: u32 + optional: OptionRangedU32 + } + RangedU64 { + mod_name: ranged_u64 + internal: u64 + signed: false + unsigned: u64 + optional: OptionRangedU64 + } + RangedU128 { + mod_name: ranged_u128 + internal: u128 + signed: false + unsigned: u128 + optional: OptionRangedU128 + } + RangedUsize { + mod_name: ranged_usize + internal: usize + signed: false + unsigned: usize + optional: OptionRangedUsize + } + RangedI8 { + mod_name: ranged_i8 + internal: i8 + signed: true + unsigned: u8 + optional: OptionRangedI8 + } + RangedI16 { + mod_name: ranged_i16 + internal: i16 + signed: true + unsigned: u16 + optional: OptionRangedI16 + } + RangedI32 { + mod_name: ranged_i32 + internal: i32 + signed: true + unsigned: u32 + optional: OptionRangedI32 + } + RangedI64 { + mod_name: ranged_i64 + internal: i64 + signed: true + unsigned: u64 + optional: OptionRangedI64 + } + RangedI128 { + mod_name: ranged_i128 + internal: i128 + signed: true + unsigned: u128 + optional: OptionRangedI128 + } + RangedIsize { + mod_name: ranged_isize + internal: isize + signed: true + unsigned: usize + optional: OptionRangedIsize + } +} diff --git a/third_party/rust/deranged/src/tests.rs b/third_party/rust/deranged/src/tests.rs new file mode 100644 index 000000000000..e9a6ff782740 --- /dev/null +++ b/third_party/rust/deranged/src/tests.rs @@ -0,0 +1,688 @@ +use std::hash::Hash; + +use crate::{ + IntErrorKind, OptionRangedI128, OptionRangedI16, OptionRangedI32, OptionRangedI64, + OptionRangedI8, OptionRangedIsize, OptionRangedU128, OptionRangedU16, OptionRangedU32, + OptionRangedU64, OptionRangedU8, OptionRangedUsize, ParseIntError, RangedI128, RangedI16, + RangedI32, RangedI64, RangedI8, RangedIsize, RangedU128, RangedU16, RangedU32, RangedU64, + RangedU8, RangedUsize, TryFromIntError, +}; + +macro_rules! if_signed { + (signed $($x:tt)*) => { $($x)* }; + (unsigned $($x:tt)*) => {}; +} + +macro_rules! if_unsigned { + (signed $($x:tt)*) => {}; + (unsigned $($x:tt)*) => { $($x)* }; +} + +#[test] +fn errors() { + assert_eq!( + TryFromIntError.to_string(), + "out of range integral type conversion attempted" + ); + assert_eq!(TryFromIntError.clone(), TryFromIntError); + assert_eq!(format!("{TryFromIntError:?}"), "TryFromIntError"); + + assert_eq!( + ParseIntError { + kind: IntErrorKind::Empty, + } + .to_string(), + "cannot parse integer from empty string" + ); + assert_eq!( + ParseIntError { + kind: IntErrorKind::InvalidDigit, + } + .to_string(), + "invalid digit found in string" + ); + assert_eq!( + ParseIntError { + kind: IntErrorKind::PosOverflow, + } + .to_string(), + "number too large to fit in target type" + ); + assert_eq!( + ParseIntError { + kind: IntErrorKind::NegOverflow, + } + .to_string(), + "number too small to fit in target type" + ); + assert_eq!( + ParseIntError { + kind: IntErrorKind::Zero, + } + .to_string(), + "number would be zero for non-zero type" + ); + assert_eq!( + format!( + "{:?}", + ParseIntError { + kind: IntErrorKind::Empty + } + ), + "ParseIntError { kind: Empty }" + ); + assert_eq!( + ParseIntError { + kind: IntErrorKind::Empty + } + .clone(), + ParseIntError { + kind: IntErrorKind::Empty + } + ); + assert_eq!( + ParseIntError { + kind: IntErrorKind::Empty + } + .kind(), + &IntErrorKind::Empty + ); +} + +macro_rules! tests { + ($($signed:ident $opt:ident $t:ident $inner:ident),* $(,)?) => { + #[test] + fn derives() {$( + assert_eq!($t::<5, 10>::MIN.clone(), $t::<5, 10>::MIN); + let mut hasher = std::collections::hash_map::DefaultHasher::new(); + $t::<5, 10>::MIN.hash(&mut hasher); + assert_eq!( + $t::<5, 10>::MIN.cmp(&$t::<5, 10>::MAX), + std::cmp::Ordering::Less + ); + + assert_eq!($opt::<5, 10>::None.clone(), $opt::<5, 10>::None); + $opt::<5, 10>::None.hash(&mut hasher); + )*} + + #[test] + fn expand() {$( + let expanded: $t::<0, 20> = $t::<5, 10>::MAX.expand(); + assert_eq!(expanded, $t::<0, 20>::new_static::<10>()); + )*} + + #[test] + fn narrow() {$( + let narrowed: Option<$t::<10, 20>> = $t::<0, 20>::new_static::<10>().narrow(); + assert_eq!(narrowed, Some($t::<10, 20>::MIN)); + )*} + + #[test] + fn new() {$( + assert!($t::<5, 10>::new(10).is_some()); + assert!($t::<5, 10>::new(11).is_none()); + )*} + + #[test] + fn new_static() {$( + let six: $t::<5, 10> = $t::<5, 10>::new_static::<6>(); + assert_eq!(Some(six), $t::<5, 10>::new(6)); + )*} + + #[test] + fn some_unchecked() {$( + // Safety: The value is in range. + unsafe { + assert_eq!($opt::<5, 10>::some_unchecked(10), $opt::Some($t::<5, 10>::MAX)); + } + )*} + + #[test] + fn is_some() {$( + assert!($opt::<5, 10>::Some($t::<5, 10>::MAX).is_some()); + )*} + + #[test] + fn is_none() {$( + assert!($opt::<5, 10>::None.is_none()); + )*} + + #[test] + fn default() {$( + assert_eq!($opt::<5, 10>::default(), $opt::<5, 10>::None); + )*} + + #[test] + fn get() {$( + assert_eq!($t::<5, 10>::MAX.get(), 10); + assert_eq!($opt::<5, 10>::None.get(), None); + assert_eq!($opt::Some($t::<5, 10>::MAX).get(), Some($t::<5, 10>::MAX)); + )*} + + #[test] + fn get_primitive() {$( + assert_eq!($opt::Some($t::<5, 10>::MAX).get_primitive(), Some(10)); + assert_eq!($opt::<5, 10>::None.get_primitive(), None); + )*} + + #[test] + fn get_ref() {$( + assert_eq!($t::<5, 10>::MAX.get_ref(), &10); + )*} + + #[test] + fn new_saturating() {$( + assert_eq!($t::<5, 10>::new_saturating(11), $t::<5, 10>::MAX); + assert_eq!($t::<5, 10>::new_saturating(0), $t::<5, 10>::MIN); + assert_eq!($t::<5, 10>::new_saturating(9), $t::<5, 10>::new_static::<9>()); + )*} + + #[test] + fn from_str_radix() {$( + assert_eq!($t::<5, 10>::from_str_radix("10", 10), Ok($t::<5, 10>::MAX)); + assert_eq!($t::<5, 10>::from_str_radix("5", 10), Ok($t::<5, 10>::MIN)); + assert_eq!( + $t::<5, 10>::from_str_radix("4", 10), + Err(ParseIntError { kind: IntErrorKind::NegOverflow }), + ); + assert_eq!( + $t::<5, 10>::from_str_radix("11", 10), + Err(ParseIntError { kind: IntErrorKind::PosOverflow }), + ); + assert_eq!( + $t::<5, 10>::from_str_radix("", 10), + Err(ParseIntError { kind: IntErrorKind::Empty }), + ); + )*} + + #[test] + fn checked_add() {$( + assert_eq!($t::<5, 10>::MAX.checked_add(1), None); + assert_eq!($t::<5, 10>::MAX.checked_add(0), Some($t::<5, 10>::MAX)); + )*} + + #[test] + fn unchecked_add() {$( + // Safety: The result is in range. + unsafe { + assert_eq!($t::<5, 10>::MIN.unchecked_add(5), $t::<5, 10>::MAX); + } + )*} + + #[test] + fn checked_sub() {$( + assert_eq!($t::<5, 10>::MIN.checked_sub(1), None); + assert_eq!($t::<5, 10>::MIN.checked_sub(0), Some($t::<5, 10>::MIN)); + )*} + + #[test] + fn unchecked_sub() {$( + // Safety: The result is in range. + unsafe { + assert_eq!($t::<5, 10>::MAX.unchecked_sub(5), $t::<5, 10>::MIN); + } + )*} + + #[test] + fn checked_mul() {$( + assert_eq!($t::<5, 10>::MAX.checked_mul(2), None); + assert_eq!($t::<5, 10>::MAX.checked_mul(1), Some($t::<5, 10>::MAX)); + )*} + + #[test] + fn unchecked_mul() {$( + // Safety: The result is in range. + unsafe { + assert_eq!($t::<5, 10>::MAX.unchecked_mul(1), $t::<5, 10>::MAX); + } + )*} + + #[test] + fn checked_div() {$( + assert_eq!($t::<5, 10>::MAX.checked_div(3), None); + assert_eq!($t::<5, 10>::MAX.checked_div(2), $t::<5, 10>::new(5)); + assert_eq!($t::<5, 10>::MAX.checked_div(1), Some($t::<5, 10>::MAX)); + assert_eq!($t::<5, 10>::MAX.checked_div(0), None); + )*} + + #[test] + fn unchecked_div() {$( + // Safety: The result is in range. + unsafe { + assert_eq!($t::<5, 10>::MAX.unchecked_div(1), $t::<5, 10>::MAX); + } + )*} + + #[test] + fn checked_div_euclid() {$( + assert_eq!($t::<5, 10>::MAX.checked_div_euclid(3), None); + assert_eq!($t::<5, 10>::MAX.checked_div_euclid(2), $t::<5, 10>::new(5)); + assert_eq!($t::<5, 10>::MAX.checked_div_euclid(1), Some($t::<5, 10>::MAX)); + assert_eq!($t::<5, 10>::MAX.checked_div_euclid(0), None); + )*} + + #[test] + fn unchecked_div_euclid() {$( + // Safety: The result is in range. + unsafe { + assert_eq!($t::<5, 10>::MAX.unchecked_div_euclid(1), $t::<5, 10>::MAX); + } + )*} + + #[test] + fn rem() {$(if_unsigned! { $signed + assert_eq!($t::<5, 10>::MAX.rem($t::exact::<3>()), $t::<0, 3>::new_static::<1>()); + assert_eq!($t::<5, 10>::MAX.rem($t::exact::<5>()), $t::<0, 5>::MIN); + })*} + + #[test] + fn checked_rem() {$( + assert_eq!($t::<5, 10>::MAX.checked_rem(11), Some($t::<5, 10>::MAX)); + assert_eq!($t::<5, 10>::MAX.checked_rem(5), None); + )*} + + #[test] + fn unchecked_rem() {$( + // Safety: The result is in range. + unsafe { + assert_eq!($t::<5, 10>::MAX.unchecked_rem(11), $t::<5, 10>::MAX); + } + )*} + + #[test] + fn checked_rem_euclid() {$( + assert_eq!($t::<5, 10>::MAX.checked_rem_euclid(11), Some($t::<5, 10>::MAX)); + assert_eq!($t::<5, 10>::MAX.checked_rem_euclid(5), None); + )*} + + #[test] + fn unchecked_rem_euclid() {$( + // Safety: The result is in range. + unsafe { + assert_eq!($t::<5, 10>::MAX.unchecked_rem_euclid(11), $t::<5, 10>::MAX); + } + )*} + + #[test] + fn checked_neg() {$( + assert_eq!($t::<5, 10>::MIN.checked_neg(), None); + assert_eq!($t::<0, 10>::MIN.checked_neg(), Some($t::<0, 10>::MIN)); + )*} + + #[test] + fn unchecked_neg() {$( + // Safety: The result is in range. + unsafe { + assert_eq!($t::<0, 10>::MIN.unchecked_neg(), $t::<0, 10>::MIN); + } + )*} + + #[test] + fn neg() {$( if_signed! { $signed + assert_eq!($t::<-10, 10>::MIN.neg(), $t::<-10, 10>::MAX); + })*} + + #[test] + fn checked_shl() {$( + assert_eq!($t::<5, 10>::MAX.checked_shl(1), None); + assert_eq!($t::<5, 10>::MAX.checked_shl(0), Some($t::<5, 10>::MAX)); + assert_eq!($t::<5, 10>::MIN.checked_shl(1), Some($t::<5, 10>::MAX)); + )*} + + #[test] + fn unchecked_shl() {$( + // Safety: The result is in range. + unsafe { + assert_eq!($t::<5, 10>::MAX.unchecked_shl(0), $t::<5, 10>::MAX); + assert_eq!($t::<5, 10>::MIN.unchecked_shl(1), $t::<5, 10>::MAX); + } + )*} + + #[test] + fn checked_shr() {$( + assert_eq!($t::<5, 10>::MAX.checked_shr(2), None); + assert_eq!($t::<5, 10>::MAX.checked_shr(1), Some($t::<5, 10>::MIN)); + assert_eq!($t::<5, 10>::MAX.checked_shr(0), Some($t::<5, 10>::MAX)); + )*} + + #[test] + fn unchecked_shr() {$( + // Safety: The result is in range. + unsafe { + assert_eq!($t::<5, 10>::MAX.unchecked_shr(1), $t::<5, 10>::MIN); + assert_eq!($t::<5, 10>::MAX.unchecked_shr(0), $t::<5, 10>::MAX); + } + )*} + + #[test] + fn checked_abs() {$( if_signed! { $signed + assert_eq!($t::<5, 10>::MAX.checked_abs(), Some($t::<5, 10>::MAX)); + assert_eq!($t::<-10, 10>::MIN.checked_abs(), Some($t::<-10, 10>::MAX)); + assert_eq!($t::<-10, 0>::MIN.checked_abs(), None); + })*} + + #[test] + fn unchecked_abs() { $(if_signed! { $signed + // Safety: The result is in range. + unsafe { + assert_eq!($t::<5, 10>::MAX.unchecked_abs(), $t::<5, 10>::MAX); + assert_eq!($t::<-10, 10>::MIN.unchecked_abs(), $t::<-10, 10>::MAX); + } + })*} + + #[test] + fn abs() { $(if_signed! { $signed + assert_eq!($t::<-5, 10>::MIN.abs().get(), 5); + })*} + + #[test] + fn checked_pow() {$( + assert_eq!($t::<5, 10>::MAX.checked_pow(0), None); + assert_eq!($t::<5, 10>::MAX.checked_pow(1), Some($t::<5, 10>::MAX)); + assert_eq!($t::<5, 10>::MAX.checked_pow(2), None); + )*} + + #[test] + fn unchecked_pow() {$( + // Safety: The result is in range. + unsafe { + assert_eq!($t::<5, 10>::MAX.unchecked_pow(1), $t::<5, 10>::MAX); + } + )*} + + #[test] + fn saturating_add() {$( + assert_eq!($t::<5, 10>::MAX.saturating_add(0), $t::<5, 10>::MAX); + assert_eq!($t::<5, 10>::MAX.saturating_add(1), $t::<5, 10>::MAX); + )*} + + #[test] + fn wrapping_add() { + $( + assert_eq!($t::<5, 10>::MAX.wrapping_add(0), $t::<5, 10>::MAX); + assert_eq!($t::<5, 10>::MAX.wrapping_add(1), $t::<5, 10>::MIN); + assert_eq!($t::<{ $inner::MIN }, { $inner::MAX }>::MAX.wrapping_add(1), + $t::<{ $inner::MIN }, { $inner::MAX }>::MIN); + for i in 1..127 { + assert_eq!( + $t::<{ $inner::MIN}, { $inner::MAX - 1 }>::MAX.wrapping_add(i), + $t::<{ $inner::MIN}, { $inner::MAX - 1 }>::new($inner::MIN + i - 1).unwrap_or_else(|| panic!("adding {i}+{} does not yield {}", $inner::MIN, $inner::MAX + i )) + ); + } + )* + $(if_signed! { $signed + for i in 1..=127 { + assert_eq!($t::<-5, 126>::MIN.wrapping_add(-i), $t::<-5,126>::new(126-i+1).unwrap_or_else(|| panic!("adding {i}+{} does not yield {}", $inner::MIN, 126-i+1))); + assert_eq!($t::<-5, 126>::MIN.wrapping_add(i), $t::<-5,126>::new(-5+i).unwrap_or_else(|| panic!("adding {i}+{} does not yield {}", $inner::MIN, 126-i+1))); + } + for i in -127..=-1 { + assert_eq!($t::<-5, 126>::MIN.wrapping_add(i), $t::<-5,126>::new(126+i+1).unwrap_or_else(|| panic!("adding {i}+{} does not yield {}", $inner::MIN, 126-i+1))); + assert_eq!($t::<-5, 126>::MIN.wrapping_add(-i), $t::<-5,126>::new(-5-i).unwrap_or_else(|| panic!("adding {i}+{} does not yield {}", $inner::MIN, 126-i+1))); + } + assert_eq!($t::<-5, 126>::MIN.wrapping_add(-128), $t::<-5,126>::new(-1).unwrap_or_else(|| panic!("adding 128+{} does not yield -1", $inner::MIN))); + assert_eq!($t::<-5, 10>::MAX.wrapping_add(0), $t::<-5, 10>::MAX); + assert_eq!($t::<-5, -3>::MIN.wrapping_add(-1-3), $t::<-5, -3>::MAX); + assert_eq!($t::<-5, -3>::MIN.wrapping_add(-1-30), $t::<-5, -3>::MAX); + assert_eq!($t::<-5, -3>::MIN.wrapping_add(30), $t::<-5, -3>::MIN); + assert_eq!($t::<-5, -3>::MIN.wrapping_add(-30), $t::<-5, -3>::MIN); + assert_eq!($t::<-5, 10>::MAX.wrapping_add(25), $t::<-5, 10>::MIN.wrapping_add(24)); + assert_eq!($t::<-5, 10>::MIN.wrapping_add(24), $t::<-5, 10>::MIN.wrapping_add(8)); + assert_eq!($t::<-5, 10>::MAX.wrapping_add(1), $t::<-5, 10>::MIN); + assert_eq!($t::<-5, 10>::MIN.wrapping_add(-1), $t::<-5, 10>::MAX); + assert_eq!($t::<-5, 127>::MIN.wrapping_add(-1), $t::<-5, 127>::MAX); + assert_eq!($t::<-127, 126>::MIN.wrapping_add(-1), $t::<-127, 126>::MAX); + assert_eq!($t::<{ $inner::MIN }, { $inner::MAX }>::MIN.wrapping_add(-1), + $t::<{ $inner::MIN }, { $inner::MAX }>::MAX); + })* + } + + #[test] + fn wrapping_sub() { + $( + assert_eq!($t::<5, 10>::MIN.wrapping_sub(0), $t::<5, 10>::MIN); + assert_eq!($t::<5, 10>::MIN.wrapping_sub(1), $t::<5, 10>::MAX); + assert_eq!($t::<5, 10>::new(5 + 1).unwrap().wrapping_sub(1), $t::<5, 10>::MIN); + assert_eq!($t::<5, 10>::MAX.wrapping_sub(1), $t::<5, 10>::new(10 - 1).unwrap()); + assert_eq!($t::<{ $inner::MIN }, { $inner::MAX }>::MIN.wrapping_sub(1), + $t::<{ $inner::MIN }, { $inner::MAX }>::MAX); + for i in 1..127 { + assert_eq!( + $t::<{ $inner::MIN + 1 }, { $inner::MAX }>::MIN.wrapping_sub(i), + $t::<{ $inner::MIN + 1 }, { $inner::MAX }>::new($inner::MAX - i + 1).unwrap_or_else(|| panic!("failed test at iteration {i}")) + ); + } + )* + $(if_signed! { $signed + for i in -127..=127 { + assert_eq!($t::<-5, 126>::MIN.wrapping_add(i), $t::<-5,126>::MIN.wrapping_sub(-i), "failed test at {i}"); + assert_eq!($t::<-5, 126>::MIN.wrapping_add(-i), $t::<-5,126>::MIN.wrapping_sub(i), "failed test at {i}"); + } + assert_eq!( + $t::<-5, 126>::MIN.wrapping_add(127).wrapping_add(1), + $t::<-5,126>::MIN.wrapping_sub(-128) + ); + assert_eq!( + $t::<-5, 126>::MIN.wrapping_add(-128), + $t::<-5,126>::MIN.wrapping_sub(127).wrapping_sub(1) + ); + })* + } + + #[test] + fn saturating_sub() {$( + assert_eq!($t::<5, 10>::MIN.saturating_sub(0), $t::<5, 10>::MIN); + assert_eq!($t::<5, 10>::MIN.saturating_sub(1), $t::<5, 10>::MIN); + )*} + + #[test] + fn saturating_neg() {$(if_signed! { $signed + assert_eq!($t::<5, 10>::MIN.saturating_neg(), $t::<5, 10>::MIN); + assert_eq!($t::<5, 10>::MAX.saturating_neg(), $t::<5, 10>::MIN); + assert_eq!($t::<-10, 0>::MIN.saturating_neg(), $t::<-10, 0>::MAX); + assert_eq!($t::<-10, 0>::MAX.saturating_neg(), $t::<-10, 0>::MAX); + })*} + + #[test] + fn saturating_abs() {$(if_signed! { $signed + assert_eq!($t::<5, 10>::MIN.saturating_abs(), $t::<5, 10>::MIN); + assert_eq!($t::<5, 10>::MAX.saturating_abs(), $t::<5, 10>::MAX); + assert_eq!($t::<-10, 0>::MIN.saturating_abs(), $t::<-10, 0>::MAX); + assert_eq!($t::<-10, 0>::MAX.saturating_abs(), $t::<-10, 0>::MAX); + })*} + + #[test] + fn saturating_mul() {$( + assert_eq!($t::<5, 10>::MIN.saturating_mul(0), $t::<5, 10>::MIN); + assert_eq!($t::<5, 10>::MIN.saturating_mul(1), $t::<5, 10>::MIN); + assert_eq!($t::<5, 10>::MIN.saturating_mul(2), $t::<5, 10>::MAX); + assert_eq!($t::<5, 10>::MIN.saturating_mul(3), $t::<5, 10>::MAX); + )*} + + #[test] + fn saturating_pow() {$( + assert_eq!($t::<5, 10>::MIN.saturating_pow(0), $t::<5, 10>::MIN); + assert_eq!($t::<5, 10>::MIN.saturating_pow(1), $t::<5, 10>::MIN); + assert_eq!($t::<5, 10>::MIN.saturating_pow(2), $t::<5, 10>::MAX); + assert_eq!($t::<5, 10>::MIN.saturating_pow(3), $t::<5, 10>::MAX); + )*} + + #[test] + fn as_ref() {$( + assert_eq!($t::<5, 10>::MIN.as_ref(), &5); + assert_eq!($t::<5, 10>::MAX.as_ref(), &10); + )*} + + #[test] + fn borrow() { + use std::borrow::Borrow; + $( + assert_eq!(Borrow::<$inner>::borrow(&$t::<5, 10>::MIN), &5); + assert_eq!(Borrow::<$inner>::borrow(&$t::<5, 10>::MAX), &10); + )* + } + + #[test] + fn formatting() {$( + let val = $t::<5, 10>::MAX; + assert_eq!(format!("{}", val), "10"); + assert_eq!(format!("{:?}", val), "10"); + assert_eq!(format!("{:b}", val), "1010"); + assert_eq!(format!("{:o}", val), "12"); + assert_eq!(format!("{:x}", val), "a"); + assert_eq!(format!("{:X}", val), "A"); + assert_eq!(format!("{:e}", val), "1e1"); + assert_eq!(format!("{:E}", val), "1E1"); + + assert_eq!(format!("{:?}", $opt::Some($t::<5, 10>::MAX)), "Some(10)"); + assert_eq!(format!("{:?}", $opt::<5, 10>::None), "None"); + )*} + + #[test] + fn ord() {$( + assert!($t::<5, 10>::MIN < $t::<5, 10>::MAX); + assert!($t::<5, 10>::MIN <= $t::<5, 10>::MAX); + assert!($t::<5, 10>::MAX > $t::<5, 10>::MIN); + assert!($t::<5, 10>::MAX >= $t::<5, 10>::MIN); + + let none = $opt::<5, 10>::None; + let five = $opt::Some($t::<5, 10>::MIN); + let ten = $opt::Some($t::<5, 10>::MAX); + + assert_eq!(none.cmp(&none), std::cmp::Ordering::Equal); + assert_eq!(five.cmp(&five), std::cmp::Ordering::Equal); + assert_eq!(ten.cmp(&ten), std::cmp::Ordering::Equal); + assert_eq!(none.cmp(&five), std::cmp::Ordering::Less); + assert_eq!(five.cmp(&ten), std::cmp::Ordering::Less); + assert_eq!(none.cmp(&ten), std::cmp::Ordering::Less); + assert_eq!(ten.cmp(&none), std::cmp::Ordering::Greater); + + let none = $opt::<0, 10>::None; + let zero = $opt::Some($t::<0, 10>::MIN); + let ten = $opt::Some($t::<0, 10>::MAX); + + assert_eq!(none.partial_cmp(&none), Some(std::cmp::Ordering::Equal)); + assert_eq!(none.partial_cmp(&zero), Some(std::cmp::Ordering::Less)); + assert_eq!(zero.partial_cmp(&ten), Some(std::cmp::Ordering::Less)); + assert_eq!(none.partial_cmp(&ten), Some(std::cmp::Ordering::Less)); + assert_eq!(ten.partial_cmp(&none), Some(std::cmp::Ordering::Greater)); + )*} + + #[test] + fn from() {$( + assert_eq!($inner::from($t::<5, 10>::MAX), 10); + assert_eq!($inner::from($t::<5, 10>::MIN), 5); + + assert_eq!($opt::from($t::<5, 10>::MAX), $opt::Some($t::<5, 10>::MAX)); + assert_eq!($opt::from(Some($t::<5, 10>::MAX)), $opt::Some($t::<5, 10>::MAX)); + assert_eq!($opt::<5, 10>::from(None), $opt::<5, 10>::None); + assert_eq!(Option::from($opt::Some($t::<5, 10>::MAX)), Some($t::<5, 10>::MAX)); + assert_eq!(Option::<$t<5, 10>>::from($opt::<5, 10>::None), None); + )*} + + #[test] + fn try_from() {$( + assert_eq!($t::<5, 10>::try_from(10), Ok($t::<5, 10>::MAX)); + assert_eq!($t::<5, 10>::try_from(5), Ok($t::<5, 10>::MIN)); + assert_eq!($t::<5, 10>::try_from(4), Err(TryFromIntError)); + assert_eq!($t::<5, 10>::try_from(11), Err(TryFromIntError)); + )*} + + #[test] + fn from_str() {$( + assert_eq!("10".parse::<$t<5, 10>>(), Ok($t::<5, 10>::MAX)); + assert_eq!("5".parse::<$t<5, 10>>(), Ok($t::<5, 10>::MIN)); + assert_eq!("4".parse::<$t<5, 10>>(), Err(ParseIntError { kind: IntErrorKind::NegOverflow })); + assert_eq!("11".parse::<$t<5, 10>>(), Err(ParseIntError { kind: IntErrorKind::PosOverflow })); + assert_eq!("".parse::<$t<5, 10>>(), Err(ParseIntError { kind: IntErrorKind::Empty })); + )*} + + #[cfg(feature = "serde")] + #[test] + fn serde() -> serde_json::Result<()> { + $( + let val = $t::<5, 10>::MAX; + let serialized = serde_json::to_string(&val)?; + assert_eq!(serialized, "10"); + let deserialized: $t<5, 10> = serde_json::from_str(&serialized)?; + assert_eq!(deserialized, val); + + assert!(serde_json::from_str::<$t<5, 10>>("").is_err()); + assert!(serde_json::from_str::<$t<5, 10>>("4").is_err()); + assert!(serde_json::from_str::<$t<5, 10>>("11").is_err()); + + let val = $opt::<5, 10>::Some($t::<5, 10>::MAX); + let serialized = serde_json::to_string(&val)?; + assert_eq!(serialized, "10"); + let deserialized: $opt<5, 10> = serde_json::from_str(&serialized)?; + assert_eq!(deserialized, val); + + assert!(serde_json::from_str::<$opt<5, 10>>("").is_err()); + assert!(serde_json::from_str::<$opt<5, 10>>("4").is_err()); + assert!(serde_json::from_str::<$opt<5, 10>>("11").is_err()); + + let val = $opt::<5, 10>::None; + let serialized = serde_json::to_string(&val)?; + assert_eq!(serialized, "null"); + + assert!(serde_json::from_str::<$opt<5, 10>>("").is_err()); + assert!(serde_json::from_str::<$opt<5, 10>>("4").is_err()); + assert!(serde_json::from_str::<$opt<5, 10>>("11").is_err()); + )* + Ok(()) + } + + #[cfg(feature = "rand")] + #[test] + fn rand() {$( + let rand_val: $t<5, 10> = rand::random(); + assert!(rand_val >= $t::<5, 10>::MIN); + assert!(rand_val <= $t::<5, 10>::MAX); + + let rand: $opt<5, 10> = rand::random(); + if let Some(rand) = rand.get() { + assert!(rand >= $t::<5, 10>::MIN); + assert!(rand <= $t::<5, 10>::MAX); + } + )*} + + #[cfg(feature = "num")] + #[test] + fn num() {$( + assert_eq!(<$t<5, 10> as num_traits::Bounded>::min_value(), $t::<5, 10>::MIN); + assert_eq!(<$t<5, 10> as num_traits::Bounded>::max_value(), $t::<5, 10>::MAX); + )*} + + #[cfg(feature = "quickcheck")] + #[test] + fn quickcheck() {$( + #[allow(trivial_casts)] + quickcheck::quickcheck((|val| { + val >= $t::<5, 10>::MIN && val <= $t::<5, 10>::MAX + }) as fn($t<5, 10>) -> bool); + + #[allow(trivial_casts)] + quickcheck::quickcheck((|val| { + if let Some(val) = val.get() { + val >= $t::<5, 10>::MIN && val <= $t::<5, 10>::MAX + } else { + true + } + }) as fn($opt<5, 10>) -> bool); + )*} + }; +} + +tests![ + signed OptionRangedI8 RangedI8 i8, + signed OptionRangedI16 RangedI16 i16, + signed OptionRangedI32 RangedI32 i32, + signed OptionRangedI64 RangedI64 i64, + signed OptionRangedI128 RangedI128 i128, + signed OptionRangedIsize RangedIsize isize, + unsigned OptionRangedU8 RangedU8 u8, + unsigned OptionRangedU16 RangedU16 u16, + unsigned OptionRangedU32 RangedU32 u32, + unsigned OptionRangedU64 RangedU64 u64, + unsigned OptionRangedU128 RangedU128 u128, + unsigned OptionRangedUsize RangedUsize usize, +]; diff --git a/third_party/rust/deranged/src/traits.rs b/third_party/rust/deranged/src/traits.rs new file mode 100644 index 000000000000..d1b69ac01f90 --- /dev/null +++ b/third_party/rust/deranged/src/traits.rs @@ -0,0 +1,117 @@ +use crate::{ + RangedI128, RangedI16, RangedI32, RangedI64, RangedI8, RangedIsize, RangedU128, RangedU16, + RangedU32, RangedU64, RangedU8, RangedUsize, +}; + +macro_rules! declare_traits { + ($($trait_name:ident),* $(,)?) => {$( + pub(crate) trait $trait_name { + const ASSERT: (); + } + )*}; +} + +macro_rules! impl_traits_for_all { + ($($ranged_ty:ident $inner_ty:ident),* $(,)?) => {$( + impl RangeIsValid for $ranged_ty { + const ASSERT: () = assert!(MIN <= MAX); + } + + impl< + const CURRENT_MIN: $inner_ty, + const CURRENT_MAX: $inner_ty, + const NEW_MIN: $inner_ty, + const NEW_MAX: $inner_ty, + > ExpandIsValid for ($ranged_ty, $ranged_ty) { + const ASSERT: () = { + assert!(NEW_MIN <= CURRENT_MIN); + assert!(NEW_MAX >= CURRENT_MAX); + }; + } + + impl< + const CURRENT_MIN: $inner_ty, + const CURRENT_MAX: $inner_ty, + const NEW_MIN: $inner_ty, + const NEW_MAX: $inner_ty, + > NarrowIsValid for ($ranged_ty, $ranged_ty) { + const ASSERT: () = { + assert!(NEW_MIN >= CURRENT_MIN); + assert!(NEW_MAX <= CURRENT_MAX); + }; + } + + impl< + const VALUE: $inner_ty, + const MIN: $inner_ty, + const MAX: $inner_ty, + > StaticIsValid for ($ranged_ty, $ranged_ty) { + const ASSERT: () = { + assert!(VALUE >= MIN); + assert!(VALUE <= MAX); + }; + } + )*}; +} + +macro_rules! impl_traits_for_signed { + ($($ranged_ty:ident $inner_ty:ident),* $(,)?) => {$( + impl AbsIsSafe for $ranged_ty { + const ASSERT: () = { + assert!(MIN != <$inner_ty>::MIN); + assert!(-MIN <= MAX); + }; + } + + impl NegIsSafe for $ranged_ty { + const ASSERT: () = { + assert!(MIN != <$inner_ty>::MIN); + assert!(-MIN <= MAX); + assert!(-MAX >= MIN); + }; + } + + impl_traits_for_all!($ranged_ty $inner_ty); + )*}; +} + +macro_rules! impl_traits_for_unsigned { + ($($ranged_ty:ident $inner_ty:ident),* $(,)?) => {$( + impl AbsIsSafe for $ranged_ty { + const ASSERT: () = (); + } + + impl NegIsSafe for $ranged_ty { + const ASSERT: () = assert!(MAX == 0); + } + + impl_traits_for_all!($ranged_ty $inner_ty); + )*}; +} + +declare_traits![ + RangeIsValid, + AbsIsSafe, + NegIsSafe, + ExpandIsValid, + NarrowIsValid, + StaticIsValid, +]; + +impl_traits_for_signed! { + RangedI8 i8, + RangedI16 i16, + RangedI32 i32, + RangedI64 i64, + RangedI128 i128, + RangedIsize isize, +} + +impl_traits_for_unsigned! { + RangedU8 u8, + RangedU16 u16, + RangedU32 u32, + RangedU64 u64, + RangedU128 u128, + RangedUsize usize, +} diff --git a/third_party/rust/deranged/src/unsafe_wrapper.rs b/third_party/rust/deranged/src/unsafe_wrapper.rs new file mode 100644 index 000000000000..8620e1217654 --- /dev/null +++ b/third_party/rust/deranged/src/unsafe_wrapper.rs @@ -0,0 +1,26 @@ +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub(crate) struct Unsafe(T); + +impl core::fmt::Debug for Unsafe { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + self.0.fmt(f) + } +} + +impl Unsafe { + pub(crate) const unsafe fn new(value: T) -> Self { + Self(value) + } + + pub(crate) const fn get(&self) -> &T { + &self.0 + } +} + +impl core::ops::Deref for Unsafe { + type Target = T; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} diff --git a/third_party/rust/num-conv/.cargo-checksum.json b/third_party/rust/num-conv/.cargo-checksum.json new file mode 100644 index 000000000000..3886e697814a --- /dev/null +++ b/third_party/rust/num-conv/.cargo-checksum.json @@ -0,0 +1 @@ +{"files":{"Cargo.toml":"c1d8999190f493d43b84b07eaffd2a9144e16be197bb3ef13eb69305e2f23047","LICENSE-Apache":"c0fd5f9df8d17e13587f8fe403d2326b835e60d532817d0b42ae4aea44209251","LICENSE-MIT":"af85fff507d80e6c7ff242acfc4b0a7f5de9a72286bb3c883c782772ca4b4402","src/lib.rs":"ab6c4b28902164204179f5c31473753fbe5220a4b23082e227478e19c2aa47ca"},"package":"51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"} \ No newline at end of file diff --git a/third_party/rust/num-conv/Cargo.toml b/third_party/rust/num-conv/Cargo.toml new file mode 100644 index 000000000000..0b8a9a6c01f8 --- /dev/null +++ b/third_party/rust/num-conv/Cargo.toml @@ -0,0 +1,55 @@ +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO +# +# When uploading crates to the registry Cargo will automatically +# "normalize" Cargo.toml files for maximal compatibility +# with all versions of Cargo and also rewrite `path` dependencies +# to registry (e.g., crates.io) dependencies. +# +# If you are reading this file be aware that the original Cargo.toml +# will likely look very different (and much more reasonable). +# See Cargo.toml.orig for the original contents. + +[package] +edition = "2021" +rust-version = "1.57.0" +name = "num-conv" +version = "0.1.0" +authors = ["Jacob Pratt "] +include = [ + "src/**/*", + "LICENSE-*", +] +description = """ +`num_conv` is a crate to convert between integer types without using `as` casts. This provides +better certainty when refactoring, makes the exact behavior of code more explicit, and allows using +turbofish syntax. +""" +readme = "README.md" +keywords = [ + "cast", + "extend", + "truncate", + "convert", + "integer", +] +categories = [ + "no-std", + "no-std::no-alloc", + "rust-patterns", +] +license = "MIT OR Apache-2.0" +repository = "https://github.com/jhpratt/num-conv" + +[package.metadata.docs.rs] +rustdoc-args = ["--generate-link-to-definition"] + +[features] + +[lints.clippy] +alloc-instead-of-core = "deny" +std-instead-of-core = "deny" + +[lints.rust] +missing-docs = "warn" +unreachable-pub = "warn" +unused = "warn" diff --git a/third_party/rust/num-conv/LICENSE-Apache b/third_party/rust/num-conv/LICENSE-Apache new file mode 100644 index 000000000000..d8bca8b9f1d1 --- /dev/null +++ b/third_party/rust/num-conv/LICENSE-Apache @@ -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 2023 Jacob Pratt + + 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. diff --git a/third_party/rust/num-conv/LICENSE-MIT b/third_party/rust/num-conv/LICENSE-MIT new file mode 100644 index 000000000000..7c7f78a5ed8b --- /dev/null +++ b/third_party/rust/num-conv/LICENSE-MIT @@ -0,0 +1,19 @@ +Copyright (c) 2023 Jacob Pratt + +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. diff --git a/third_party/rust/num-conv/src/lib.rs b/third_party/rust/num-conv/src/lib.rs new file mode 100644 index 000000000000..cbf3ff21d635 --- /dev/null +++ b/third_party/rust/num-conv/src/lib.rs @@ -0,0 +1,329 @@ +//! `num_conv` is a crate to convert between integer types without using `as` casts. This provides +//! better certainty when refactoring, makes the exact behavior of code more explicit, and allows +//! using turbofish syntax. + +#![no_std] + +/// Anonymously import all extension traits. +/// +/// This allows you to use the methods without worrying about polluting the namespace or importing +/// them individually. +/// +/// ```rust +/// use num_conv::prelude::*; +/// ``` +pub mod prelude { + pub use crate::{CastSigned as _, CastUnsigned as _, Extend as _, Truncate as _}; +} + +mod sealed { + pub trait Integer {} + + macro_rules! impl_integer { + ($($t:ty)*) => {$( + impl Integer for $t {} + )*}; + } + + impl_integer! { + u8 u16 u32 u64 u128 usize + i8 i16 i32 i64 i128 isize + } + + pub trait ExtendTargetSealed { + fn extend(self) -> T; + } + + pub trait TruncateTargetSealed { + fn truncate(self) -> T; + } +} + +/// Cast to a signed integer of the same size. +/// +/// This trait is implemented for all integers. Unsigned to signed casts are equivalent to +/// `0.wrapping_add_signed(value)`, while signed to signed casts are an identity conversion. +/// +/// ```rust +/// # use num_conv::CastSigned; +/// assert_eq!(u8::MAX.cast_signed(), -1_i8); +/// assert_eq!(u16::MAX.cast_signed(), -1_i16); +/// assert_eq!(u32::MAX.cast_signed(), -1_i32); +/// assert_eq!(u64::MAX.cast_signed(), -1_i64); +/// assert_eq!(u128::MAX.cast_signed(), -1_i128); +/// assert_eq!(usize::MAX.cast_signed(), -1_isize); +/// ``` +/// +/// ```rust +/// # use num_conv::CastSigned; +/// assert_eq!(0_i8.cast_signed(), 0_i8); +/// assert_eq!(0_i16.cast_signed(), 0_i16); +/// assert_eq!(0_i32.cast_signed(), 0_i32); +/// assert_eq!(0_i64.cast_signed(), 0_i64); +/// assert_eq!(0_i128.cast_signed(), 0_i128); +/// assert_eq!(0_isize.cast_signed(), 0_isize); +/// ``` +pub trait CastSigned: sealed::Integer { + /// The signed integer type with the same size as `Self`. + type Signed; + + /// Cast an integer to the signed integer of the same size. + fn cast_signed(self) -> Self::Signed; +} + +/// Cast to an unsigned integer of the same size. +/// +/// This trait is implemented for all integers. Signed to unsigned casts are equivalent to +/// `0.wrapping_add_unsigned(value)`, while unsigned to unsigned casts are an identity conversion. +/// +/// ```rust +/// # use num_conv::CastUnsigned; +/// assert_eq!((-1_i8).cast_unsigned(), u8::MAX); +/// assert_eq!((-1_i16).cast_unsigned(), u16::MAX); +/// assert_eq!((-1_i32).cast_unsigned(), u32::MAX); +/// assert_eq!((-1_i64).cast_unsigned(), u64::MAX); +/// assert_eq!((-1_i128).cast_unsigned(), u128::MAX); +/// assert_eq!((-1_isize).cast_unsigned(), usize::MAX); +/// ``` +/// +/// ```rust +/// # use num_conv::CastUnsigned; +/// assert_eq!(0_u8.cast_unsigned(), 0_u8); +/// assert_eq!(0_u16.cast_unsigned(), 0_u16); +/// assert_eq!(0_u32.cast_unsigned(), 0_u32); +/// assert_eq!(0_u64.cast_unsigned(), 0_u64); +/// assert_eq!(0_u128.cast_unsigned(), 0_u128); +/// assert_eq!(0_usize.cast_unsigned(), 0_usize); +/// ``` +pub trait CastUnsigned: sealed::Integer { + /// The unsigned integer type with the same size as `Self`. + type Unsigned; + + /// Cast an integer to the unsigned integer of the same size. + fn cast_unsigned(self) -> Self::Unsigned; +} + +/// A type that can be used with turbofish syntax in [`Extend::extend`]. +/// +/// It is unlikely that you will want to use this trait directly. You are probably looking for the +/// [`Extend`] trait. +pub trait ExtendTarget: sealed::ExtendTargetSealed {} + +/// A type that can be used with turbofish syntax in [`Truncate::truncate`]. +/// +/// It is unlikely that you will want to use this trait directly. You are probably looking for the +/// [`Truncate`] trait. +pub trait TruncateTarget: sealed::TruncateTargetSealed {} + +/// Extend to an integer of the same size or larger, preserving its value. +/// +/// ```rust +/// # use num_conv::Extend; +/// assert_eq!(0_u8.extend::(), 0_u16); +/// assert_eq!(0_u16.extend::(), 0_u32); +/// assert_eq!(0_u32.extend::(), 0_u64); +/// assert_eq!(0_u64.extend::(), 0_u128); +/// ``` +/// +/// ```rust +/// # use num_conv::Extend; +/// assert_eq!((-1_i8).extend::(), -1_i16); +/// assert_eq!((-1_i16).extend::(), -1_i32); +/// assert_eq!((-1_i32).extend::(), -1_i64); +/// assert_eq!((-1_i64).extend::(), -1_i128); +/// ``` +pub trait Extend: sealed::Integer { + /// Extend an integer to an integer of the same size or larger, preserving its value. + fn extend(self) -> T + where + Self: ExtendTarget; +} + +impl Extend for T { + fn extend(self) -> U + where + T: ExtendTarget, + { + sealed::ExtendTargetSealed::extend(self) + } +} + +/// Truncate to an integer of the same size or smaller, preserving the least significant bits. +/// +/// ```rust +/// # use num_conv::Truncate; +/// assert_eq!(u16::MAX.truncate::(), u8::MAX); +/// assert_eq!(u32::MAX.truncate::(), u16::MAX); +/// assert_eq!(u64::MAX.truncate::(), u32::MAX); +/// assert_eq!(u128::MAX.truncate::(), u64::MAX); +/// ``` +/// +/// ```rust +/// # use num_conv::Truncate; +/// assert_eq!((-1_i16).truncate::(), -1_i8); +/// assert_eq!((-1_i32).truncate::(), -1_i16); +/// assert_eq!((-1_i64).truncate::(), -1_i32); +/// assert_eq!((-1_i128).truncate::(), -1_i64); +/// ``` +pub trait Truncate: sealed::Integer { + /// Truncate an integer to an integer of the same size or smaller, preserving the least + /// significant bits. + fn truncate(self) -> T + where + Self: TruncateTarget; +} + +impl Truncate for T { + fn truncate(self) -> U + where + T: TruncateTarget, + { + sealed::TruncateTargetSealed::truncate(self) + } +} + +macro_rules! impl_cast_signed { + ($($($from:ty),+ => $to:ty;)*) => {$($( + const _: () = assert!( + core::mem::size_of::<$from>() == core::mem::size_of::<$to>(), + concat!( + "cannot cast ", + stringify!($from), + " to ", + stringify!($to), + " because they are different sizes" + ) + ); + + impl CastSigned for $from { + type Signed = $to; + fn cast_signed(self) -> Self::Signed { + self as _ + } + } + )+)*}; +} + +macro_rules! impl_cast_unsigned { + ($($($from:ty),+ => $to:ty;)*) => {$($( + const _: () = assert!( + core::mem::size_of::<$from>() == core::mem::size_of::<$to>(), + concat!( + "cannot cast ", + stringify!($from), + " to ", + stringify!($to), + " because they are different sizes" + ) + ); + + impl CastUnsigned for $from { + type Unsigned = $to; + fn cast_unsigned(self) -> Self::Unsigned { + self as _ + } + } + )+)*}; +} + +macro_rules! impl_extend { + ($($from:ty => $($to:ty),+;)*) => {$($( + const _: () = assert!( + core::mem::size_of::<$from>() <= core::mem::size_of::<$to>(), + concat!( + "cannot extend ", + stringify!($from), + " to ", + stringify!($to), + " because ", + stringify!($from), + " is larger than ", + stringify!($to) + ) + ); + + impl sealed::ExtendTargetSealed<$to> for $from { + fn extend(self) -> $to { + self as _ + } + } + + impl ExtendTarget<$to> for $from {} + )+)*}; +} + +macro_rules! impl_truncate { + ($($($from:ty),+ => $to:ty;)*) => {$($( + const _: () = assert!( + core::mem::size_of::<$from>() >= core::mem::size_of::<$to>(), + concat!( + "cannot truncate ", + stringify!($from), + " to ", + stringify!($to), + " because ", + stringify!($from), + " is smaller than ", + stringify!($to) + ) + ); + + impl sealed::TruncateTargetSealed<$to> for $from { + fn truncate(self) -> $to { + self as _ + } + } + + impl TruncateTarget<$to> for $from {} + )+)*}; +} + +impl_cast_signed! { + u8, i8 => i8; + u16, i16 => i16; + u32, i32 => i32; + u64, i64 => i64; + u128, i128 => i128; + usize, isize => isize; +} + +impl_cast_unsigned! { + u8, i8 => u8; + u16, i16 => u16; + u32, i32 => u32; + u64, i64 => u64; + u128, i128 => u128; + usize, isize => usize; +} + +impl_extend! { + u8 => u8, u16, u32, u64, u128, usize; + u16 => u16, u32, u64, u128, usize; + u32 => u32, u64, u128; + u64 => u64, u128; + u128 => u128; + usize => usize; + + i8 => i8, i16, i32, i64, i128, isize; + i16 => i16, i32, i64, i128, isize; + i32 => i32, i64, i128; + i64 => i64, i128; + i128 => i128; + isize => isize; +} + +impl_truncate! { + u8, u16, u32, u64, u128, usize => u8; + u16, u32, u64, u128, usize => u16; + u32, u64, u128 => u32; + u64, u128 => u64; + u128 => u128; + usize => usize; + + i8, i16, i32, i64, i128, isize => i8; + i16, i32, i64, i128, isize => i16; + i32, i64, i128 => i32; + i64, i128 => i64; + i128 => i128; + isize => isize; +} diff --git a/third_party/rust/powerfmt/.cargo-checksum.json b/third_party/rust/powerfmt/.cargo-checksum.json new file mode 100644 index 000000000000..15fec13022f6 --- /dev/null +++ b/third_party/rust/powerfmt/.cargo-checksum.json @@ -0,0 +1 @@ +{"files":{"Cargo.toml":"59fa10abb1a34f70e61c97022938b02ec77ea0b161524f4a2599440bd3190c3b","LICENSE-Apache":"155420c6403d4e0fca34105e3c03fdd6939b64c393c7ec6f95f5b72c5474eab0","LICENSE-MIT":"070dbc7dda03a29296f2d58bdb9b7331af90f2abc9f31df22875d1eabaf29852","README.md":"188fa8a1323086828b9eeaf5a2031d66f066b617fc7dec318835a685d7c2e3c7","src/buf.rs":"b76bcb3daff67ed24e3e5fd958d98565c753107d368c3204ae0d70f9ca7394d4","src/ext.rs":"e6e5063f0006bfe92b59712032d4c6dfe1e1d302d8c92596f3eb7b42f747b9b4","src/lib.rs":"b7a6d061f8d79ed7d78088edefb5946d8eb911b1b523ef849f5f989533955b03","src/smart_display.rs":"44d89db6dbefc1b90e2e3e42279d9ae58f77baeab27a30cafebdb84bfdfaf03c","src/smart_display_impls.rs":"833c5dcb851f7979b222f4736e704ab50bba4d42ef264253dc57237381ef0e5b"},"package":"439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"} \ No newline at end of file diff --git a/third_party/rust/powerfmt/Cargo.toml b/third_party/rust/powerfmt/Cargo.toml new file mode 100644 index 000000000000..f7acec39445b --- /dev/null +++ b/third_party/rust/powerfmt/Cargo.toml @@ -0,0 +1,59 @@ +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO +# +# When uploading crates to the registry Cargo will automatically +# "normalize" Cargo.toml files for maximal compatibility +# with all versions of Cargo and also rewrite `path` dependencies +# to registry (e.g., crates.io) dependencies. +# +# If you are reading this file be aware that the original Cargo.toml +# will likely look very different (and much more reasonable). +# See Cargo.toml.orig for the original contents. + +[package] +edition = "2021" +rust-version = "1.67.0" +name = "powerfmt" +version = "0.2.0" +authors = ["Jacob Pratt "] +description = """ + `powerfmt` is a library that provides utilities for formatting values. This crate makes it + significantly easier to support filling to a minimum width with alignment, avoid heap + allocation, and avoid repetitive calculations. +""" +readme = "README.md" +keywords = [ + "display", + "format", + "fmt", + "formatter", + "extension", +] +categories = [ + "no-std", + "no-std::no-alloc", + "rust-patterns", +] +license = "MIT OR Apache-2.0" +repository = "https://github.com/jhpratt/powerfmt" + +[package.metadata.docs.rs] +all-features = true +rustdoc-args = [ + "--cfg", + "__powerfmt_docs", + "--generate-link-to-definition", +] +targets = ["x86_64-unknown-linux-gnu"] + +[dependencies.powerfmt-macros] +version = "=0.1.0" +optional = true + +[features] +alloc = [] +default = [ + "std", + "macros", +] +macros = ["dep:powerfmt-macros"] +std = ["alloc"] diff --git a/third_party/rust/powerfmt/LICENSE-Apache b/third_party/rust/powerfmt/LICENSE-Apache new file mode 100644 index 000000000000..ddde1f9a0fde --- /dev/null +++ b/third_party/rust/powerfmt/LICENSE-Apache @@ -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 2023 Jacob Pratt et al. + + 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. diff --git a/third_party/rust/powerfmt/LICENSE-MIT b/third_party/rust/powerfmt/LICENSE-MIT new file mode 100644 index 000000000000..89c1f78cb468 --- /dev/null +++ b/third_party/rust/powerfmt/LICENSE-MIT @@ -0,0 +1,19 @@ +Copyright (c) 2023 Jacob Pratt et al. + +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. diff --git a/third_party/rust/powerfmt/README.md b/third_party/rust/powerfmt/README.md new file mode 100644 index 000000000000..c22a3e2fe08d --- /dev/null +++ b/third_party/rust/powerfmt/README.md @@ -0,0 +1,45 @@ +# `powerfmt` + +[![minimum rustc: 1.65](https://img.shields.io/badge/minimum%20rustc-1.65-yellowgreen?logo=rust&style=flat-square)](https://www.whatrustisit.com) +[![version](https://img.shields.io/crates/v/powerfmt?color=blue&logo=rust&style=flat-square)](https://crates.io/crates/powerfmt) +[![build status](https://img.shields.io/github/actions/workflow/status/jhpratt/powerfmt/build.yaml?branch=main&style=flat-square)](https://github.com/jhpratt/powerfmt/actions) + +Documentation is available [on docs.rs](https://docs.rs/powerfmt). + +## Minimum Rust version policy + +`powerfmt` is guaranteed to compile with the latest stable release of Rust in addition to the two +prior minor releases. For example, if the latest stable Rust release is 1.70, then `powerfmt` is +guaranteed to compile with Rust 1.68, 1.69, and 1.70. + +The minimum supported Rust version may be increased to one of the aforementioned versions if doing +so provides the end user a benefit. However, the minimum supported Rust version may also be bumped +to a version four minor releases prior to the most recent stable release if doing so improves code +quality or maintainability. + +For interoperability with third-party crates, it is guaranteed that there exists a version of that +crate that supports the minimum supported Rust version of `powerfmt`. This does not mean that the +latest version of the third-party crate supports the minimum supported Rust version of `powerfmt`. + +## Contributing + +Contributions are always welcome! If you have an idea, it's best to float it by me before working on +it to ensure no effort is wasted. If there's already an open issue for it, knock yourself out. + +If you have any questions, feel free to use [Discussions]. Don't hesitate to ask questions — that's +what I'm here for! + +[Discussions]: https://github.com/jhpratt/powerfmt/discussions + +## License + +This project is licensed under either of + +- [Apache License, Version 2.0](https://github.com/jhpratt/powerfmt/blob/main/LICENSE-Apache) +- [MIT license](https://github.com/jhpratt/powerfmt/blob/main/LICENSE-MIT) + +at your option. + +Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in +time by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any +additional terms or conditions. diff --git a/third_party/rust/powerfmt/src/buf.rs b/third_party/rust/powerfmt/src/buf.rs new file mode 100644 index 000000000000..5a57a60a3539 --- /dev/null +++ b/third_party/rust/powerfmt/src/buf.rs @@ -0,0 +1,198 @@ +//! A buffer for constructing a string while avoiding heap allocation. + +use core::hash::{Hash, Hasher}; +use core::mem::MaybeUninit; +use core::{fmt, str}; + +use crate::smart_display::{FormatterOptions, Metadata, SmartDisplay}; + +/// A buffer for construct a string while avoiding heap allocation. +/// +/// The only requirement is that the buffer is large enough to hold the formatted string. +pub struct WriteBuffer { + buf: [MaybeUninit; SIZE], + len: usize, +} + +impl fmt::Debug for WriteBuffer { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("DisplayBuffer") + .field("buf", &self.as_str()) + .field("remaining_capacity", &self.remaining_capacity()) + .finish() + } +} + +impl WriteBuffer { + /// Creates an empty buffer. + pub const fn new() -> Self { + Self { + buf: maybe_uninit_uninit_array::<_, SIZE>(), + len: 0, + } + } + + /// Obtain the contents of the buffer as a string. + pub fn as_str(&self) -> &str { + self + } + + /// Determine how many bytes are remaining in the buffer. + pub const fn remaining_capacity(&self) -> usize { + SIZE - self.len + } +} + +impl Default for WriteBuffer { + fn default() -> Self { + Self::new() + } +} + +impl PartialOrd> + for WriteBuffer +{ + fn partial_cmp(&self, other: &WriteBuffer) -> Option { + self.as_str().partial_cmp(other.as_str()) + } +} + +impl PartialEq> + for WriteBuffer +{ + fn eq(&self, other: &WriteBuffer) -> bool { + self.as_str() == other.as_str() + } +} + +impl Eq for WriteBuffer {} + +impl Ord for WriteBuffer { + fn cmp(&self, other: &Self) -> core::cmp::Ordering { + self.as_str().cmp(other.as_str()) + } +} + +impl Hash for WriteBuffer { + fn hash(&self, state: &mut H) { + self.as_str().hash(state) + } +} + +impl AsRef for WriteBuffer { + fn as_ref(&self) -> &str { + self + } +} + +impl AsRef<[u8]> for WriteBuffer { + fn as_ref(&self) -> &[u8] { + self.as_bytes() + } +} + +impl core::borrow::Borrow for WriteBuffer { + fn borrow(&self) -> &str { + self + } +} + +impl core::ops::Deref for WriteBuffer { + type Target = str; + + fn deref(&self) -> &Self::Target { + // SAFETY: `buf` is only written to by the `fmt::Write::write_str` implementation which + // writes a valid UTF-8 string to `buf` and correctly sets `len`. + unsafe { + let s = maybe_uninit_slice_assume_init_ref(&self.buf[..self.len]); + str::from_utf8_unchecked(s) + } + } +} + +impl fmt::Display for WriteBuffer { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(self) + } +} + +impl SmartDisplay for WriteBuffer { + type Metadata = (); + + fn metadata(&self, _: FormatterOptions) -> Metadata<'_, Self> { + Metadata::new(self.len, self, ()) + } + + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.pad(self) + } +} + +impl fmt::Write for WriteBuffer { + fn write_str(&mut self, s: &str) -> fmt::Result { + let bytes = s.as_bytes(); + + if let Some(buf) = self.buf.get_mut(self.len..(self.len + bytes.len())) { + maybe_uninit_write_slice(buf, bytes); + self.len += bytes.len(); + Ok(()) + } else { + Err(fmt::Error) + } + } +} + +/// Equivalent of [`MaybeUninit::uninit_array`] that compiles on stable. +#[must_use] +#[inline(always)] +const fn maybe_uninit_uninit_array() -> [MaybeUninit; N] { + // SAFETY: An uninitialized `[MaybeUninit<_>; LEN]` is valid. + unsafe { MaybeUninit::<[MaybeUninit; N]>::uninit().assume_init() } +} + +/// Equivalent of [`MaybeUninit::write_slice`] that compiles on stable. +fn maybe_uninit_write_slice<'a, T>(this: &'a mut [MaybeUninit], src: &[T]) -> &'a mut [T] +where + T: Copy, +{ + #[allow(trivial_casts)] + // SAFETY: T and MaybeUninit have the same layout + let uninit_src = unsafe { &*(src as *const [T] as *const [MaybeUninit]) }; + + this.copy_from_slice(uninit_src); + + // SAFETY: Valid elements have just been copied into `this` so it is initialized + unsafe { maybe_uninit_slice_assume_init_mut(this) } +} + +/// Equivalent of [`MaybeUninit::slice_assume_init_mut`] that compiles on stable. +/// +/// # Safety +/// +/// See [`MaybeUninit::slice_assume_init_mut`](https://doc.rust-lang.org/stable/std/mem/union.MaybeUninit.html#method.slice_assume_init_mut). +#[inline(always)] +unsafe fn maybe_uninit_slice_assume_init_mut(slice: &mut [MaybeUninit]) -> &mut [U] { + #[allow(trivial_casts)] + // SAFETY: similar to safety notes for `slice_get_ref`, but we have a mutable reference which is + // also guaranteed to be valid for writes. + unsafe { + &mut *(slice as *mut [MaybeUninit] as *mut [U]) + } +} + +/// Equivalent of [`MaybeUninit::slice_assume_init_ref`] that compiles on stable. +/// +/// # Safety +/// +/// See [`MaybeUninit::slice_assume_init_ref`](https://doc.rust-lang.org/stable/std/mem/union.MaybeUninit.html#method.slice_assume_init_ref). +#[inline(always)] +const unsafe fn maybe_uninit_slice_assume_init_ref(slice: &[MaybeUninit]) -> &[T] { + #[allow(trivial_casts)] + // SAFETY: casting `slice` to a `*const [T]` is safe since the caller guarantees that `slice` is + // initialized, and `MaybeUninit` is guaranteed to have the same layout as `T`. The pointer + // obtained is valid since it refers to memory owned by `slice` which is a reference and thus + // guaranteed to be valid for reads. + unsafe { + &*(slice as *const [MaybeUninit] as *const [T]) + } +} diff --git a/third_party/rust/powerfmt/src/ext.rs b/third_party/rust/powerfmt/src/ext.rs new file mode 100644 index 000000000000..20af7c0aaba4 --- /dev/null +++ b/third_party/rust/powerfmt/src/ext.rs @@ -0,0 +1,54 @@ +//! Extension traits. + +use core::fmt::{Alignment, Arguments, Formatter, Result, Write}; + +mod sealed { + pub trait Sealed {} + + impl Sealed for core::fmt::Formatter<'_> {} +} + +/// An extension trait for [`core::fmt::Formatter`]. +pub trait FormatterExt: sealed::Sealed { + /// Writes the given arguments to the formatter, padding them with the given width. If `width` + /// is incorrect, the resulting output will not be the requested width. + fn pad_with_width(&mut self, width: usize, args: Arguments<'_>) -> Result; +} + +impl FormatterExt for Formatter<'_> { + fn pad_with_width(&mut self, args_width: usize, args: Arguments<'_>) -> Result { + let Some(final_width) = self.width() else { + // The caller has not requested a width. Write the arguments as-is. + return self.write_fmt(args); + }; + let Some(fill_width @ 1..) = final_width.checked_sub(args_width) else { + // No padding will be present. Write the arguments as-is. + return self.write_fmt(args); + }; + + let alignment = self.align().unwrap_or(Alignment::Left); + let fill = self.fill(); + + let left_fill_width = match alignment { + Alignment::Left => 0, + Alignment::Right => fill_width, + Alignment::Center => fill_width / 2, + }; + let right_fill_width = match alignment { + Alignment::Left => fill_width, + Alignment::Right => 0, + // When the fill is not even on both sides, the extra fill goes on the right. + Alignment::Center => (fill_width + 1) / 2, + }; + + for _ in 0..left_fill_width { + self.write_char(fill)?; + } + self.write_fmt(args)?; + for _ in 0..right_fill_width { + self.write_char(fill)?; + } + + Ok(()) + } +} diff --git a/third_party/rust/powerfmt/src/lib.rs b/third_party/rust/powerfmt/src/lib.rs new file mode 100644 index 000000000000..0cd6f7cbb3a7 --- /dev/null +++ b/third_party/rust/powerfmt/src/lib.rs @@ -0,0 +1,15 @@ +//! `powerfmt` is a library that provides utilities for formatting values. Specifically, it makes it +//! significantly easier to support filling to a minimum width with alignment, avoid heap +//! allocation, and avoid repetitive calculations. + +#![cfg_attr(not(feature = "std"), no_std)] +#![cfg_attr(__powerfmt_docs, feature(doc_auto_cfg, rustc_attrs))] +#![cfg_attr(__powerfmt_docs, allow(internal_features))] + +#[cfg(feature = "alloc")] +extern crate alloc; + +pub mod buf; +pub mod ext; +pub mod smart_display; +mod smart_display_impls; diff --git a/third_party/rust/powerfmt/src/smart_display.rs b/third_party/rust/powerfmt/src/smart_display.rs new file mode 100644 index 000000000000..bb55554b87f0 --- /dev/null +++ b/third_party/rust/powerfmt/src/smart_display.rs @@ -0,0 +1,695 @@ +//! Definition of [`SmartDisplay`] and its related items. +//! +//! [`SmartDisplay`] is a trait that allows authors to provide additional information to both the +//! formatter and other users. This information is provided in the form of a metadata type. The only +//! required piece of metadata is the width of the value. This is _before_ it is passed to the +//! formatter (i.e. it does not include any padding added by the formatter). Other information +//! can be stored in a custom metadata type as needed. This information may be made available to +//! downstream users, but it is not required. +//! +//! This module contains the [`SmartDisplay`] and associated items. +//! +//! # Example +//! +//! ```rust +//! use std::fmt; +//! +//! use powerfmt::ext::FormatterExt as _; +//! use powerfmt::smart_display::{self, FormatterOptions, Metadata, SmartDisplay}; +//! +//! #[derive(Debug)] +//! struct User { +//! id: usize, +//! } +//! +//! // If you try to use `UserMetadata` in the `SmartDisplay` implementation, you will get a +//! // compiler error about a private type being used publicly. To avoid this, use this attribute to +//! // declare a private metadata type. You shouldn't need to worry about how this works, but be +//! // aware that any public fields or methods remain usable by downstream users. +//! #[smart_display::private_metadata] +//! struct UserMetadata { +//! username: String, +//! legal_name: String, +//! } +//! +//! // This attribute can be applied to `SmartDisplay` implementations. It will generate an +//! // implementation of `Display` that delegates to `SmartDisplay`, avoiding the need to write +//! // boilerplate. +//! #[smart_display::delegate] +//! impl SmartDisplay for User { +//! type Metadata = UserMetadata; +//! +//! fn metadata(&self, _: FormatterOptions) -> Metadata<'_, Self> { +//! // This could be obtained from a database, for example. +//! let legal_name = "John Doe".to_owned(); +//! let username = "jdoe".to_owned(); +//! +//! // Note that this must be kept in sync with the implementation of `fmt_with_metadata`. +//! let width = smart_display::padded_width_of!(username, " (", legal_name, ")",); +//! +//! Metadata::new( +//! width, +//! self, +//! UserMetadata { +//! username, +//! legal_name, +//! }, +//! ) +//! } +//! +//! // Use the now-generated metadata to format the value. Here we use the `pad_with_width` +//! // method to use the alignment and desired width from the formatter. +//! fn fmt_with_metadata( +//! &self, +//! f: &mut fmt::Formatter<'_>, +//! metadata: Metadata, +//! ) -> fmt::Result { +//! f.pad_with_width( +//! metadata.unpadded_width(), +//! format_args!("{} ({})", metadata.username, metadata.legal_name), +//! ) +//! } +//! } +//! +//! let user = User { id: 42 }; +//! assert_eq!(user.to_string(), "jdoe (John Doe)"); +//! assert_eq!(format!("{user:>20}"), " jdoe (John Doe)"); +//! ``` + +use core::cmp; +use core::convert::Infallible; +use core::fmt::{Alignment, Debug, Display, Formatter, Result}; +use core::marker::PhantomData; +use core::mem::MaybeUninit; +use core::ops::Deref; + +/// Compute the width of multiple items while optionally declaring the options for each item. +/// +/// ```rust +/// # use powerfmt::smart_display; +/// let alpha = 0; +/// let beta = 1; +/// let gamma = 100; +/// +/// let width = smart_display::padded_width_of!( +/// alpha, // use the default options +/// beta => width(2), // use the specified options +/// gamma => width(2) sign_plus(true), // use multiple options +/// ); +/// assert_eq!(width, 7); +/// +/// let formatted = format!("{alpha}{beta:2}{gamma:+2}"); +/// assert_eq!(formatted.len(), width); +/// ``` +/// +/// Supported options are: +/// +/// Option | Method called +/// --- | --- +/// `fill(char)` | [`FormatterOptions::with_fill`] +/// `sign_plus(bool)` | [`FormatterOptions::with_sign_plus`] +/// `sign_minus(bool)` | [`FormatterOptions::with_sign_minus`] +/// `align(Alignment)` | [`FormatterOptions::with_align`] +/// `width(usize)` | [`FormatterOptions::with_width`] +/// `precision(usize)` | [`FormatterOptions::with_precision`] +/// `alternate(bool)` | [`FormatterOptions::with_alternate`] +/// `sign_aware_zero_pad(bool)` | [`FormatterOptions::with_sign_aware_zero_pad`] +/// +/// If there are future additions to [`FormatterOptions`], they will be added to this macro as well. +/// +/// Options may be provided in any order and will be called in the order they are provided. The +/// ordering matters if providing both `sign_plus` and `sign_minus`. +#[cfg(doc)] +#[doc(hidden)] // Don't show at crate root. +#[macro_export] +macro_rules! padded_width_of { + ($($t:tt)*) => {}; +} + +#[cfg(not(doc))] +#[allow(missing_docs)] // This is done with `#[cfg(doc)]` to avoid showing the various rules. +#[macro_export] +macro_rules! __not_public_at_root__padded_width_of { + // Base case + (@inner [] [$($output:tt)+]) => { $($output)+ }; + (@inner [$e:expr $(, $($remaining:tt)*)?] [$($expansion:tt)+]) => { + $crate::smart_display::padded_width_of!(@inner [$($($remaining)*)?] [ + $($expansion)+ + $crate::smart_display::Metadata::padded_width_of( + &$e, + $crate::smart_display::padded_width_of!(@options) + ) + ]) + }; + (@inner + [$e:expr => $($call:ident($call_expr:expr))+ $(, $($remaining:tt)*)?] + [$($expansion:tt)+] + ) => { + $crate::smart_display::padded_width_of!(@inner [$($($remaining)*)?] [ + $($expansion)+ + $crate::smart_display::Metadata::padded_width_of( + &$e, + *$crate::smart_display::padded_width_of!(@options $($call($call_expr))+) + ) + ]) + }; + + // Options base case + (@options_inner [] [$($output:tt)+]) => { $($output)+ }; + (@options_inner [fill($e:expr) $($remaining:tt)*] [$($expansion:tt)*]) => { + $crate::smart_display::padded_width_of!(@options_inner [$($remaining)*] [ + $($expansion)*.with_fill($e) + ]) + }; + (@options_inner [sign_plus($e:expr) $($remaining:tt)*] [$($expansion:tt)*]) => { + $crate::smart_display::padded_width_of!(@options_inner [$($remaining)*] [ + $($expansion)*.with_sign_plus($e) + ]) + }; + (@options_inner [sign_minus($e:expr) $($remaining:tt)*] [$($expansion:tt)*]) => { + $crate::smart_display::padded_width_of!(@options_inner [$($remaining)*] [ + $($expansion)*.with_sign_minus($e) + ]) + }; + (@options_inner [align($e:expr) $($remaining:tt)*] [$($expansion:tt)*]) => { + $crate::smart_display::padded_width_of!(@options_inner [$($remaining)*] [ + $($expansion)*.with_align(Some($e)) + ]) + }; + (@options_inner [width($e:expr) $($remaining:tt)*] [$($expansion:tt)*]) => { + $crate::smart_display::padded_width_of!(@options_inner [$($remaining)*] [ + $($expansion)*.with_width(Some($e)) + ]) + }; + (@options_inner [precision($e:expr) $($remaining:tt)*] [$($expansion:tt)*]) => { + $crate::smart_display::padded_width_of!(@options_inner [$($remaining)*] [ + $($expansion)*.with_precision(Some($e)) + ]) + }; + (@options_inner [alternate($e:expr) $($remaining:tt)*] [$($expansion:tt)*]) => { + $crate::smart_display::padded_width_of!(@options_inner [$($remaining)*] [ + $($expansion)*.with_width($e) + ]) + }; + (@options_inner [sign_aware_zero_pad($e:expr) $($remaining:tt)*] [$($expansion:tt)*]) => { + $crate::smart_display::padded_width_of!(@options_inner [$($remaining)*] [ + $($expansion)*.with_sign_aware_zero_pad($e) + ]) + }; + // Options entry point + (@options $($e:tt)*) => { + $crate::smart_display::padded_width_of!(@options_inner [$($e)*] [ + $crate::smart_display::FormatterOptions::default() + ]) + }; + + // Entry point + ($($t:tt)*) => { + $crate::smart_display::padded_width_of!( + @inner [$($t)*] [0] + ) + }; +} + +#[cfg(not(doc))] +pub use __not_public_at_root__padded_width_of as padded_width_of; +#[cfg(doc)] +#[doc(inline)] // Show in this module. +pub use padded_width_of; +/// Implement [`Display`] for a type by using its implementation of [`SmartDisplay`]. +/// +/// This attribute is applied to the `SmartDisplay` implementation. +/// +/// ```rust,no_run +/// # use powerfmt::smart_display::{self, SmartDisplay, Metadata, FormatterOptions}; +/// # struct Foo; +/// #[smart_display::delegate] +/// impl SmartDisplay for Foo { +/// # type Metadata = (); +/// # fn metadata(&self, f: FormatterOptions) -> Metadata { +/// # todo!() +/// # } +/// // ... +/// } +/// ``` +#[cfg(feature = "macros")] +pub use powerfmt_macros::smart_display_delegate as delegate; +/// Declare a private metadata type for `SmartDisplay`. +/// +/// Use this attribute if you want to provide metadata for a type that is not public. Doing +/// this will avoid a compiler error about a private type being used publicly. Keep in mind +/// that any public fields, public methods, and trait implementations _will_ be able to be used +/// by downstream users. +/// +/// To avoid accidentally exposing details, such as when all fields are public or if the type +/// is a unit struct, the type is annotated with `#[non_exhaustive]` automatically. +/// +/// ```rust,no_run +/// # use powerfmt::smart_display; +/// /// Metadata for `Foo` +/// #[smart_display::private_metadata] +/// #[derive(Debug)] +/// pub(crate) struct FooMetadata { +/// pub(crate) expensive_to_calculate: usize, +/// } +/// ``` +#[cfg(feature = "macros")] +pub use powerfmt_macros::smart_display_private_metadata as private_metadata; + +#[derive(Debug)] +enum FlagBit { + SignPlus, + SignMinus, + Alternate, + SignAwareZeroPad, + WidthIsInitialized, + PrecisionIsInitialized, +} + +/// Configuration for formatting. +/// +/// This struct is obtained from a [`Formatter`]. It provides the same functionality as that of a +/// reference to a `Formatter`. However, it is not possible to construct a `Formatter`, which is +/// necessary for some use cases of [`SmartDisplay`]. `FormatterOptions` implements [`Default`] and +/// has builder methods to alleviate this. +#[derive(Clone, Copy)] +pub struct FormatterOptions { + flags: u8, + fill: char, + align: Option, + width: MaybeUninit, + precision: MaybeUninit, +} + +impl Debug for FormatterOptions { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + f.debug_struct("FormatterOptions") + .field("fill", &self.fill) + .field("align", &self.align()) + .field("width", &self.width()) + .field("precision", &self.precision()) + .field("sign_plus", &self.sign_plus()) + .field("sign_minus", &self.sign_minus()) + .field("alternate", &self.alternate()) + .field("sign_aware_zero_pad", &self.sign_aware_zero_pad()) + .finish() + } +} + +impl Default for FormatterOptions { + #[inline] + fn default() -> Self { + Self { + flags: 0, + fill: ' ', + align: None, + width: MaybeUninit::uninit(), + precision: MaybeUninit::uninit(), + } + } +} + +impl FormatterOptions { + /// Sets the fill character to use whenever there is alignment. + #[inline] + pub fn with_fill(&mut self, c: char) -> &mut Self { + self.fill = c; + self + } + + /// Set whether the `+` flag is specified. + #[inline] + pub fn with_sign_plus(&mut self, b: bool) -> &mut Self { + if b { + self.flags |= 1 << FlagBit::SignPlus as u8; + self.flags &= !(1 << FlagBit::SignMinus as u8); + } else { + self.flags &= !(1 << FlagBit::SignPlus as u8); + } + self + } + + /// Set whether the `-` flag is specified. + #[inline] + pub fn with_sign_minus(&mut self, b: bool) -> &mut Self { + if b { + self.flags |= 1 << FlagBit::SignMinus as u8; + self.flags &= !(1 << FlagBit::SignPlus as u8); + } else { + self.flags &= !(1 << FlagBit::SignMinus as u8); + } + self + } + + /// Set the flag indicating what form of alignment is requested, if any. + #[inline] + pub fn with_align(&mut self, align: Option) -> &mut Self { + self.align = align; + self + } + + /// Set the optional integer width that the output should be. + #[inline] + pub fn with_width(&mut self, width: Option) -> &mut Self { + if let Some(width) = width { + self.flags |= 1 << FlagBit::WidthIsInitialized as u8; + self.width = MaybeUninit::new(width); + } else { + self.flags &= !(1 << FlagBit::WidthIsInitialized as u8); + } + self + } + + /// Set the optional precision for numeric types. Alternatively, the maximum width for string + /// types. + #[inline] + pub fn with_precision(&mut self, precision: Option) -> &mut Self { + if let Some(precision) = precision { + self.flags |= 1 << FlagBit::PrecisionIsInitialized as u8; + self.precision = MaybeUninit::new(precision); + } else { + self.flags &= !(1 << FlagBit::PrecisionIsInitialized as u8); + } + self + } + + /// Set whether the `#` flag is specified. + #[inline] + pub fn with_alternate(&mut self, b: bool) -> &mut Self { + if b { + self.flags |= 1 << FlagBit::Alternate as u8; + } else { + self.flags &= !(1 << FlagBit::Alternate as u8); + } + self + } + + /// Set whether the `0` flag is specified. + #[inline] + pub fn with_sign_aware_zero_pad(&mut self, b: bool) -> &mut Self { + if b { + self.flags |= 1 << FlagBit::SignAwareZeroPad as u8; + } else { + self.flags &= !(1 << FlagBit::SignAwareZeroPad as u8); + } + self + } +} + +impl FormatterOptions { + /// Character used as 'fill' whenever there is alignment. + #[inline] + #[must_use] + pub const fn fill(&self) -> char { + self.fill + } + + /// Flag indicating what form of alignment was requested. + #[inline] + #[must_use] + pub const fn align(&self) -> Option { + self.align + } + + /// Optionally specified integer width that the output should be. + #[inline] + #[must_use] + pub const fn width(&self) -> Option { + if (self.flags >> FlagBit::WidthIsInitialized as u8) & 1 == 1 { + // Safety: `width` is initialized if the flag is set. + Some(unsafe { self.width.assume_init() }) + } else { + None + } + } + + /// Optionally specified precision for numeric types. Alternatively, the maximum width for + /// string types. + #[inline] + #[must_use] + pub const fn precision(&self) -> Option { + if (self.flags >> FlagBit::PrecisionIsInitialized as u8) & 1 == 1 { + // Safety: `precision` is initialized if the flag is set. + Some(unsafe { self.precision.assume_init() }) + } else { + None + } + } + + /// Determines if the `+` flag was specified. + #[inline] + #[must_use] + pub const fn sign_plus(&self) -> bool { + (self.flags >> FlagBit::SignPlus as u8) & 1 == 1 + } + + /// Determines if the `-` flag was specified. + #[inline] + #[must_use] + pub const fn sign_minus(&self) -> bool { + (self.flags >> FlagBit::SignMinus as u8) & 1 == 1 + } + + /// Determines if the `#` flag was specified. + #[inline] + #[must_use] + pub const fn alternate(&self) -> bool { + (self.flags >> FlagBit::Alternate as u8) & 1 == 1 + } + + /// Determines if the `0` flag was specified. + #[inline] + #[must_use] + pub const fn sign_aware_zero_pad(&self) -> bool { + (self.flags >> FlagBit::SignAwareZeroPad as u8) & 1 == 1 + } +} + +impl From<&Formatter<'_>> for FormatterOptions { + fn from(value: &Formatter<'_>) -> Self { + *Self::default() + .with_fill(value.fill()) + .with_sign_plus(value.sign_plus()) + .with_sign_minus(value.sign_minus()) + .with_align(value.align()) + .with_width(value.width()) + .with_precision(value.precision()) + .with_alternate(value.alternate()) + .with_sign_aware_zero_pad(value.sign_aware_zero_pad()) + } +} + +impl From<&mut Formatter<'_>> for FormatterOptions { + #[inline] + fn from(value: &mut Formatter<'_>) -> Self { + (&*value).into() + } +} + +/// Information used to format a value. This is returned by [`SmartDisplay::metadata`]. +/// +/// This type is generic over any user-provided type. This allows the author to store any +/// information that is needed. For example, a type's implementation of [`SmartDisplay`] may need +/// to calculate something before knowing its width. This calculation can be performed, with the +/// result being stored in the custom metadata type. +/// +/// Note that `Metadata` _always_ contains the width of the type. Authors do not need to store this +/// information in their custom metadata type. +/// +/// Generally speaking, a type should be able to be formatted using only its metadata, fields, and +/// the formatter. Any other information should be stored in the metadata type. +pub struct Metadata<'a, T> +where + T: SmartDisplay + ?Sized, +{ + unpadded_width: usize, + metadata: T::Metadata, + _value: PhantomData<&'a T>, // variance +} + +// manual impls for bounds +impl Debug for Metadata<'_, T> +where + T: SmartDisplay, + T::Metadata: Debug, +{ + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + f.debug_struct("Metadata") + .field("unpadded_width", &self.unpadded_width) + .field("metadata", &self.metadata) + .finish() + } +} + +impl Clone for Metadata<'_, T> +where + T: SmartDisplay, + T::Metadata: Clone, +{ + fn clone(&self) -> Self { + Self { + unpadded_width: self.unpadded_width, + metadata: self.metadata.clone(), + _value: self._value, + } + } +} + +impl Copy for Metadata<'_, T> +where + T: SmartDisplay, + T::Metadata: Copy, +{ +} + +impl<'a, T> Metadata<'a, T> +where + T: SmartDisplay + ?Sized, +{ + /// Creates a new `Metadata` with the given width and metadata. While the width _should_ be + /// exact, this is not a requirement for soundness. + pub const fn new(unpadded_width: usize, _value: &T, metadata: T::Metadata) -> Self { + Self { + unpadded_width, + metadata, + _value: PhantomData, + } + } + + /// Reuse the metadata for another type. This is useful when implementing [`SmartDisplay`] for a + /// type that wraps another type. Both type's metadata type must be the same. + pub fn reuse<'b, U>(self) -> Metadata<'b, U> + where + 'a: 'b, + U: SmartDisplay + ?Sized, + { + Metadata { + unpadded_width: self.unpadded_width, + metadata: self.metadata, + _value: PhantomData, + } + } + + /// Obtain the width of the value before padding. + pub const fn unpadded_width(&self) -> usize { + self.unpadded_width + } + + /// Obtain the width of the value after padding. + pub fn padded_width(&self, f: FormatterOptions) -> usize { + match f.width() { + Some(requested_width) => cmp::max(self.unpadded_width(), requested_width), + None => self.unpadded_width(), + } + } +} + +impl Metadata<'_, Infallible> { + /// Obtain the width of the value before padding, given the formatter options. + pub fn unpadded_width_of(value: T, f: FormatterOptions) -> usize + where + T: SmartDisplay, + { + value.metadata(f).unpadded_width + } + + /// Obtain the width of the value after padding, given the formatter options. + pub fn padded_width_of(value: T, f: FormatterOptions) -> usize + where + T: SmartDisplay, + { + value.metadata(f).padded_width(f) + } +} + +/// Permit using `Metadata` as a smart pointer to the user-provided metadata. +impl Deref for Metadata<'_, T> +where + T: SmartDisplay + ?Sized, +{ + type Target = T::Metadata; + + fn deref(&self) -> &T::Metadata { + &self.metadata + } +} + +/// Format trait that allows authors to provide additional information. +/// +/// This trait is similar to [`Display`], but allows the author to provide additional information +/// to the formatter. This information is provided in the form of a custom metadata type. +/// +/// The only required piece of metadata is the width of the value. This is _before_ it is passed to +/// the formatter (i.e. it does not include any padding added by the formatter). Other information +/// can be stored in a custom metadata type as needed. This information may be made available to +/// downstream users, but it is not required. +/// +/// **Note**: While both `fmt_with_metadata` and `fmt` have default implementations, it is strongly +/// recommended to implement only `fmt_with_metadata`. `fmt` should be implemented if and only if +/// the type does not require any of the calculated metadata. In that situation, `fmt_with_metadata` +/// should be omitted. +#[cfg_attr(__powerfmt_docs, rustc_must_implement_one_of(fmt, fmt_with_metadata))] +pub trait SmartDisplay: Display { + /// User-provided metadata type. + type Metadata; + + /// Compute any information needed to format the value. This must, at a minimum, determine the + /// width of the value before any padding is added by the formatter. + /// + /// If the type uses other types that implement `SmartDisplay` verbatim, the inner types should + /// have their metadata calculated and included in the returned value. + /// + /// # Lifetimes + /// + /// This method's return type contains a lifetime to `self`. This ensures that the metadata will + /// neither outlive the value nor be invalidated by a mutation of the value (barring interior + /// mutability). + /// + /// ```rust,compile_fail + /// # use std::fmt; + /// # use std::fmt::Write; + /// # use powerfmt::buf::WriteBuffer; + /// # use powerfmt::smart_display::{self, FormatterOptions, Metadata, SmartDisplay}; + /// #[derive(Debug)] + /// struct WrappedBuffer(WriteBuffer<128>); + /// + /// #[smart_display::delegate] + /// impl SmartDisplay for WrappedBuffer { + /// type Metadata = (); + /// + /// fn metadata(&self, _: FormatterOptions) -> Metadata<'_, Self> { + /// Metadata::new(self.0.len(), self, ()) + /// } + /// + /// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + /// f.pad(self.0.as_str()) + /// } + /// } + /// + /// let mut buf = WrappedBuffer(WriteBuffer::new()); + /// let metadata = buf.metadata(FormatterOptions::default()); + /// // We cannot mutate the buffer while it is borrowed and use its previous metadata on the + /// // following line. + /// write!(buf.0, "Hello, world!")?; + /// assert_eq!(metadata.width(), 13); + /// # Ok::<(), Box>(()) + /// ``` + fn metadata(&self, f: FormatterOptions) -> Metadata<'_, Self>; + + /// Format the value using the given formatter and metadata. The formatted output should have + /// the width indicated by the metadata. This is before any padding is added by the + /// formatter. + /// + /// If the metadata is not needed, you should implement the `fmt` method instead. + fn fmt_with_metadata(&self, f: &mut Formatter<'_>, _metadata: Metadata<'_, Self>) -> Result { + SmartDisplay::fmt(self, f) + } + + /// Format the value using the given formatter. This is the same as [`Display::fmt`]. + /// + /// The default implementation of this method calls `fmt_with_metadata` with the result of + /// `metadata`. Generally speaking, this method should not be implemented. You should implement + /// the `fmt_with_metadata` method instead. + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + let metadata = self.metadata(f.into()); + self.fmt_with_metadata(f, metadata) + } +} diff --git a/third_party/rust/powerfmt/src/smart_display_impls.rs b/third_party/rust/powerfmt/src/smart_display_impls.rs new file mode 100644 index 000000000000..dc82395f298e --- /dev/null +++ b/third_party/rust/powerfmt/src/smart_display_impls.rs @@ -0,0 +1,303 @@ +//! Implementation of [`SmartDisplay`] for various types. + +#[cfg(feature = "alloc")] +use alloc::borrow::{Cow, ToOwned}; +#[cfg(feature = "alloc")] +use alloc::boxed::Box; +#[cfg(feature = "alloc")] +use alloc::rc::Rc; +#[cfg(feature = "alloc")] +use alloc::string::String; +#[cfg(feature = "alloc")] +use alloc::sync::Arc; +use core::cell::{Ref, RefMut}; +use core::cmp::min; +use core::convert::Infallible; +use core::fmt::{self, Display, Formatter}; +use core::num::Wrapping; +use core::pin::Pin; + +use crate::smart_display::{FormatterOptions, Metadata, SmartDisplay}; + +impl SmartDisplay for Infallible { + type Metadata = Self; + + #[inline] + fn metadata(&self, _: FormatterOptions) -> Metadata<'_, Self> { + match *self {} + } + + #[inline] + fn fmt(&self, _: &mut Formatter<'_>) -> fmt::Result { + match *self {} + } +} + +impl SmartDisplay for bool { + type Metadata = (); + + #[inline] + fn metadata(&self, _: FormatterOptions) -> Metadata<'_, Self> { + Metadata::new(if *self { 4 } else { 5 }, self, ()) + } + + #[inline] + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + Display::fmt(self, f) + } +} + +impl SmartDisplay for str { + type Metadata = (); + + #[inline] + fn metadata(&self, f: FormatterOptions) -> Metadata<'_, Self> { + Metadata::new( + match f.precision() { + Some(max_len) => min(self.len(), max_len), + None => self.len(), + }, + self, + (), + ) + } + + #[inline] + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + Display::fmt(self, f) + } +} + +#[cfg(feature = "alloc")] +impl SmartDisplay for String { + type Metadata = (); + + #[inline] + fn metadata(&self, f: FormatterOptions) -> Metadata<'_, Self> { + (**self).metadata(f).reuse() + } + + #[inline] + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + Display::fmt(self, f) + } +} + +#[cfg(feature = "alloc")] +impl<'a, B, O> SmartDisplay for Cow<'a, B> +where + B: SmartDisplay + ToOwned + ?Sized, + O: SmartDisplay + 'a, +{ + type Metadata = B::Metadata; + + fn metadata(&self, f: FormatterOptions) -> Metadata<'_, Self> { + match *self { + Cow::Borrowed(ref b) => b.metadata(f).reuse(), + Cow::Owned(ref o) => o.metadata(f).reuse(), + } + } + + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + Display::fmt(self, f) + } +} + +impl SmartDisplay for Pin<&T> +where + T: SmartDisplay + ?Sized, +{ + type Metadata = T::Metadata; + + fn metadata(&self, f: FormatterOptions) -> Metadata<'_, Self> { + self.get_ref().metadata(f).reuse() + } + + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + SmartDisplay::fmt(self.get_ref(), f) + } +} + +impl SmartDisplay for &T +where + T: SmartDisplay + ?Sized, +{ + type Metadata = T::Metadata; + + fn metadata(&self, f: FormatterOptions) -> Metadata<'_, Self> { + (**self).metadata(f).reuse() + } + + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + SmartDisplay::fmt(*self, f) + } +} + +impl SmartDisplay for &mut T +where + T: SmartDisplay + ?Sized, +{ + type Metadata = T::Metadata; + + fn metadata(&self, f: FormatterOptions) -> Metadata<'_, Self> { + (**self).metadata(f).reuse() + } + + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + SmartDisplay::fmt(*self, f) + } +} + +impl SmartDisplay for Ref<'_, T> +where + T: SmartDisplay + ?Sized, +{ + type Metadata = T::Metadata; + + fn metadata(&self, f: FormatterOptions) -> Metadata<'_, Self> { + (**self).metadata(f).reuse() + } + + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + SmartDisplay::fmt(&**self, f) + } +} + +impl SmartDisplay for RefMut<'_, T> +where + T: SmartDisplay + ?Sized, +{ + type Metadata = T::Metadata; + + fn metadata(&self, f: FormatterOptions) -> Metadata<'_, Self> { + (**self).metadata(f).reuse() + } + + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + SmartDisplay::fmt(&**self, f) + } +} + +impl SmartDisplay for Wrapping +where + T: SmartDisplay, +{ + type Metadata = T::Metadata; + + fn metadata(&self, f: FormatterOptions) -> Metadata<'_, Self> { + self.0.metadata(f).reuse() + } + + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + SmartDisplay::fmt(&self.0, f) + } +} + +#[cfg(feature = "alloc")] +impl SmartDisplay for Rc +where + T: SmartDisplay + ?Sized, +{ + type Metadata = T::Metadata; + + fn metadata(&self, f: FormatterOptions) -> Metadata<'_, Self> { + (**self).metadata(f).reuse() + } + + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + SmartDisplay::fmt(&**self, f) + } +} + +#[cfg(feature = "alloc")] +impl SmartDisplay for Arc +where + T: SmartDisplay + ?Sized, +{ + type Metadata = T::Metadata; + + fn metadata(&self, f: FormatterOptions) -> Metadata<'_, Self> { + (**self).metadata(f).reuse() + } + + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + SmartDisplay::fmt(&**self, f) + } +} + +#[cfg(feature = "alloc")] +impl SmartDisplay for Box +where + T: SmartDisplay + ?Sized, +{ + type Metadata = T::Metadata; + + fn metadata(&self, f: FormatterOptions) -> Metadata<'_, Self> { + (**self).metadata(f).reuse() + } + + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + SmartDisplay::fmt(&**self, f) + } +} + +/// Implement [`SmartDisplay`] for unsigned integers. +macro_rules! impl_uint { + ($($t:ty)*) => {$( + impl SmartDisplay for $t { + type Metadata = (); + + fn metadata(&self, f: FormatterOptions) -> Metadata<'_, Self> { + let mut width = self.checked_ilog10().map_or(1, |n| n as usize + 1); + if f.sign_plus() || f.sign_minus() { + width += 1; + } + Metadata::new(width, self, ()) + } + + #[inline] + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + Display::fmt(self, f) + } + } + )*}; +} + +impl_uint![u8 u16 u32 u64 u128 usize]; + +/// Implement [`SmartDisplay`] for signed integers. +macro_rules! impl_int { + ($($t:ty)*) => {$( + impl SmartDisplay for $t { + type Metadata = (); + + fn metadata(&self, f: FormatterOptions) -> Metadata<'_, Self> { + let mut width = if f.sign_plus() || *self < 0 { 1 } else { 0 }; + width += self.unsigned_abs().checked_ilog10().map_or(1, |n| n as usize + 1); + Metadata::new(width, self, ()) + } + + #[inline] + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + Display::fmt(self, f) + } + } + )*}; +} + +impl_int![i8 i16 i32 i64 i128 isize]; + +impl SmartDisplay for char { + type Metadata = (); + + fn metadata(&self, _: FormatterOptions) -> Metadata<'_, Self> { + let mut buf = [0; 4]; + let c = self.encode_utf8(&mut buf); + + Metadata::new(c.len(), self, ()) + } + + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + Display::fmt(self, f) + } +} diff --git a/third_party/rust/time-core/.cargo-checksum.json b/third_party/rust/time-core/.cargo-checksum.json index 239ee766219a..32d6e08d1f1a 100644 --- a/third_party/rust/time-core/.cargo-checksum.json +++ b/third_party/rust/time-core/.cargo-checksum.json @@ -1 +1 @@ -{"files":{"Cargo.toml":"032c780eaf4ddfde703d5a6b260ad7bad35a5a1ee57a33cacf503f5e47dff6a9","LICENSE-Apache":"b8929fea28678da67251fb2daf9438f67503814211051861612441806d8edb05","LICENSE-MIT":"04620bf27e4a643dd47bf27652320c205acdb776c1f9f24bb8c3bfaba10804c5","src/convert.rs":"59566933f2977d62abbfe39b20be16a85df00db8627211471ccfe182dbbe684c","src/lib.rs":"18020c914b1cd561465e624ef3ea3eef980bd82bc93847e2543bce12da28b043","src/util.rs":"52c1fbf68b71c3582caf0d9a8255378c6c14a737e2df8d7e6d6603b0eb12ca06"},"package":"7300fbefb4dadc1af235a9cef3737cea692a9d97e1b9cbcd4ebdae6f8868e6fb"} \ No newline at end of file +{"files":{"Cargo.toml":"d90c41d20f37fc3dbc3d88f7715cacafb5aea973030f498e9b2833decdbe63f0","LICENSE-Apache":"b8929fea28678da67251fb2daf9438f67503814211051861612441806d8edb05","LICENSE-MIT":"04620bf27e4a643dd47bf27652320c205acdb776c1f9f24bb8c3bfaba10804c5","src/convert.rs":"354a1b05e8bb1e92eda5dcdecf33dc6cf2ce72b11115ae4cb0909dcd51d2b294","src/lib.rs":"461b752a45b0f819284e8d8e6b2f49d52b3b661026ab84ee64bf04f4daa0a2d2","src/util.rs":"52c1fbf68b71c3582caf0d9a8255378c6c14a737e2df8d7e6d6603b0eb12ca06"},"package":"ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3"} \ No newline at end of file diff --git a/third_party/rust/time-core/Cargo.toml b/third_party/rust/time-core/Cargo.toml index bbc8a325e370..3d6555ebd5ff 100644 --- a/third_party/rust/time-core/Cargo.toml +++ b/third_party/rust/time-core/Cargo.toml @@ -11,9 +11,9 @@ [package] edition = "2021" -rust-version = "1.65.0" +rust-version = "1.67.0" name = "time-core" -version = "0.1.1" +version = "0.1.2" authors = [ "Jacob Pratt ", "Time contributors", @@ -29,4 +29,7 @@ categories = ["date-and-time"] license = "MIT OR Apache-2.0" repository = "https://github.com/time-rs/time" +[package.metadata.docs.rs] +rustdoc-args = ["--generate-link-to-definition"] + [dependencies] diff --git a/third_party/rust/time-core/src/convert.rs b/third_party/rust/time-core/src/convert.rs index dae77e9afae7..9c28f0263867 100644 --- a/third_party/rust/time-core/src/convert.rs +++ b/third_party/rust/time-core/src/convert.rs @@ -1,88 +1,104 @@ -#![allow(clippy::missing_docs_in_private_items)] // TODO temporary +//! Conversion between units of time. -macro_rules! declare_structs { - ($($t:ident)*) => {$( - #[derive(Debug, Copy, Clone)] +use self::sealed::Per; + +mod sealed { + /// A trait for defining the ratio of two units of time. + /// + /// This trait is used to implement the `per` method on the various structs. + pub trait Per { + /// The smallest unsigned integer type that can represent [`VALUE`](Self::VALUE). + type Output; + + /// The number of one unit of time in the other. + const VALUE: Self::Output; + } +} + +/// Declare and implement `Per` for all relevant types. Identity implementations are automatic. +macro_rules! impl_per { + ($($t:ident ($str:literal) per {$( + $larger:ident : $output:ty = $value:expr + )*})*) => {$( + #[doc = concat!("A unit of time representing exactly one ", $str, ".")] + #[derive(Debug, Clone, Copy)] pub struct $t; impl $t { - pub const fn per(self, _: T) -> <(Self, T) as Per>::Output + #[doc = concat!("Obtain the number of times `", stringify!($t), "` can fit into `T`.")] + #[doc = concat!("If `T` is smaller than `", stringify!($t), "`, the code will fail to")] + /// compile. The return type is the smallest unsigned integer type that can represent + /// the value. + /// + /// Valid calls: + /// + #[doc = concat!(" - `", stringify!($t), "::per(", stringify!($t), ")` (returns `u8`)")] + $(#[doc = concat!(" - `", stringify!($t), "::per(", stringify!($larger), ")` (returns `", stringify!($output), "`)")])* + pub const fn per(_larger: T) -> >::Output where - (Self, T): Per, + Self: Per, T: Copy, { - <(Self, T)>::VALUE + Self::VALUE } } - )*}; -} -declare_structs! { - Nanosecond - Microsecond - Millisecond - Second - Minute - Hour - Day - Week -} + impl Per<$t> for $t { + type Output = u8; -mod sealed { - pub trait Sealed {} -} - -pub trait Per: sealed::Sealed { - type Output; - - const VALUE: Self::Output; -} - -macro_rules! impl_per { - ($($t:ty : $x:ident in $y:ident = $val:expr)*) => {$( - impl sealed::Sealed for ($x, $y) {} - - impl Per for ($x, $y) { - type Output = $t; - - const VALUE: $t = $val; + const VALUE: u8 = 1; } + + $(impl Per<$larger> for $t { + type Output = $output; + + const VALUE: $output = $value; + })* )*}; } impl_per! { - u16: Nanosecond in Microsecond = 1_000 - u32: Nanosecond in Millisecond = 1_000_000 - u32: Nanosecond in Second = 1_000_000_000 - u64: Nanosecond in Minute = 60_000_000_000 - u64: Nanosecond in Hour = 3_600_000_000_000 - u64: Nanosecond in Day = 86_400_000_000_000 - u64: Nanosecond in Week = 604_800_000_000_000 - - u16: Microsecond in Millisecond = 1_000 - u32: Microsecond in Second = 1_000_000 - u32: Microsecond in Minute = 60_000_000 - u32: Microsecond in Hour = 3_600_000_000 - u64: Microsecond in Day = 86_400_000_000 - u64: Microsecond in Week = 604_800_000_000 - - u16: Millisecond in Second = 1_000 - u16: Millisecond in Minute = 60_000 - u32: Millisecond in Hour = 3_600_000 - u32: Millisecond in Day = 86_400_000 - u32: Millisecond in Week = 604_800_000 - - u8: Second in Minute = 60 - u16: Second in Hour = 3_600 - u32: Second in Day = 86_400 - u32: Second in Week = 604_800 - - u8: Minute in Hour = 60 - u16: Minute in Day = 1_440 - u16: Minute in Week = 10_080 - - u8: Hour in Day = 24 - u8: Hour in Week = 168 - - u8: Day in Week = 7 + Nanosecond ("nanosecond") per { + Microsecond: u16 = 1_000 + Millisecond: u32 = 1_000_000 + Second: u32 = 1_000_000_000 + Minute: u64 = 60_000_000_000 + Hour: u64 = 3_600_000_000_000 + Day: u64 = 86_400_000_000_000 + Week: u64 = 604_800_000_000_000 + } + Microsecond ("microsecond") per { + Millisecond: u16 = 1_000 + Second: u32 = 1_000_000 + Minute: u32 = 60_000_000 + Hour: u32 = 3_600_000_000 + Day: u64 = 86_400_000_000 + Week: u64 = 604_800_000_000 + } + Millisecond ("millisecond") per { + Second: u16 = 1_000 + Minute: u16 = 60_000 + Hour: u32 = 3_600_000 + Day: u32 = 86_400_000 + Week: u32 = 604_800_000 + } + Second ("second") per { + Minute: u8 = 60 + Hour: u16 = 3_600 + Day: u32 = 86_400 + Week: u32 = 604_800 + } + Minute ("minute") per { + Hour: u8 = 60 + Day: u16 = 1_440 + Week: u16 = 10_080 + } + Hour ("hour") per { + Day: u8 = 24 + Week: u8 = 168 + } + Day ("day") per { + Week: u8 = 7 + } + Week ("week") per {} } diff --git a/third_party/rust/time-core/src/lib.rs b/third_party/rust/time-core/src/lib.rs index 4f1c53b12e8a..41c3547869ad 100644 --- a/third_party/rust/time-core/src/lib.rs +++ b/third_party/rust/time-core/src/lib.rs @@ -3,47 +3,6 @@ //! This crate is an implementation detail of `time` and should not be relied upon directly. #![no_std] -#![deny( - anonymous_parameters, - clippy::all, - clippy::alloc_instead_of_core, - clippy::explicit_auto_deref, - clippy::obfuscated_if_else, - clippy::std_instead_of_core, - clippy::undocumented_unsafe_blocks, - illegal_floating_point_literal_pattern, - late_bound_lifetime_arguments, - path_statements, - patterns_in_fns_without_body, - rust_2018_idioms, - trivial_casts, - trivial_numeric_casts, - unreachable_pub, - unsafe_op_in_unsafe_fn, - unused_extern_crates, - rustdoc::broken_intra_doc_links, - rustdoc::private_intra_doc_links -)] -#![warn( - clippy::dbg_macro, - clippy::decimal_literal_representation, - clippy::get_unwrap, - clippy::missing_docs_in_private_items, - clippy::nursery, - clippy::print_stdout, - clippy::todo, - clippy::unimplemented, - clippy::unnested_or_patterns, - clippy::unwrap_in_result, - clippy::unwrap_used, - clippy::use_debug, - deprecated_in_future, - missing_copy_implementations, - missing_debug_implementations, - unused_qualifications, - variant_size_differences -)] -#![allow(clippy::redundant_pub_crate)] #![doc(html_favicon_url = "https://avatars0.githubusercontent.com/u/55999857")] #![doc(html_logo_url = "https://avatars0.githubusercontent.com/u/55999857")] #![doc(test(attr(deny(warnings))))] diff --git a/third_party/rust/time-macros/.cargo-checksum.json b/third_party/rust/time-macros/.cargo-checksum.json index 6f3847898a21..ab81b00599dd 100644 --- a/third_party/rust/time-macros/.cargo-checksum.json +++ b/third_party/rust/time-macros/.cargo-checksum.json @@ -1 +1 @@ -{"files":{"Cargo.toml":"97dbc36d7e8c8658e151c1cfe57397a116135a0d0efc97aacd339142da5d1c96","LICENSE-Apache":"b8929fea28678da67251fb2daf9438f67503814211051861612441806d8edb05","LICENSE-MIT":"04620bf27e4a643dd47bf27652320c205acdb776c1f9f24bb8c3bfaba10804c5","src/date.rs":"ffcd3d0998ec67abb43a3f8eccc6104172f5061b855312b89d53bb82fece2460","src/datetime.rs":"5c7f6e07dc2f0dcfcd86216664df53bc008dbc86f346df57a9ff57f52fe43bc6","src/error.rs":"b3dea92631092068dd73e57e1cbf548f7ae85762826dcdea7fd6454bf357a50a","src/format_description/ast.rs":"8ba87e3249766b89c42b040f623d3134aeec46b78208fdfee825ed0eeeb4591a","src/format_description/format_item.rs":"03ff10699383e5ad08fe690199d45288f13363337abbc811a70b03a8b1703ab1","src/format_description/lexer.rs":"e7db7b6431f00c81b8d15a162088a1622ecd65bfb58d4e642c3c93a8dd5ae4ad","src/format_description/mod.rs":"f48c0ff590bc74529f06a98f60a6af5814bc30d1456bf0b81ac334c0b3f41bba","src/format_description/public/component.rs":"e2c2c8a189e2eb9f9354ff1d9d8edeafa34303e91dc58457df373e7e61c38b78","src/format_description/public/mod.rs":"5260592b310ea9e30808d30c92ea94c7bf1bdb171250a1342279e927d2528d73","src/format_description/public/modifier.rs":"37661e1f7cd9fd11a82f5a1ce6d5971686afa91e6feebc7b9d32df297e8b667f","src/helpers/mod.rs":"a8f8ed59a72b239d7a530357d212873f2e75ea924ec19a6d5d6e24a2baa8100c","src/helpers/string.rs":"3af2d0c701ca978c705922a272e76506dbdf0f376d44ed9ae7283086c67852ba","src/lib.rs":"200678edc14d5920abc0516717b8e010667e58da8bdc65c1cb583fdde0353089","src/offset.rs":"4b9c001a954c1f121a572f5675073f7a4e46d00cc9eb77736bfea2df94ffd05b","src/quote.rs":"634a12b95236e4ab2b8ab70a1a4a2629113c3ce3cf6defefc7ffeb81544c1d89","src/serde_format_description.rs":"db5fb2dc94e01c5114cab3484e68334516d53c4642f31dae0d66f1183253a17c","src/time.rs":"d762e8f22f749d9546d5d2a78b8a0380510be27b4cd3fed375695d7982d8396e","src/to_tokens.rs":"6636ea489c7484bad9b39f72d6956a04c95ce82d8462b12079cc03db778fd263"},"package":"96ba15a897f3c86766b757e5ac7221554c6750054d74d5b28844fce5fb36a6c4"} \ No newline at end of file +{"files":{"Cargo.toml":"3330436e81a4de8b20b9a2931f9e857b7974a8423462d928b04cff55ad531cff","LICENSE-Apache":"edd65bdd88957a205c47d53fa499eed8865a70320f0f03f6391668cb304ea376","LICENSE-MIT":"231c837c45eb53f108fb48929e488965bc4fcc14e9ea21d35f50e6b99d98685b","src/date.rs":"be197c8a2ed37e8b3123a798a91697b0e61cf9b60e7b1898a0e1b458fe8e3ef1","src/datetime.rs":"5c7f6e07dc2f0dcfcd86216664df53bc008dbc86f346df57a9ff57f52fe43bc6","src/error.rs":"b3dea92631092068dd73e57e1cbf548f7ae85762826dcdea7fd6454bf357a50a","src/format_description/ast.rs":"697d5ce506b5386092d706bfe5bf4f81f50e1130796cb17c2fc61457fb165307","src/format_description/format_item.rs":"02d12976209c7af83c2aa4a3221a1a65420fae8c8ba12a28933fb738a2872ff9","src/format_description/lexer.rs":"e2c75f3dda5773a0c8301fdfc0d58a0b833923ba59ac04bcc49fd10aee20496c","src/format_description/mod.rs":"2109b77a8198769c6a6732a54233d7e0058bf4a6da724824103d107859795956","src/format_description/public/component.rs":"5d86912e247724957f7183d70745ced20a7408ed90c24bb47da73a0e26550899","src/format_description/public/mod.rs":"8030e767cb94d559dda2dddc72d42654a756362bd165e5c2cccf112f15d49610","src/format_description/public/modifier.rs":"e1d8fdababcaee2e181a7acb3a938baf309f5a0e2d3877585cf678fcc12f212a","src/helpers/mod.rs":"af47d6c053ffd1113788c5d7591d46fa7d879dc0c5cb2c6c02f9c05462499c4f","src/helpers/string.rs":"3af2d0c701ca978c705922a272e76506dbdf0f376d44ed9ae7283086c67852ba","src/lib.rs":"6ed2d4a41d15a1b5d9fef7d437a1520d967acbfbab98a88630062340f701ca54","src/offset.rs":"aed29d0da9fc65a7dc77314e0346dfdc6fdaf663f17adf9edf00933e8f8e605f","src/quote.rs":"d3003dafa3073825f188851a974846099681cc81145070affb033469cbc7bb31","src/serde_format_description.rs":"db5fb2dc94e01c5114cab3484e68334516d53c4642f31dae0d66f1183253a17c","src/time.rs":"299ddb54e44fb88e514592db5335f06352ebdd0dbf064752790657db85f4c13c","src/to_tokens.rs":"afb067f4d95d19c1b7a650cbcf60ae155b5a9619c89825867997f39ce163ac94"},"package":"3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf"} \ No newline at end of file diff --git a/third_party/rust/time-macros/Cargo.toml b/third_party/rust/time-macros/Cargo.toml index 1c86d657d54a..b81762215453 100644 --- a/third_party/rust/time-macros/Cargo.toml +++ b/third_party/rust/time-macros/Cargo.toml @@ -11,9 +11,9 @@ [package] edition = "2021" -rust-version = "1.65.0" +rust-version = "1.67.0" name = "time-macros" -version = "0.2.10" +version = "0.2.18" authors = [ "Jacob Pratt ", "Time contributors", @@ -32,14 +32,104 @@ categories = ["date-and-time"] license = "MIT OR Apache-2.0" repository = "https://github.com/time-rs/time" +[package.metadata.docs.rs] +rustdoc-args = ["--generate-link-to-definition"] + [lib] proc-macro = true +[dependencies.num-conv] +version = "0.1.0" + [dependencies.time-core] -version = "=0.1.1" +version = "=0.1.2" [features] formatting = [] large-dates = [] parsing = [] serde = [] + +[lints.clippy] +all = "warn" +alloc-instead-of-core = "deny" +dbg-macro = "warn" +decimal-literal-representation = "warn" +explicit-auto-deref = "warn" +get-unwrap = "warn" +manual-let-else = "warn" +missing-docs-in-private-items = "warn" +missing-enforced-import-renames = "warn" +nursery = "warn" +obfuscated-if-else = "warn" +print-stdout = "warn" +semicolon-outside-block = "warn" +std-instead-of-core = "deny" +todo = "warn" +undocumented-unsafe-blocks = "deny" +unimplemented = "warn" +uninlined-format-args = "warn" +unnested-or-patterns = "warn" +unwrap-in-result = "warn" +unwrap-used = "warn" +use-debug = "warn" + +[lints.clippy.option-if-let-else] +level = "allow" +priority = 1 + +[lints.clippy.redundant-pub-crate] +level = "allow" +priority = 1 + +[lints.rust] +ambiguous-glob-reexports = "deny" +clashing-extern-declarations = "deny" +const-item-mutation = "deny" +deref-nullptr = "deny" +drop-bounds = "deny" +future-incompatible = "deny" +hidden-glob-reexports = "deny" +improper-ctypes = "deny" +improper-ctypes-definitions = "deny" +invalid-from-utf8 = "deny" +invalid-macro-export-arguments = "deny" +invalid-nan-comparisons = "deny" +invalid-reference-casting = "deny" +invalid-value = "deny" +keyword-idents = "warn" +let-underscore = "warn" +macro-use-extern-crate = "warn" +meta-variable-misuse = "warn" +missing-abi = "warn" +missing-copy-implementations = "warn" +missing-debug-implementations = "warn" +missing-docs = "warn" +named-arguments-used-positionally = "deny" +non-ascii-idents = "deny" +noop-method-call = "warn" +opaque-hidden-inferred-bound = "deny" +overlapping-range-endpoints = "deny" +single-use-lifetimes = "warn" +suspicious-double-ref-op = "deny" +temporary-cstring-as-ptr = "deny" +trivial-casts = "warn" +trivial-numeric-casts = "warn" +unconditional-recursion = "deny" +unnameable-test-items = "deny" +unreachable-pub = "warn" +unsafe-op-in-unsafe-fn = "deny" +unstable-syntax-pre-expansion = "deny" +unused = "warn" +unused-import-braces = "warn" +unused-lifetimes = "warn" +unused-qualifications = "warn" +variant-size-differences = "warn" + +[lints.rust.unstable-name-collisions] +level = "warn" +priority = 1 + +[lints.rustdoc] +private-doc-tests = "warn" +unescaped-backticks = "warn" diff --git a/third_party/rust/time-macros/LICENSE-Apache b/third_party/rust/time-macros/LICENSE-Apache index 7646f21e37ed..c763a0c9dea8 100644 --- a/third_party/rust/time-macros/LICENSE-Apache +++ b/third_party/rust/time-macros/LICENSE-Apache @@ -187,7 +187,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright 2022 Jacob Pratt et al. + Copyright 2024 Jacob Pratt et al. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/third_party/rust/time-macros/LICENSE-MIT b/third_party/rust/time-macros/LICENSE-MIT index a11a755732ce..5cc097f1c029 100644 --- a/third_party/rust/time-macros/LICENSE-MIT +++ b/third_party/rust/time-macros/LICENSE-MIT @@ -1,4 +1,4 @@ -Copyright (c) 2022 Jacob Pratt et al. +Copyright (c) 2024 Jacob Pratt et al. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/third_party/rust/time-macros/src/date.rs b/third_party/rust/time-macros/src/date.rs index 574ef8ce6fd5..03cbf11ae27f 100644 --- a/third_party/rust/time-macros/src/date.rs +++ b/third_party/rust/time-macros/src/date.rs @@ -1,5 +1,6 @@ use std::iter::Peekable; +use num_conv::Truncate; use proc_macro::{token_stream, TokenTree}; use time_core::util::{days_in_year, weeks_in_year}; @@ -93,7 +94,7 @@ pub(crate) fn parse(chars: &mut Peekable) -> Result days_in_year_month(year, month) { return Err(Error::InvalidComponent { name: "day", @@ -127,10 +128,12 @@ pub(crate) fn parse(chars: &mut Peekable) -> Result TokenTree { quote_group! {{ - const DATE: ::time::Date = ::time::Date::__from_ordinal_date_unchecked( - #(self.year), - #(self.ordinal), - ); + const DATE: ::time::Date = unsafe { + ::time::Date::__from_ordinal_date_unchecked( + #(self.year), + #(self.ordinal), + ) + }; DATE }} } diff --git a/third_party/rust/time-macros/src/format_description/ast.rs b/third_party/rust/time-macros/src/format_description/ast.rs index b75056bc2fd8..4c3a19e5a618 100644 --- a/third_party/rust/time-macros/src/format_description/ast.rs +++ b/third_party/rust/time-macros/src/format_description/ast.rs @@ -1,4 +1,3 @@ -use std::boxed::Box; use std::iter; use super::{lexer, unused, Error, Location, Spanned, SpannedValue, Unused}; diff --git a/third_party/rust/time-macros/src/format_description/format_item.rs b/third_party/rust/time-macros/src/format_description/format_item.rs index 6a8cf555ee5a..ea36caee05fb 100644 --- a/third_party/rust/time-macros/src/format_description/format_item.rs +++ b/third_party/rust/time-macros/src/format_description/format_item.rs @@ -1,4 +1,3 @@ -use std::boxed::Box; use std::num::NonZeroU16; use std::str::{self, FromStr}; @@ -103,14 +102,9 @@ impl From> for crate::format_description::public::OwnedFormatItem { impl<'a> From]>> for crate::format_description::public::OwnedFormatItem { fn from(items: Box<[Item<'a>]>) -> Self { let items = items.into_vec(); - if items.len() == 1 { - if let Ok([item]) = <[_; 1]>::try_from(items) { - item.into() - } else { - bug!("the length was just checked to be 1") - } - } else { - Self::Compound(items.into_iter().map(Self::from).collect()) + match <[_; 1]>::try_from(items) { + Ok([item]) => item.into(), + Err(vec) => Self::Compound(vec.into_iter().map(Into::into).collect()), } } } @@ -143,6 +137,7 @@ macro_rules! component_definition { _component_span: Span, ) -> Result { + #[allow(unused_mut)] let mut this = Self { $($field: None),* }; @@ -212,6 +207,7 @@ component_definition! { Day = "day" { padding = "padding": Option => padding, }, + End = "end" {}, Hour = "hour" { padding = "padding": Option => padding, base = "repr": Option => is_12_hour_clock, diff --git a/third_party/rust/time-macros/src/format_description/lexer.rs b/third_party/rust/time-macros/src/format_description/lexer.rs index 2c927cb94d53..2ea53af57abb 100644 --- a/third_party/rust/time-macros/src/format_description/lexer.rs +++ b/third_party/rust/time-macros/src/format_description/lexer.rs @@ -3,7 +3,7 @@ use core::iter; use super::{Error, Location, Spanned, SpannedValue}; pub(super) struct Lexed { - iter: core::iter::Peekable, + iter: iter::Peekable, } impl Iterator for Lexed { diff --git a/third_party/rust/time-macros/src/format_description/mod.rs b/third_party/rust/time-macros/src/format_description/mod.rs index fde1272f6ab5..676028dec481 100644 --- a/third_party/rust/time-macros/src/format_description/mod.rs +++ b/third_party/rust/time-macros/src/format_description/mod.rs @@ -1,7 +1,5 @@ //! Parser for format descriptions. -use std::vec::Vec; - macro_rules! version { ($range:expr) => { $range.contains(&VERSION) @@ -17,7 +15,7 @@ pub(crate) fn parse_with_version( version: Option, s: &[u8], proc_span: proc_macro::Span, -) -> Result, crate::Error> { +) -> Result, crate::Error> { match version { Some(crate::FormatDescriptionVersion::V1) | None => parse::<1>(s, proc_span), Some(crate::FormatDescriptionVersion::V2) => parse::<2>(s, proc_span), @@ -27,7 +25,7 @@ pub(crate) fn parse_with_version( fn parse( s: &[u8], proc_span: proc_macro::Span, -) -> Result, crate::Error> { +) -> Result, crate::Error> { let mut lexed = lexer::lex::(s, proc_span); let ast = ast::parse::<_, VERSION>(&mut lexed); let format_items = format_item::parse(ast); diff --git a/third_party/rust/time-macros/src/format_description/public/component.rs b/third_party/rust/time-macros/src/format_description/public/component.rs index 4737c6ce5c28..94c73f0fb4d5 100644 --- a/third_party/rust/time-macros/src/format_description/public/component.rs +++ b/third_party/rust/time-macros/src/format_description/public/component.rs @@ -46,4 +46,5 @@ declare_component! { OffsetSecond Ignore UnixTimestamp + End } diff --git a/third_party/rust/time-macros/src/format_description/public/mod.rs b/third_party/rust/time-macros/src/format_description/public/mod.rs index ccb0b6e2a3be..9fd8b5ece1fe 100644 --- a/third_party/rust/time-macros/src/format_description/public/mod.rs +++ b/third_party/rust/time-macros/src/format_description/public/mod.rs @@ -19,12 +19,12 @@ impl ToTokenStream for OwnedFormatItem { fn append_to(self, ts: &mut TokenStream) { match self { Self::Literal(bytes) => quote_append! { ts - ::time::format_description::FormatItem::Literal { + ::time::format_description::BorrowedFormatItem::Literal { 0: #(Literal::byte_string(bytes.as_ref())) } }, Self::Component(component) => quote_append! { ts - ::time::format_description::FormatItem::Component { 0: #S(component) } + ::time::format_description::BorrowedFormatItem::Component { 0: #S(component) } }, Self::Compound(items) => { let items = items @@ -33,11 +33,11 @@ impl ToTokenStream for OwnedFormatItem { .map(|item| quote! { #S(item), }) .collect::(); quote_append! { ts - ::time::format_description::FormatItem::Compound { 0: &[#S(items)] } + ::time::format_description::BorrowedFormatItem::Compound { 0: &[#S(items)] } } } Self::Optional(item) => quote_append! {ts - ::time::format_description::FormatItem::Optional { 0: &#S(*item) } + ::time::format_description::BorrowedFormatItem::Optional { 0: &#S(*item) } }, Self::First(items) => { let items = items @@ -46,7 +46,7 @@ impl ToTokenStream for OwnedFormatItem { .map(|item| quote! { #S(item), }) .collect::(); quote_append! { ts - ::time::format_description::FormatItem::First { 0: &[#S(items)] } + ::time::format_description::BorrowedFormatItem::First { 0: &[#S(items)] } } } } diff --git a/third_party/rust/time-macros/src/format_description/public/modifier.rs b/third_party/rust/time-macros/src/format_description/public/modifier.rs index e39c6bf5526d..63bfaa7065f2 100644 --- a/third_party/rust/time-macros/src/format_description/public/modifier.rs +++ b/third_party/rust/time-macros/src/format_description/public/modifier.rs @@ -10,18 +10,18 @@ macro_rules! to_tokens { $struct_vis:vis struct $struct_name:ident {$( $(#[$field_attr:meta])* $field_vis:vis $field_name:ident : $field_ty:ty - ),+ $(,)?} + ),* $(,)?} ) => { $(#[$struct_attr])* $struct_vis struct $struct_name {$( $(#[$field_attr])* $field_vis $field_name: $field_ty - ),+} + ),*} impl ToTokenTree for $struct_name { fn into_token_tree(self) -> TokenTree { let mut tokens = TokenStream::new(); - let Self {$($field_name),+} = self; + let Self {$($field_name),*} = self; quote_append! { tokens let mut value = ::time::format_description::modifier::$struct_name::default(); @@ -30,7 +30,7 @@ macro_rules! to_tokens { quote_append!(tokens value.$field_name =); $field_name.append_to(&mut tokens); quote_append!(tokens ;); - )+ + )* quote_append!(tokens value); proc_macro::TokenTree::Group(proc_macro::Group::new( @@ -245,3 +245,7 @@ to_tokens! { pub(crate) sign_is_mandatory: bool, } } + +to_tokens! { + pub(crate) struct End {} +} diff --git a/third_party/rust/time-macros/src/helpers/mod.rs b/third_party/rust/time-macros/src/helpers/mod.rs index 56300b3e6504..0cca2002def0 100644 --- a/third_party/rust/time-macros/src/helpers/mod.rs +++ b/third_party/rust/time-macros/src/helpers/mod.rs @@ -4,6 +4,7 @@ mod string; use std::iter::Peekable; use std::str::FromStr; +use num_conv::prelude::*; use proc_macro::{token_stream, Span, TokenTree}; use time_core::util::{days_in_year, is_leap_year}; @@ -92,15 +93,17 @@ fn jan_weekday(year: i32, ordinal: i32) -> u8 { } let adj_year = year - 1; - ((ordinal + adj_year + div_floor!(adj_year, 4) - div_floor!(adj_year, 100) + (ordinal + adj_year + div_floor!(adj_year, 4) - div_floor!(adj_year, 100) + div_floor!(adj_year, 400) + 6) - .rem_euclid(7)) as _ + .rem_euclid(7) + .cast_unsigned() + .truncate() } pub(crate) fn days_in_year_month(year: i32, month: u8) -> u8 { - [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][month as usize - 1] - + (month == 2 && is_leap_year(year)) as u8 + [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][month.extend::() - 1] + + u8::from(month == 2 && is_leap_year(year)) } pub(crate) fn ywd_to_yo(year: i32, week: u8, iso_weekday_number: u8) -> (i32, u16) { @@ -120,8 +123,9 @@ pub(crate) fn ywd_to_yo(year: i32, week: u8, iso_weekday_number: u8) -> (i32, u1 } pub(crate) fn ymd_to_yo(year: i32, month: u8, day: u8) -> (i32, u16) { - let ordinal = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334][month as usize - 1] - + (month > 2 && is_leap_year(year)) as u16; + let ordinal = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334] + [month.extend::() - 1] + + u16::from(month > 2 && is_leap_year(year)); (year, ordinal + u16::from(day)) } diff --git a/third_party/rust/time-macros/src/lib.rs b/third_party/rust/time-macros/src/lib.rs index 0e8568cdada7..65e24d7d3b03 100644 --- a/third_party/rust/time-macros/src/lib.rs +++ b/third_party/rust/time-macros/src/lib.rs @@ -1,37 +1,12 @@ -#![deny( - anonymous_parameters, - clippy::all, - illegal_floating_point_literal_pattern, - late_bound_lifetime_arguments, - path_statements, - patterns_in_fns_without_body, - rust_2018_idioms, - trivial_casts, - trivial_numeric_casts, - unreachable_pub, - unsafe_code, - unused_extern_crates -)] -#![warn( - clippy::dbg_macro, - clippy::decimal_literal_representation, - clippy::get_unwrap, - clippy::nursery, - clippy::print_stdout, - clippy::todo, - clippy::unimplemented, - clippy::unnested_or_patterns, - clippy::unwrap_used, - clippy::use_debug, - single_use_lifetimes, - unused_qualifications, - variant_size_differences -)] #![allow( - clippy::missing_const_for_fn, // useless in proc macro - clippy::redundant_pub_crate, // suggests bad style - clippy::option_if_let_else, // suggests terrible code + clippy::missing_const_for_fn, // irrelevant for proc macros + clippy::missing_docs_in_private_items, // TODO remove + clippy::std_instead_of_core, // irrelevant for proc macros + clippy::std_instead_of_alloc, // irrelevant for proc macros + clippy::alloc_instead_of_core, // irrelevant for proc macros + missing_docs, // TODO remove )] + #[allow(unused_macros)] macro_rules! bug { () => { compile_error!("provide an error message to help fix a possible bug") }; @@ -93,6 +68,7 @@ enum FormatDescriptionVersion { #[cfg(any(feature = "formatting", feature = "parsing"))] enum VersionOrModuleName { Version(FormatDescriptionVersion), + #[cfg_attr(not(feature = "serde"), allow(dead_code))] ModuleName(Ident), } @@ -175,7 +151,7 @@ pub fn format_description(input: TokenStream) -> TokenStream { let items = format_description::parse_with_version(version, &string, span)?; Ok(quote! {{ - const DESCRIPTION: &[::time::format_description::FormatItem<'_>] = &[#S( + const DESCRIPTION: &[::time::format_description::BorrowedFormatItem<'_>] = &[#S( items .into_iter() .map(|item| quote! { #S(item), }) @@ -236,7 +212,8 @@ pub fn serde_format_description(input: TokenStream) -> TokenStream { let items: TokenStream = items.into_iter().map(|item| quote! { #S(item), }).collect(); let items = quote! { - const ITEMS: &[::time::format_description::FormatItem<'_>] = &[#S(items)]; + const ITEMS: &[::time::format_description::BorrowedFormatItem<'_>] + = &[#S(items)]; ITEMS }; diff --git a/third_party/rust/time-macros/src/offset.rs b/third_party/rust/time-macros/src/offset.rs index 62d7a223da25..04dd37f1310b 100644 --- a/third_party/rust/time-macros/src/offset.rs +++ b/third_party/rust/time-macros/src/offset.rs @@ -1,5 +1,6 @@ use std::iter::Peekable; +use num_conv::prelude::*; use proc_macro::{token_stream, Span, TokenTree}; use time_core::convert::*; @@ -52,21 +53,21 @@ pub(crate) fn parse(chars: &mut Peekable) -> Result= 24 { + if hours > 25 { Err(Error::InvalidComponent { name: "hour", value: hours.to_string(), span_start: Some(hours_span), span_end: Some(hours_span), }) - } else if minutes >= Minute.per(Hour) as _ { + } else if minutes >= Minute::per(Hour).cast_signed() { Err(Error::InvalidComponent { name: "minute", value: minutes.to_string(), span_start: Some(minutes_span), span_end: Some(minutes_span), }) - } else if seconds >= Second.per(Minute) as _ { + } else if seconds >= Second::per(Minute).cast_signed() { Err(Error::InvalidComponent { name: "second", value: seconds.to_string(), @@ -85,11 +86,13 @@ pub(crate) fn parse(chars: &mut Peekable) -> Result TokenTree { quote_group! {{ - const OFFSET: ::time::UtcOffset = ::time::UtcOffset::__from_hms_unchecked( - #(self.hours), - #(self.minutes), - #(self.seconds), - ); + const OFFSET: ::time::UtcOffset = unsafe { + ::time::UtcOffset::__from_hms_unchecked( + #(self.hours), + #(self.minutes), + #(self.seconds), + ) + }; OFFSET }} } diff --git a/third_party/rust/time-macros/src/quote.rs b/third_party/rust/time-macros/src/quote.rs index 4d3dcbca030b..8603f4fa466b 100644 --- a/third_party/rust/time-macros/src/quote.rs +++ b/third_party/rust/time-macros/src/quote.rs @@ -45,20 +45,19 @@ macro_rules! sym { }; } +#[allow(unused_macro_rules)] // Varies by feature flag combination. macro_rules! quote_inner { // Base case ($ts:ident) => {}; // Single or double symbols ($ts:ident :: $($tail:tt)*) => { sym!($ts ':' ':'); quote_inner!($ts $($tail)*); }; - ($ts:ident .. $($tail:tt)*) => { sym!($ts '.' '.'); quote_inner!($ts $($tail)*); }; ($ts:ident : $($tail:tt)*) => { sym!($ts ':'); quote_inner!($ts $($tail)*); }; ($ts:ident = $($tail:tt)*) => { sym!($ts '='); quote_inner!($ts $($tail)*); }; ($ts:ident ; $($tail:tt)*) => { sym!($ts ';'); quote_inner!($ts $($tail)*); }; ($ts:ident , $($tail:tt)*) => { sym!($ts ','); quote_inner!($ts $($tail)*); }; ($ts:ident . $($tail:tt)*) => { sym!($ts '.'); quote_inner!($ts $($tail)*); }; ($ts:ident & $($tail:tt)*) => { sym!($ts '&'); quote_inner!($ts $($tail)*); }; - ($ts:ident << $($tail:tt)*) => { sym!($ts '<' '<'); quote_inner!($ts $($tail)*); }; ($ts:ident < $($tail:tt)*) => { sym!($ts '<'); quote_inner!($ts $($tail)*); }; ($ts:ident >> $($tail:tt)*) => { sym!($ts '>' '>'); quote_inner!($ts $($tail)*); }; ($ts:ident > $($tail:tt)*) => { sym!($ts '>'); quote_inner!($ts $($tail)*); }; diff --git a/third_party/rust/time-macros/src/time.rs b/third_party/rust/time-macros/src/time.rs index 96314de1f457..4e565c1ded13 100644 --- a/third_party/rust/time-macros/src/time.rs +++ b/third_party/rust/time-macros/src/time.rs @@ -73,21 +73,21 @@ pub(crate) fn parse(chars: &mut Peekable) -> Result