diff --git a/patches/backport-rust-crates.bootstrap b/patches/backport-rust-crates.bootstrap index c25fd82..f1003ae 100644 --- a/patches/backport-rust-crates.bootstrap +++ b/patches/backport-rust-crates.bootstrap @@ -1,11 +1,15 @@ -# Rust crate backport by daijro -# Fixes https://bugzilla.mozilla.org/show_bug.cgi?id=1900504 -# Requirement: ./mach vendor rust diff --git a/Cargo.lock b/Cargo.lock -index 7242103ece..d52ae0cacd 100644 +index 7242103ece..1406b76c63 100644 --- a/Cargo.lock +++ b/Cargo.lock -@@ -823,7 +823,7 @@ version = "0.16.2" +@@ -816,21 +816,21 @@ checksum = "74428ae4f7f05f32f4448e9f42d371538196919c4834979f4f96d1fdebffcb47" + dependencies = [ + "winapi", + ] + + [[package]] + name = "cookie" + version = "0.16.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e859cd57d0710d9e06c381b550c06e76992472a8c6d527aecd2fc673dcc231fb" dependencies = [ @@ -14,7 +18,21 @@ index 7242103ece..d52ae0cacd 100644 "version_check", ] -@@ -1264,6 +1264,16 @@ dependencies = [ + [[package]] + name = "core-foundation" + version = "0.9.3" + source = "registry+https://github.com/rust-lang/crates.io-index" + checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" + dependencies = [ + "core-foundation-sys", +@@ -1257,20 +1257,29 @@ dependencies = [ + + [[package]] + name = "debugid" + version = "0.8.0" + source = "registry+https://github.com/rust-lang/crates.io-index" + checksum = "bef552e6f588e446098f6ba40d89ac146c8c7b64aade83c051ee00bb5d2bc18d" + dependencies = [ "uuid", ] @@ -25,13 +43,26 @@ index 7242103ece..d52ae0cacd 100644 +checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +dependencies = [ + "powerfmt", -+ "serde", +] + [[package]] name = "derive_arbitrary" version = "1.3.1" -@@ -3757,7 +3767,7 @@ dependencies = [ + source = "registry+https://github.com/rust-lang/crates.io-index" + checksum = "53e0efad4403bfc52dc201159c4b842a246a14b98c64b55dfd0f2d89729dfeb8" + dependencies = [ + "proc-macro2", + "quote", + "syn", + ] +@@ -3750,21 +3759,21 @@ dependencies = [ + "once_cell", + "proc-macro2", + "quote", + "regex", + "scopeguard", + "semver", + "serde", "serde_json", "smallvec", "syn", @@ -40,7 +71,21 @@ index 7242103ece..d52ae0cacd 100644 "tokio", "tokio-util", "tracing", -@@ -3887,7 +3897,7 @@ dependencies = [ + "url", + "uuid", + "winapi", + "windows-sys 0.52.0", + ] + + [[package]] +@@ -3880,21 +3889,21 @@ dependencies = [ + + [[package]] + name = "neqo-common" + version = "0.7.2" + source = "git+https://github.com/mozilla/neqo?tag=v0.7.2#ce5cbe4dfc2e38b238abb022c39eee4215058221" + dependencies = [ + "enum-map", "env_logger", "log", "qlog", @@ -49,7 +94,21 @@ index 7242103ece..d52ae0cacd 100644 "winapi", ] -@@ -4077,6 +4087,12 @@ dependencies = [ + [[package]] + name = "neqo-crypto" + version = "0.7.2" + source = "git+https://github.com/mozilla/neqo?tag=v0.7.2#ce5cbe4dfc2e38b238abb022c39eee4215058221" + dependencies = [ + "bindgen 0.69.4", + "log", +@@ -4070,20 +4079,26 @@ dependencies = [ + "encoding_rs", + ] + + [[package]] + name = "nsstring-gtest" + version = "0.1.0" + dependencies = [ "nsstring", ] @@ -62,7 +121,21 @@ index 7242103ece..d52ae0cacd 100644 [[package]] name = "num-derive" version = "0.4.0" -@@ -4450,10 +4466,16 @@ dependencies = [ + source = "registry+https://github.com/rust-lang/crates.io-index" + checksum = "9e6a0fd4f737c707bd9086cc16c925f294943eb62eb71499e9fd4cf71f8b9f4e" + dependencies = [ + "proc-macro2", + "quote", + "syn", + ] +@@ -4443,40 +4458,47 @@ dependencies = [ + [[package]] + name = "plist" + version = "1.3.1" + source = "registry+https://github.com/rust-lang/crates.io-index" + checksum = "bd39bc6cdc9355ad1dc5eeedefee696bb35c34caf21768741e81826c0bbd7225" + dependencies = [ + "base64 0.13.999", "indexmap 1.9.3", "line-wrap", "serde", @@ -71,6 +144,23 @@ index 7242103ece..d52ae0cacd 100644 "xml-rs", ] ++ + [[package]] + name = "ppv-lite86" + version = "0.2.17" + source = "registry+https://github.com/rust-lang/crates.io-index" + checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + + [[package]] + name = "precomputed-hash" + version = "0.1.1" + source = "registry+https://github.com/rust-lang/crates.io-index" + checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" + + [[package]] + name = "prefs_parser" + version = "0.0.1" + +[[package]] +name = "powerfmt" +version = "0.2.0" @@ -78,9 +168,23 @@ index 7242103ece..d52ae0cacd 100644 +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + [[package]] - name = "ppv-lite86" - version = "0.2.17" -@@ -5629,11 +5651,14 @@ dependencies = [ + name = "presser" + version = "0.3.1" + source = "registry+https://github.com/rust-lang/crates.io-index" + checksum = "e8cf8e6a8aa66ce33f63993ffc4ea4271eb5b0530a9002db8455ea6050c77bfa" + + [[package]] + name = "prio" + version = "0.15.3" + source = "registry+https://github.com/rust-lang/crates.io-index" +@@ -5622,42 +5644,46 @@ version = "0.1.45" + source = "registry+https://github.com/rust-lang/crates.io-index" + checksum = "1b797afad3f312d1c66a56d11d0316f916356d11bd158fbc6ca6389ff6bf805a" + dependencies = [ + "libc", + "wasi 0.10.0+wasi-snapshot-preview999", + "winapi", + ] [[package]] name = "time" @@ -97,7 +201,7 @@ index 7242103ece..d52ae0cacd 100644 "serde", "time-core", "time-macros", -@@ -5641,16 +5666,17 @@ dependencies = [ + ] [[package]] name = "time-core" @@ -119,7 +223,21 @@ index 7242103ece..d52ae0cacd 100644 "time-core", ] -@@ -6327,7 +6353,7 @@ dependencies = [ + [[package]] + name = "tinystr" + version = "0.7.4" + source = "registry+https://github.com/rust-lang/crates.io-index" + checksum = "d5d0e245e80bdc9b4e5356fc45a72184abbc3861992603f515270e9340f5a219" + dependencies = [ + "displaydoc", +@@ -6320,21 +6346,21 @@ dependencies = [ + "base64 0.21.3", + "bytes", + "cookie", + "http", + "icu_segmenter", + "log", + "serde", "serde_derive", "serde_json", "thiserror", @@ -128,11 +246,25 @@ index 7242103ece..d52ae0cacd 100644 "tokio", "tokio-stream", "url", + "warp", + ] + + [[package]] + name = "webext-storage" + version = "0.1.0" + source = "git+https://github.com/mozilla/application-services?rev=5fc8ee2f0f6950e36d4096983757bd046d55df9f#5fc8ee2f0f6950e36d4096983757bd046d55df9f" diff --git a/supply-chain/audits.toml b/supply-chain/audits.toml -index 31ca3fcf0f..03c38e4fe0 100644 +index 31ca3fcf0f..6aadf9aef9 100644 --- a/supply-chain/audits.toml +++ b/supply-chain/audits.toml -@@ -1388,6 +1388,16 @@ criteria = "safe-to-deploy" +@@ -1381,20 +1381,30 @@ delta = "0.14.3 -> 0.20.1" + who = "Mike Hommey " + criteria = "safe-to-deploy" + delta = "2.3.2 -> 2.3.3" + + [[audits.debugid]] + who = "Gabriele Svelto " + 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." @@ -149,7 +281,21 @@ index 31ca3fcf0f..03c38e4fe0 100644 [[audits.derive_arbitrary]] who = "Mike Hommey " criteria = "safe-to-run" -@@ -2746,6 +2756,24 @@ criteria = "safe-to-deploy" + delta = "1.1.0 -> 1.1.1" + + [[audits.derive_arbitrary]] + who = "Mike Hommey " + criteria = "safe-to-run" + delta = "1.1.1 -> 1.1.3" + +@@ -2739,20 +2749,29 @@ who = "Josh Stone " + criteria = "safe-to-deploy" + version = "0.4.3" + notes = "All code written or reviewed by Josh Stone." + + [[audits.num-complex]] + who = "Josh Stone " + criteria = "safe-to-deploy" version = "0.4.2" notes = "All code written or reviewed by Josh Stone." @@ -162,6 +308,27 @@ index 31ca3fcf0f..03c38e4fe0 100644 +side-effectful std functions, etc. +""" + + [[audits.num-derive]] + who = "Josh Stone " + criteria = "safe-to-deploy" + version = "0.3.3" + notes = "All code written or reviewed by Josh Stone." + + [[audits.num-derive]] + who = "Mike Hommey " + criteria = "safe-to-deploy" + delta = "0.3.3 -> 0.4.0" +@@ -3008,20 +3027,29 @@ delta = "0.1.4 -> 0.1.5" + who = "Mike Hommey " + criteria = "safe-to-deploy" + delta = "0.3.25 -> 0.3.26" + + [[audits.plane-split]] + who = "Nicolas Silva " + 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" @@ -171,10 +338,24 @@ index 31ca3fcf0f..03c38e4fe0 100644 +yet, but it's all valid. Otherwise it's a pretty simple crate. +""" + - [[audits.num-derive]] - who = "Josh Stone " + [[audits.ppv-lite86]] + who = "Mike Hommey " criteria = "safe-to-deploy" -@@ -3804,6 +3832,16 @@ who = "Kershaw Chang " + delta = "0.2.16 -> 0.2.17" + + [[audits.precomputed-hash]] + who = "Bobby Holley " + criteria = "safe-to-deploy" + version = "0.1.1" + notes = "This is a trivial crate." +@@ -3797,50 +3825,70 @@ delta = "0.1.45 -> 0.3.17" + [[audits.time]] + who = "Mike Hommey " + criteria = "safe-to-run" + delta = "0.3.9 -> 0.3.17" + + [[audits.time]] + who = "Kershaw Chang " criteria = "safe-to-deploy" delta = "0.3.17 -> 0.3.23" @@ -191,7 +372,15 @@ index 31ca3fcf0f..03c38e4fe0 100644 [[audits.time-core]] who = "Kershaw Chang " criteria = "safe-to-deploy" -@@ -3819,6 +3857,11 @@ who = "Kershaw Chang " + version = "0.1.0" + + [[audits.time-core]] + who = "Mike Hommey " + criteria = "safe-to-run" + version = "0.1.0" + + [[audits.time-core]] + who = "Kershaw Chang " criteria = "safe-to-deploy" delta = "0.1.0 -> 0.1.1" @@ -203,7 +392,15 @@ index 31ca3fcf0f..03c38e4fe0 100644 [[audits.time-macros]] who = "Kershaw Chang " criteria = "safe-to-deploy" -@@ -3834,6 +3877,11 @@ who = "Kershaw Chang " + version = "0.2.6" + + [[audits.time-macros]] + who = "Mike Hommey " + criteria = "safe-to-run" + delta = "0.2.4 -> 0.2.6" + + [[audits.time-macros]] + who = "Kershaw Chang " criteria = "safe-to-deploy" delta = "0.2.6 -> 0.2.10" @@ -215,6 +412,4970 @@ index 31ca3fcf0f..03c38e4fe0 100644 [[audits.tinystr]] who = "Zibi Braniecki " criteria = "safe-to-deploy" + version = "0.3.4" + + [[audits.tinystr]] + who = "Zibi Braniecki " + criteria = "safe-to-deploy" + version = "0.6.0" + +diff --git a/third_party/rust/deranged/.cargo-checksum.json b/third_party/rust/deranged/.cargo-checksum.json +new file mode 100644 +index 0000000000..f29abcd86f +--- /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 0000000000..ff660b538c +--- /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 0000000000..7646f21e37 +--- /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 0000000000..a11a755732 +--- /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 0000000000..eddc4b99af +--- /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 0000000000..aee3ea99f6 +--- /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 0000000000..e9a6ff7827 +--- /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 0000000000..d1b69ac01f +--- /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 0000000000..8620e12176 +--- /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 0000000000..3886e69781 +--- /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 0000000000..0b8a9a6c01 +--- /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 0000000000..d8bca8b9f1 +--- /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 0000000000..7c7f78a5ed +--- /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 0000000000..cbf3ff21d6 +--- /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 0000000000..15fec13022 +--- /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 0000000000..f7acec3944 +--- /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 0000000000..ddde1f9a0f +--- /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 0000000000..89c1f78cb4 +--- /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 0000000000..c22a3e2fe0 +--- /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 0000000000..5a57a60a35 +--- /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 0000000000..20af7c0aab +--- /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 0000000000..0cd6f7cbb3 +--- /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 0000000000..bb55554b87 +--- /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 0000000000..dc82395f29 +--- /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 239ee76621..32d6e08d1f 100644 --- a/third_party/rust/time-core/.cargo-checksum.json @@ -228,7 +5389,14 @@ diff --git a/third_party/rust/time-core/Cargo.toml b/third_party/rust/time-core/ index bbc8a325e3..3d6555ebd5 100644 --- a/third_party/rust/time-core/Cargo.toml +++ b/third_party/rust/time-core/Cargo.toml -@@ -11,9 +11,9 @@ +@@ -4,29 +4,32 @@ + # "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" @@ -240,7 +5408,15 @@ index bbc8a325e3..3d6555ebd5 100644 authors = [ "Jacob Pratt ", "Time contributors", -@@ -29,4 +29,7 @@ categories = ["date-and-time"] + ] + description = "This crate is an implementation detail and should not be relied upon directly." + keywords = [ + "date", + "time", + "calendar", + "duration", + ] + categories = ["date-and-time"] license = "MIT OR Apache-2.0" repository = "https://github.com/time-rs/time" @@ -431,7 +5607,9 @@ diff --git a/third_party/rust/time-core/src/lib.rs b/third_party/rust/time-core/ index 4f1c53b12e..41c3547869 100644 --- a/third_party/rust/time-core/src/lib.rs +++ b/third_party/rust/time-core/src/lib.rs -@@ -3,47 +3,6 @@ +@@ -1,52 +1,11 @@ + //! Core items for `time`. + //! //! This crate is an implementation detail of `time` and should not be relied upon directly. #![no_std] @@ -479,6 +5657,9 @@ index 4f1c53b12e..41c3547869 100644 #![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))))] + + pub mod convert; + pub mod util; diff --git a/third_party/rust/time-macros/.cargo-checksum.json b/third_party/rust/time-macros/.cargo-checksum.json index 6f3847898a..ab81b00599 100644 --- a/third_party/rust/time-macros/.cargo-checksum.json @@ -492,7 +5673,14 @@ diff --git a/third_party/rust/time-macros/Cargo.toml b/third_party/rust/time-mac index 1c86d657d5..b817622154 100644 --- a/third_party/rust/time-macros/Cargo.toml +++ b/third_party/rust/time-macros/Cargo.toml -@@ -11,9 +11,9 @@ +@@ -4,42 +4,132 @@ + # "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" @@ -504,7 +5692,18 @@ index 1c86d657d5..b817622154 100644 authors = [ "Jacob Pratt ", "Time contributors", -@@ -32,14 +32,104 @@ categories = ["date-and-time"] + ] + description = """ + Procedural macros for the time crate. + This crate is an implementation detail and should not be relied upon directly. + """ + keywords = [ + "date", + "time", + "calendar", + "duration", + ] + categories = ["date-and-time"] license = "MIT OR Apache-2.0" repository = "https://github.com/time-rs/time" @@ -614,7 +5813,14 @@ diff --git a/third_party/rust/time-macros/LICENSE-Apache b/third_party/rust/time index 7646f21e37..c763a0c9de 100644 --- a/third_party/rust/time-macros/LICENSE-Apache +++ b/third_party/rust/time-macros/LICENSE-Apache -@@ -187,7 +187,7 @@ +@@ -180,21 +180,21 @@ + + 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. @@ -623,28 +5829,56 @@ index 7646f21e37..c763a0c9de 100644 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. diff --git a/third_party/rust/time-macros/LICENSE-MIT b/third_party/rust/time-macros/LICENSE-MIT index a11a755732..5cc097f1c0 100644 --- a/third_party/rust/time-macros/LICENSE-MIT +++ b/third_party/rust/time-macros/LICENSE-MIT -@@ -1,4 +1,4 @@ +@@ -1,11 +1,11 @@ -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 + 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. diff --git a/third_party/rust/time-macros/src/date.rs b/third_party/rust/time-macros/src/date.rs index 574ef8ce6f..03cbf11ae2 100644 --- a/third_party/rust/time-macros/src/date.rs +++ b/third_party/rust/time-macros/src/date.rs -@@ -1,5 +1,6 @@ +@@ -1,12 +1,13 @@ 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) -> Result("day", chars)?; + + if month == 0 || month > 12 { + return Err(Error::InvalidComponent { + name: "month", + value: month.to_string(), + span_start: Some(month_span), span_end: Some(month_span), }); } @@ -653,7 +5887,21 @@ index 574ef8ce6f..03cbf11ae2 100644 if day == 0 || day > days_in_year_month(year, month) { return Err(Error::InvalidComponent { name: "day", -@@ -127,10 +128,12 @@ pub(crate) fn parse(chars: &mut Peekable) -> Result) -> Result TokenTree { quote_group! {{ @@ -670,25 +5918,47 @@ index 574ef8ce6f..03cbf11ae2 100644 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 b75056bc2f..4c3a19e5a6 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 @@ +@@ -1,11 +1,10 @@ -use std::boxed::Box; use std::iter; use super::{lexer, unused, Error, Location, Spanned, SpannedValue, Unused}; + + pub(super) enum Item<'a> { + Literal(Spanned<&'a [u8]>), + EscapedBracket { + _first: Unused, + _second: 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 6a8cf555ee..ea36caee05 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 @@ +@@ -1,11 +1,10 @@ -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 { + use super::{ast, unused, Error, Span, Spanned, Unused}; + + pub(super) fn parse<'a>( + ast_items: impl Iterator, Error>>, + ) -> impl Iterator, Error>> { + ast_items.map(|ast_item| ast_item.and_then(Item::from_ast)) + } +@@ -96,28 +95,23 @@ impl From> for crate::format_description::public::OwnedFormatItem { + Item::First { value, _span: _ } => { + Self::First(value.into_vec().into_iter().map(Into::into).collect()) + } + } + } + } + impl<'a> From]>> for crate::format_description::public::OwnedFormatItem { fn from(items: Box<[Item<'a>]>) -> Self { let items = items.into_vec(); @@ -706,7 +5976,21 @@ index 6a8cf555ee..ea36caee05 100644 } } } -@@ -143,6 +137,7 @@ macro_rules! component_definition { + + macro_rules! component_definition { + (@if_required required then { $($then:tt)* } $(else { $($else:tt)* })?) => { $($then)* }; + (@if_required then { $($then:tt)* } $(else { $($else:tt)* })?) => { $($($else)*)? }; + (@if_from_str from_str then { $($then:tt)* } $(else { $($else:tt)* })?) => { $($then)* }; + (@if_from_str then { $($then:tt)* } $(else { $($else:tt)* })?) => { $($($else)*)? }; + +@@ -136,20 +130,21 @@ macro_rules! component_definition { + $($vis struct $variant { + $($field: Option<$field_type>),* + })* + + $(impl $variant { + fn with_modifiers( + modifiers: &[ast::Modifier<'_>], _component_span: Span, ) -> Result { @@ -714,7 +5998,21 @@ index 6a8cf555ee..ea36caee05 100644 let mut this = Self { $($field: None),* }; -@@ -212,6 +207,7 @@ component_definition! { + + for modifier in modifiers { + $(#[allow(clippy::string_lit_as_bytes)] + if modifier.key.eq_ignore_ascii_case($parse_field.as_bytes()) { + this.$field = component_definition!(@if_from_str $($from_str)? + then { + parse_from_modifier_value::<$field_type>(&modifier.value)? +@@ -205,20 +200,21 @@ macro_rules! component_definition { + Err(name.span.error("invalid component")) + } + } + } + + component_definition! { + pub(super) enum Component { Day = "day" { padding = "padding": Option => padding, }, @@ -722,11 +6020,20 @@ index 6a8cf555ee..ea36caee05 100644 Hour = "hour" { padding = "padding": Option => padding, base = "repr": Option => is_12_hour_clock, + }, + Ignore = "ignore" { + #[required] + count = "count": Option<#[from_str] NonZeroU16> => count, + }, + Minute = "minute" { + padding = "padding": Option => padding, 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 2c927cb94d..2ea53af57a 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; +@@ -1,16 +1,16 @@ + use core::iter; + use super::{Error, Location, Spanned, SpannedValue}; pub(super) struct Lexed { @@ -735,11 +6042,18 @@ index 2c927cb94d..2ea53af57a 100644 } impl Iterator for Lexed { + type Item = I::Item; + + fn next(&mut self) -> Option { + self.iter.next() + } + } + 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 fde1272f6a..676028dec4 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 @@ +@@ -1,40 +1,38 @@ //! Parser for format descriptions. -use std::vec::Vec; @@ -747,7 +6061,15 @@ index fde1272f6a..676028dec4 100644 macro_rules! version { ($range:expr) => { $range.contains(&VERSION) -@@ -17,7 +15,7 @@ pub(crate) fn parse_with_version( + }; + } + + mod ast; + mod format_item; + mod lexer; + mod public; + + pub(crate) fn parse_with_version( version: Option, s: &[u8], proc_span: proc_macro::Span, @@ -756,7 +6078,9 @@ index fde1272f6a..676028dec4 100644 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, @@ -765,11 +6089,25 @@ index fde1272f6a..676028dec4 100644 let mut lexed = lexer::lex::(s, proc_span); let ast = ast::parse::<_, VERSION>(&mut lexed); let format_items = format_item::parse(ast); + Ok(format_items + .map(|res| res.map(Into::into)) + .collect::>()?) + } + + #[derive(Clone, Copy)] + struct Location { 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 4737c6ce5c..94c73f0fb4 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! { +@@ -39,11 +39,12 @@ declare_component! { + Hour + Minute + Period + Second + Subsecond + OffsetHour + OffsetMinute OffsetSecond Ignore UnixTimestamp @@ -779,7 +6117,14 @@ diff --git a/third_party/rust/time-macros/src/format_description/public/mod.rs b index ccb0b6e2a3..9fd8b5ece1 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 { +@@ -12,43 +12,43 @@ pub(crate) enum OwnedFormatItem { + Component(Component), + Compound(Box<[Self]>), + Optional(Box), + First(Box<[Self]>), + } + + impl ToTokenStream for OwnedFormatItem { fn append_to(self, ts: &mut TokenStream) { match self { Self::Literal(bytes) => quote_append! { ts @@ -794,7 +6139,8 @@ index ccb0b6e2a3..9fd8b5ece1 100644 }, Self::Compound(items) => { let items = items -@@ -33,11 +33,11 @@ impl ToTokenStream for OwnedFormatItem { + .into_vec() + .into_iter() .map(|item| quote! { #S(item), }) .collect::(); quote_append! { ts @@ -808,7 +6154,8 @@ index ccb0b6e2a3..9fd8b5ece1 100644 }, Self::First(items) => { let items = items -@@ -46,7 +46,7 @@ impl ToTokenStream for OwnedFormatItem { + .into_vec() + .into_iter() .map(|item| quote! { #S(item), }) .collect::(); quote_append! { ts @@ -817,11 +6164,20 @@ index ccb0b6e2a3..9fd8b5ece1 100644 } } } + } + } 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 e39c6bf552..63bfaa7065 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 { +@@ -3,41 +3,41 @@ use std::num::NonZeroU16; + use proc_macro::{Ident, Span, TokenStream, TokenTree}; + + use crate::to_tokens::{ToTokenStream, ToTokenTree}; + + macro_rules! to_tokens { + ( + $(#[$struct_attr:meta])* $struct_vis:vis struct $struct_name:ident {$( $(#[$field_attr:meta])* $field_vis:vis $field_name:ident : $field_ty:ty @@ -843,7 +6199,8 @@ index e39c6bf552..63bfaa7065 100644 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 ;); @@ -852,7 +6209,21 @@ index e39c6bf552..63bfaa7065 100644 quote_append!(tokens value); proc_macro::TokenTree::Group(proc_macro::Group::new( -@@ -245,3 +245,7 @@ to_tokens! { + proc_macro::Delimiter::Brace, + tokens, + )) + } + } + }; + +@@ -238,10 +238,14 @@ to_tokens! { + Nanosecond, + } + } + + to_tokens! { + pub(crate) struct UnixTimestamp { + pub(crate) precision: UnixTimestampPrecision, pub(crate) sign_is_mandatory: bool, } } @@ -864,7 +6235,10 @@ diff --git a/third_party/rust/time-macros/src/helpers/mod.rs b/third_party/rust/ index 56300b3e65..0cca2002de 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; +@@ -1,16 +1,17 @@ + #[cfg(any(feature = "formatting", feature = "parsing"))] + mod string; + use std::iter::Peekable; use std::str::FromStr; @@ -872,7 +6246,21 @@ index 56300b3e65..0cca2002de 100644 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 { + use crate::Error; + + #[cfg(any(feature = "formatting", feature = "parsing"))] + pub(crate) fn get_string_literal( + mut tokens: impl Iterator, + ) -> Result<(Span, Vec), Error> { + match (tokens.next(), tokens.next()) { +@@ -85,43 +86,46 @@ fn jan_weekday(year: i32, ordinal: i32) -> u8 { + let (_quotient, _remainder) = ($a / $b, $a % $b); + if (_remainder > 0 && $b < 0) || (_remainder < 0 && $b > 0) { + _quotient - 1 + } else { + _quotient + } + }}; } let adj_year = year - 1; @@ -894,7 +6282,19 @@ index 56300b3e65..0cca2002de 100644 } 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 + let (ordinal, overflow) = (u16::from(week) * 7 + u16::from(iso_weekday_number)) + .overflowing_sub(u16::from(jan_weekday(year, 4)) + 4); + + if overflow || ordinal == 0 { + return (year - 1, (ordinal.wrapping_add(days_in_year(year - 1)))); + } + + let days_in_cur_year = days_in_year(year); + if ordinal > days_in_cur_year { + (year + 1, ordinal - days_in_cur_year) + } else { + (year, ordinal) + } } pub(crate) fn ymd_to_yo(year: i32, month: u8, day: u8) -> (i32, u16) { @@ -910,7 +6310,7 @@ diff --git a/third_party/rust/time-macros/src/lib.rs b/third_party/rust/time-mac index 0e8568cdad..65e24d7d3b 100644 --- a/third_party/rust/time-macros/src/lib.rs +++ b/third_party/rust/time-macros/src/lib.rs -@@ -1,37 +1,12 @@ +@@ -1,44 +1,19 @@ -#![deny( - anonymous_parameters, - clippy::all, @@ -955,7 +6355,21 @@ index 0e8568cdad..65e24d7d3b 100644 #[allow(unused_macros)] macro_rules! bug { () => { compile_error!("provide an error message to help fix a possible bug") }; -@@ -93,6 +68,7 @@ enum FormatDescriptionVersion { + ($descr:literal $($rest:tt)?) => { + unreachable!(concat!("internal error: ", $descr) $($rest)?) + } + } + + #[macro_use] + mod quote; +@@ -86,20 +61,21 @@ impl_macros![date datetime offset time]; + + #[cfg(any(feature = "formatting", feature = "parsing"))] + enum FormatDescriptionVersion { + V1, + V2, + } + #[cfg(any(feature = "formatting", feature = "parsing"))] enum VersionOrModuleName { Version(FormatDescriptionVersion), @@ -963,7 +6377,21 @@ index 0e8568cdad..65e24d7d3b 100644 ModuleName(Ident), } -@@ -175,7 +151,7 @@ pub fn format_description(input: TokenStream) -> TokenStream { + #[cfg(any(feature = "formatting", feature = "parsing"))] + fn parse_format_description_version( + iter: &mut Peekable, + ) -> Result, Error> { + let version_ident = match iter.peek() { + Some(TokenTree::Ident(ident)) if ident.to_string() == "version" => match iter.next() { + Some(TokenTree::Ident(ident)) => ident, +@@ -168,21 +144,21 @@ pub fn format_description(input: TokenStream) -> TokenStream { + let version = match parse_format_description_version::(&mut input)? { + Some(VersionOrModuleName::Version(version)) => Some(version), + None => None, + // This branch should never occur here, as `false` is the provided as a const parameter. + Some(VersionOrModuleName::ModuleName(_)) => bug!("branch should never occur"), + }; + let (span, string) = helpers::get_string_literal(input)?; let items = format_description::parse_with_version(version, &string, span)?; Ok(quote! {{ @@ -972,7 +6400,21 @@ index 0e8568cdad..65e24d7d3b 100644 items .into_iter() .map(|item| quote! { #S(item), }) -@@ -236,7 +212,8 @@ pub fn serde_format_description(input: TokenStream) -> TokenStream { + .collect::() + )]; + DESCRIPTION + }}) + })() + .unwrap_or_else(|err: Error| err.to_compile_error()) + } +@@ -229,21 +205,22 @@ pub fn serde_format_description(input: TokenStream) -> TokenStream { + // they can provide a path to a format description. If the latter, all remaining tokens are + // assumed to be part of the path. + let (format, format_description_display) = match tokens.peek() { + // string literal + Some(TokenTree::Literal(_)) => { + let (span, format_string) = helpers::get_string_literal(tokens)?; + let items = format_description::parse_with_version(version, &format_string, span)?; let items: TokenStream = items.into_iter().map(|item| quote! { #S(item), }).collect(); let items = quote! { @@ -982,18 +6424,39 @@ index 0e8568cdad..65e24d7d3b 100644 ITEMS }; + (items, String::from_utf8_lossy(&format_string).into_owned()) + } + // path + Some(_) => { + let tokens = tokens.collect::(); + let tokens_string = tokens.to_string(); + ( diff --git a/third_party/rust/time-macros/src/offset.rs b/third_party/rust/time-macros/src/offset.rs index 62d7a223da..04dd37f131 100644 --- a/third_party/rust/time-macros/src/offset.rs +++ b/third_party/rust/time-macros/src/offset.rs -@@ -1,5 +1,6 @@ +@@ -1,12 +1,13 @@ 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) -> Result("second", chars)?; + seconds_span = sec.0; + seconds = sec.1; } } @@ -1018,7 +6481,18 @@ index 62d7a223da..04dd37f131 100644 Err(Error::InvalidComponent { name: "second", value: seconds.to_string(), -@@ -85,11 +86,13 @@ pub(crate) fn parse(chars: &mut Peekable) -> Result TokenTree { quote_group! {{ @@ -1037,11 +6511,19 @@ index 62d7a223da..04dd37f131 100644 OFFSET }} } + } diff --git a/third_party/rust/time-macros/src/quote.rs b/third_party/rust/time-macros/src/quote.rs index 4d3dcbca03..8603f4fa46 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 { +@@ -38,34 +38,33 @@ macro_rules! sym { + ]); + }; + ($ts:ident $x:tt) => { + $ts.extend([::proc_macro::TokenTree::from(::proc_macro::Punct::new( + $x, + ::proc_macro::Spacing::Alone, + ))]); }; } @@ -1063,11 +6545,25 @@ index 4d3dcbca03..8603f4fa46 100644 ($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 96314de1f4..4e565c1ded 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