omegafox/patches/backport-rust-crates.bootstrap
2024-07-30 21:07:41 -05:00

21484 lines
808 KiB
Text

diff --git a/Cargo.lock b/Cargo.lock
index 7242103ece..1406b76c63 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -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 = [
- "time 0.3.23",
+ "time 0.3.36",
"version_check",
]
[[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",
]
+[[package]]
+name = "deranged"
+version = "0.3.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4"
+dependencies = [
+ "powerfmt",
+]
+
[[package]]
name = "derive_arbitrary"
version = "1.3.1"
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",
- "time 0.3.23",
+ "time 0.3.36",
"tokio",
"tokio-util",
"tracing",
"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",
- "time 0.3.23",
+ "time 0.3.36",
"winapi",
]
[[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",
]
+[[package]]
+name = "num-conv"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
+
[[package]]
name = "num-derive"
version = "0.4.0"
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",
- "time 0.3.23",
+ "time 0.3.36",
"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"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
+
[[package]]
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"
-version = "0.3.23"
+version = "0.3.36"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "59e399c068f43a5d116fedaf73b203fa4f9c519f17e2b34f63221d3792f81446"
+checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885"
dependencies = [
+ "deranged",
"itoa",
+ "num-conv",
+ "powerfmt",
"serde",
"time-core",
"time-macros",
]
[[package]]
name = "time-core"
-version = "0.1.1"
+version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7300fbefb4dadc1af235a9cef3737cea692a9d97e1b9cbcd4ebdae6f8868e6fb"
+checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3"
[[package]]
name = "time-macros"
-version = "0.2.10"
+version = "0.2.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "96ba15a897f3c86766b757e5ac7221554c6750054d74d5b28844fce5fb36a6c4"
+checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf"
dependencies = [
+ "num-conv",
"time-core",
]
[[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",
- "time 0.3.23",
+ "time 0.3.36",
"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..6aadf9aef9 100644
--- a/supply-chain/audits.toml
+++ b/supply-chain/audits.toml
@@ -1381,20 +1381,30 @@ delta = "0.14.3 -> 0.20.1"
who = "Mike Hommey <mh+mozilla@glandium.org>"
criteria = "safe-to-deploy"
delta = "2.3.2 -> 2.3.3"
[[audits.debugid]]
who = "Gabriele Svelto <gsvelto@mozilla.com>"
criteria = "safe-to-deploy"
version = "0.8.0"
notes = "This crates was written by Sentry and I've fully audited it as Firefox crash reporting machinery relies on it."
+[[audits.deranged]]
+who = "Alex Franchuk <afranchuk@mozilla.com>"
+criteria = "safe-to-deploy"
+version = "0.3.11"
+notes = """
+This crate contains a decent bit of `unsafe` code, however all internal
+unsafety is verified with copious assertions (many are compile-time), and
+otherwise the unsafety is documented and left to the caller to verify.
+"""
+
[[audits.derive_arbitrary]]
who = "Mike Hommey <mh+mozilla@glandium.org>"
criteria = "safe-to-run"
delta = "1.1.0 -> 1.1.1"
[[audits.derive_arbitrary]]
who = "Mike Hommey <mh+mozilla@glandium.org>"
criteria = "safe-to-run"
delta = "1.1.1 -> 1.1.3"
@@ -2739,20 +2749,29 @@ who = "Josh Stone <jistone@redhat.com>"
criteria = "safe-to-deploy"
version = "0.4.3"
notes = "All code written or reviewed by Josh Stone."
[[audits.num-complex]]
who = "Josh Stone <jistone@redhat.com>"
criteria = "safe-to-deploy"
version = "0.4.2"
notes = "All code written or reviewed by Josh Stone."
+[[audits.num-conv]]
+who = "Alex Franchuk <afranchuk@mozilla.com>"
+criteria = "safe-to-deploy"
+version = "0.1.0"
+notes = """
+Very straightforward, simple crate. No dependencies, unsafe, extern,
+side-effectful std functions, etc.
+"""
+
[[audits.num-derive]]
who = "Josh Stone <jistone@redhat.com>"
criteria = "safe-to-deploy"
version = "0.3.3"
notes = "All code written or reviewed by Josh Stone."
[[audits.num-derive]]
who = "Mike Hommey <mh+mozilla@glandium.org>"
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 <mh+mozilla@glandium.org>"
criteria = "safe-to-deploy"
delta = "0.3.25 -> 0.3.26"
[[audits.plane-split]]
who = "Nicolas Silva <nical@fastmail.com>"
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 <afranchuk@mozilla.com>"
+criteria = "safe-to-deploy"
+version = "0.2.0"
+notes = """
+A tiny bit of unsafe code to implement functionality that isn't in stable rust
+yet, but it's all valid. Otherwise it's a pretty simple crate.
+"""
+
[[audits.ppv-lite86]]
who = "Mike Hommey <mh+mozilla@glandium.org>"
criteria = "safe-to-deploy"
delta = "0.2.16 -> 0.2.17"
[[audits.precomputed-hash]]
who = "Bobby Holley <bobbyholley@gmail.com>"
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 <mh+mozilla@glandium.org>"
criteria = "safe-to-run"
delta = "0.3.9 -> 0.3.17"
[[audits.time]]
who = "Kershaw Chang <kershaw@mozilla.com>"
criteria = "safe-to-deploy"
delta = "0.3.17 -> 0.3.23"
+[[audits.time]]
+who = "Alex Franchuk <afranchuk@mozilla.com>"
+criteria = "safe-to-deploy"
+delta = "0.3.23 -> 0.3.36"
+notes = """
+There's a bit of new unsafe code that is self-imposed because they now assert
+that ordinals are non-zero. All unsafe code was checked to ensure that the
+invariants claimed were true.
+"""
+
[[audits.time-core]]
who = "Kershaw Chang <kershaw@mozilla.com>"
criteria = "safe-to-deploy"
version = "0.1.0"
[[audits.time-core]]
who = "Mike Hommey <mh+mozilla@glandium.org>"
criteria = "safe-to-run"
version = "0.1.0"
[[audits.time-core]]
who = "Kershaw Chang <kershaw@mozilla.com>"
criteria = "safe-to-deploy"
delta = "0.1.0 -> 0.1.1"
+[[audits.time-core]]
+who = "Alex Franchuk <afranchuk@mozilla.com>"
+criteria = "safe-to-deploy"
+delta = "0.1.1 -> 0.1.2"
+
[[audits.time-macros]]
who = "Kershaw Chang <kershaw@mozilla.com>"
criteria = "safe-to-deploy"
version = "0.2.6"
[[audits.time-macros]]
who = "Mike Hommey <mh+mozilla@glandium.org>"
criteria = "safe-to-run"
delta = "0.2.4 -> 0.2.6"
[[audits.time-macros]]
who = "Kershaw Chang <kershaw@mozilla.com>"
criteria = "safe-to-deploy"
delta = "0.2.6 -> 0.2.10"
+[[audits.time-macros]]
+who = "Alex Franchuk <afranchuk@mozilla.com>"
+criteria = "safe-to-deploy"
+delta = "0.2.10 -> 0.2.18"
+
[[audits.tinystr]]
who = "Zibi Braniecki <zibi@unicode.org>"
criteria = "safe-to-deploy"
version = "0.3.4"
[[audits.tinystr]]
who = "Zibi Braniecki <zibi@unicode.org>"
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 <jacob@jhpratt.dev>"]
+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<const MIN: $internal, const MAX: $internal>(
+ 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<const MIN: $internal, const MAX: $internal>(
+ $internal,
+ );
+
+ impl $type<0, 0> {
+ #[inline(always)]
+ pub const fn exact<const VALUE: $internal>() -> $type<VALUE, VALUE> {
+ // Safety: The value is the only one in range.
+ unsafe { $type::new_unchecked(VALUE) }
+ }
+ }
+
+ impl<const MIN: $internal, const MAX: $internal> $type<MIN, MAX> {
+ /// The smallest value that can be represented by this type.
+ // Safety: `MIN` is in range by definition.
+ pub const MIN: Self = Self::new_static::<MIN>();
+
+ /// The largest value that can be represented by this type.
+ // Safety: `MAX` is in range by definition.
+ pub const MAX: Self = Self::new_static::<MAX>();
+
+ /// 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 {
+ <Self as $crate::traits::RangeIsValid>::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 {
+ <Self as $crate::traits::RangeIsValid>::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 {
+ <Self as $crate::traits::RangeIsValid>::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<Self> {
+ <Self as $crate::traits::RangeIsValid>::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<const VALUE: $internal>() -> Self {
+ <($type<MIN, VALUE>, $type<VALUE, MAX>) 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 {
+ <Self as $crate::traits::RangeIsValid>::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<const NEW_MIN: $internal, const NEW_MAX: $internal>(
+ self,
+ ) -> $type<NEW_MIN, NEW_MAX> {
+ <$type<MIN, MAX> as $crate::traits::RangeIsValid>::ASSERT;
+ <$type<NEW_MIN, NEW_MAX> as $crate::traits::RangeIsValid>::ASSERT;
+ <($type<MIN, MAX>, $type<NEW_MIN, NEW_MAX>) 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<NEW_MIN, NEW_MAX>> {
+ <$type<MIN, MAX> as $crate::traits::RangeIsValid>::ASSERT;
+ <$type<NEW_MIN, NEW_MAX> as $crate::traits::RangeIsValid>::ASSERT;
+ <($type<MIN, MAX>, $type<NEW_MIN, NEW_MAX>) as $crate::traits::NarrowIsValid>
+ ::ASSERT;
+ $type::<NEW_MIN, NEW_MAX>::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<Self, ParseIntError> {
+ <Self as $crate::traits::RangeIsValid>::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<Self> {
+ <Self as $crate::traits::RangeIsValid>::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 {
+ <Self as $crate::traits::RangeIsValid>::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<Self> {
+ <Self as $crate::traits::RangeIsValid>::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 {
+ <Self as $crate::traits::RangeIsValid>::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<Self> {
+ <Self as $crate::traits::RangeIsValid>::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 {
+ <Self as $crate::traits::RangeIsValid>::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<Self> {
+ <Self as $crate::traits::RangeIsValid>::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 {
+ <Self as $crate::traits::RangeIsValid>::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<Self> {
+ <Self as $crate::traits::RangeIsValid>::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 {
+ <Self as $crate::traits::RangeIsValid>::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<const RHS_VALUE: $internal>(
+ self,
+ rhs: $type<RHS_VALUE, RHS_VALUE>,
+ ) -> $type<0, RHS_VALUE> {
+ <Self as $crate::traits::RangeIsValid>::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<Self> {
+ <Self as $crate::traits::RangeIsValid>::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 {
+ <Self as $crate::traits::RangeIsValid>::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<Self> {
+ <Self as $crate::traits::RangeIsValid>::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 {
+ <Self as $crate::traits::RangeIsValid>::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<Self> {
+ <Self as $crate::traits::RangeIsValid>::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 {
+ <Self as $crate::traits::RangeIsValid>::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 {
+ <Self as $crate::traits::RangeIsValid>::ASSERT;
+ <Self as $crate::traits::NegIsSafe>::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<Self> {
+ <Self as $crate::traits::RangeIsValid>::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 {
+ <Self as $crate::traits::RangeIsValid>::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<Self> {
+ <Self as $crate::traits::RangeIsValid>::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 {
+ <Self as $crate::traits::RangeIsValid>::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<Self> {
+ <Self as $crate::traits::RangeIsValid>::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 {
+ <Self as $crate::traits::RangeIsValid>::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 {
+ <Self as $crate::traits::RangeIsValid>::ASSERT;
+ <Self as $crate::traits::AbsIsSafe>::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<Self> {
+ <Self as $crate::traits::RangeIsValid>::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 {
+ <Self as $crate::traits::RangeIsValid>::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 {
+ <Self as $crate::traits::RangeIsValid>::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 {
+ <Self as $crate::traits::RangeIsValid>::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 {
+ <Self as $crate::traits::RangeIsValid>::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 {
+ <Self as $crate::traits::RangeIsValid>::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 {
+ <Self as $crate::traits::RangeIsValid>::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 {
+ <Self as $crate::traits::RangeIsValid>::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 {
+ <Self as $crate::traits::RangeIsValid>::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 {
+ <Self as $crate::traits::RangeIsValid>::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<const MIN: $internal, const MAX: $internal> $optional_type<MIN, MAX> {
+ /// 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<MIN, MAX>) -> Self {
+ <$type<MIN, MAX> 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<MIN, MAX>> {
+ <$type<MIN, MAX> 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<MIN, MAX> 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<MIN, MAX> as $crate::traits::RangeIsValid>::ASSERT;
+ self.0
+ }
+
+ #[inline(always)]
+ pub const fn get_primitive(self) -> Option<$internal> {
+ <$type<MIN, MAX> 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<MIN, MAX> 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<MIN, MAX> as $crate::traits::RangeIsValid>::ASSERT;
+ self.get().is_some()
+ }
+ }
+
+ impl<const MIN: $internal, const MAX: $internal> fmt::Debug for $type<MIN, MAX> {
+ #[inline(always)]
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ <Self as $crate::traits::RangeIsValid>::ASSERT;
+ self.get().fmt(f)
+ }
+ }
+
+ impl<const MIN: $internal, const MAX: $internal> fmt::Debug for $optional_type<MIN, MAX> {
+ #[inline(always)]
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ <$type<MIN, MAX> as $crate::traits::RangeIsValid>::ASSERT;
+ self.get().fmt(f)
+ }
+ }
+
+ impl<const MIN: $internal, const MAX: $internal> fmt::Display for $type<MIN, MAX> {
+ #[inline(always)]
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ <Self as $crate::traits::RangeIsValid>::ASSERT;
+ self.get().fmt(f)
+ }
+ }
+
+ #[cfg(feature = "powerfmt")]
+ impl<
+ const MIN: $internal,
+ const MAX: $internal,
+ > smart_display::SmartDisplay for $type<MIN, MAX> {
+ type Metadata = <$internal as smart_display::SmartDisplay>::Metadata;
+
+ #[inline(always)]
+ fn metadata(
+ &self,
+ f: smart_display::FormatterOptions,
+ ) -> smart_display::Metadata<'_, Self> {
+ <Self as $crate::traits::RangeIsValid>::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 {
+ <Self as $crate::traits::RangeIsValid>::ASSERT;
+ self.get().fmt_with_metadata(f, metadata.reuse())
+ }
+ }
+
+ impl<const MIN: $internal, const MAX: $internal> Default for $optional_type<MIN, MAX> {
+ #[inline(always)]
+ fn default() -> Self {
+ <$type<MIN, MAX> as $crate::traits::RangeIsValid>::ASSERT;
+ Self::None
+ }
+ }
+
+ impl<const MIN: $internal, const MAX: $internal> AsRef<$internal> for $type<MIN, MAX> {
+ #[inline(always)]
+ fn as_ref(&self) -> &$internal {
+ <Self as $crate::traits::RangeIsValid>::ASSERT;
+ &self.get_ref()
+ }
+ }
+
+ impl<const MIN: $internal, const MAX: $internal> Borrow<$internal> for $type<MIN, MAX> {
+ #[inline(always)]
+ fn borrow(&self) -> &$internal {
+ <Self as $crate::traits::RangeIsValid>::ASSERT;
+ &self.get_ref()
+ }
+ }
+
+ impl<
+ const MIN_A: $internal,
+ const MAX_A: $internal,
+ const MIN_B: $internal,
+ const MAX_B: $internal,
+ > PartialEq<$type<MIN_B, MAX_B>> for $type<MIN_A, MAX_A> {
+ #[inline(always)]
+ fn eq(&self, other: &$type<MIN_B, MAX_B>) -> bool {
+ <Self as $crate::traits::RangeIsValid>::ASSERT;
+ <$type<MIN_B, MAX_B> 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<MIN_B, MAX_B>> for $optional_type<MIN_A, MAX_A> {
+ #[inline(always)]
+ fn eq(&self, other: &$optional_type<MIN_B, MAX_B>) -> bool {
+ <$type<MIN_A, MAX_A> as $crate::traits::RangeIsValid>::ASSERT;
+ <$type<MIN_B, MAX_B> 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<MIN_B, MAX_B>> for $type<MIN_A, MAX_A> {
+ #[inline(always)]
+ fn partial_cmp(&self, other: &$type<MIN_B, MAX_B>) -> Option<Ordering> {
+ <Self as $crate::traits::RangeIsValid>::ASSERT;
+ <$type<MIN_B, MAX_B> 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<MIN_B, MAX_B>> for $optional_type<MIN_A, MAX_A> {
+ #[inline]
+ fn partial_cmp(&self, other: &$optional_type<MIN_B, MAX_B>) -> Option<Ordering> {
+ <$type<MIN_A, MAX_A> as $crate::traits::RangeIsValid>::ASSERT;
+ <$type<MIN_B, MAX_B> 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<MIN, MAX> {
+ #[inline]
+ fn cmp(&self, other: &Self) -> Ordering {
+ <$type<MIN, MAX> 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<const MIN: $internal, const MAX: $internal> fmt::Binary for $type<MIN, MAX> {
+ #[inline(always)]
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ <Self as $crate::traits::RangeIsValid>::ASSERT;
+ self.get().fmt(f)
+ }
+ }
+
+ impl<const MIN: $internal, const MAX: $internal> fmt::LowerHex for $type<MIN, MAX> {
+ #[inline(always)]
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ <Self as $crate::traits::RangeIsValid>::ASSERT;
+ self.get().fmt(f)
+ }
+ }
+
+ impl<const MIN: $internal, const MAX: $internal> fmt::UpperHex for $type<MIN, MAX> {
+ #[inline(always)]
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ <Self as $crate::traits::RangeIsValid>::ASSERT;
+ self.get().fmt(f)
+ }
+ }
+
+ impl<const MIN: $internal, const MAX: $internal> fmt::LowerExp for $type<MIN, MAX> {
+ #[inline(always)]
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ <Self as $crate::traits::RangeIsValid>::ASSERT;
+ self.get().fmt(f)
+ }
+ }
+
+ impl<const MIN: $internal, const MAX: $internal> fmt::UpperExp for $type<MIN, MAX> {
+ #[inline(always)]
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ <Self as $crate::traits::RangeIsValid>::ASSERT;
+ self.get().fmt(f)
+ }
+ }
+
+ impl<const MIN: $internal, const MAX: $internal> fmt::Octal for $type<MIN, MAX> {
+ #[inline(always)]
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ <Self as $crate::traits::RangeIsValid>::ASSERT;
+ self.get().fmt(f)
+ }
+ }
+
+ impl<const MIN: $internal, const MAX: $internal> From<$type<MIN, MAX>> for $internal {
+ #[inline(always)]
+ fn from(value: $type<MIN, MAX>) -> Self {
+ <$type<MIN, MAX> as $crate::traits::RangeIsValid>::ASSERT;
+ value.get()
+ }
+ }
+
+ impl<
+ const MIN: $internal,
+ const MAX: $internal,
+ > From<$type<MIN, MAX>> for $optional_type<MIN, MAX> {
+ #[inline(always)]
+ fn from(value: $type<MIN, MAX>) -> Self {
+ <$type<MIN, MAX> as $crate::traits::RangeIsValid>::ASSERT;
+ Self::Some(value)
+ }
+ }
+
+ impl<
+ const MIN: $internal,
+ const MAX: $internal,
+ > From<Option<$type<MIN, MAX>>> for $optional_type<MIN, MAX> {
+ #[inline(always)]
+ fn from(value: Option<$type<MIN, MAX>>) -> Self {
+ <$type<MIN, MAX> 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<MIN, MAX>> for Option<$type<MIN, MAX>> {
+ #[inline(always)]
+ fn from(value: $optional_type<MIN, MAX>) -> Self {
+ <$type<MIN, MAX> as $crate::traits::RangeIsValid>::ASSERT;
+ value.get()
+ }
+ }
+
+ impl<const MIN: $internal, const MAX: $internal> TryFrom<$internal> for $type<MIN, MAX> {
+ type Error = TryFromIntError;
+
+ #[inline]
+ fn try_from(value: $internal) -> Result<Self, Self::Error> {
+ <Self as $crate::traits::RangeIsValid>::ASSERT;
+ Self::new(value).ok_or(TryFromIntError)
+ }
+ }
+
+ impl<const MIN: $internal, const MAX: $internal> FromStr for $type<MIN, MAX> {
+ type Err = ParseIntError;
+
+ #[inline]
+ fn from_str(s: &str) -> Result<Self, Self::Err> {
+ <Self as $crate::traits::RangeIsValid>::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<const MIN: $internal, const MAX: $internal> serde::Serialize for $type<MIN, MAX> {
+ #[inline(always)]
+ fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
+ <Self as $crate::traits::RangeIsValid>::ASSERT;
+ self.get().serialize(serializer)
+ }
+ }
+
+ #[cfg(feature = "serde")]
+ impl<
+ const MIN: $internal,
+ const MAX: $internal,
+ > serde::Serialize for $optional_type<MIN, MAX> {
+ #[inline(always)]
+ fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
+ <$type<MIN, MAX> 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<MIN, MAX> {
+ #[inline]
+ fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
+ <Self as $crate::traits::RangeIsValid>::ASSERT;
+ let internal = <$internal>::deserialize(deserializer)?;
+ Self::new(internal).ok_or_else(|| <D::Error as serde::de::Error>::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<MIN, MAX> {
+ #[inline]
+ fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
+ <$type<MIN, MAX> as $crate::traits::RangeIsValid>::ASSERT;
+ Ok(Self::Some($type::<MIN, MAX>::deserialize(deserializer)?))
+ }
+ }
+
+ #[cfg(feature = "rand")]
+ impl<
+ const MIN: $internal,
+ const MAX: $internal,
+ > rand::distributions::Distribution<$type<MIN, MAX>> for rand::distributions::Standard {
+ #[inline]
+ fn sample<R: rand::Rng + ?Sized>(&self, rng: &mut R) -> $type<MIN, MAX> {
+ <$type<MIN, MAX> 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<MIN, MAX>>
+ for rand::distributions::Standard {
+ #[inline]
+ fn sample<R: rand::Rng + ?Sized>(&self, rng: &mut R) -> $optional_type<MIN, MAX> {
+ <$type<MIN, MAX> as $crate::traits::RangeIsValid>::ASSERT;
+ rng.gen::<Option<$type<MIN, MAX>>>().into()
+ }
+ }
+
+ #[cfg(feature = "num")]
+ impl<const MIN: $internal, const MAX: $internal> num_traits::Bounded for $type<MIN, MAX> {
+ #[inline(always)]
+ fn min_value() -> Self {
+ <Self as $crate::traits::RangeIsValid>::ASSERT;
+ Self::MIN
+ }
+
+ #[inline(always)]
+ fn max_value() -> Self {
+ <Self as $crate::traits::RangeIsValid>::ASSERT;
+ Self::MAX
+ }
+ }
+
+ #[cfg(feature = "quickcheck")]
+ impl<const MIN: $internal, const MAX: $internal> quickcheck::Arbitrary for $type<MIN, MAX> {
+ #[inline]
+ fn arbitrary(g: &mut quickcheck::Gen) -> Self {
+ <Self as $crate::traits::RangeIsValid>::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<dyn Iterator<Item = Self>> {
+ ::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<MIN, MAX> {
+ #[inline]
+ fn arbitrary(g: &mut quickcheck::Gen) -> Self {
+ <$type<MIN, MAX> as $crate::traits::RangeIsValid>::ASSERT;
+ Option::<$type<MIN, MAX>>::arbitrary(g).into()
+ }
+
+ #[inline]
+ fn shrink(&self) -> ::alloc::boxed::Box<dyn Iterator<Item = Self>> {
+ ::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<const MIN: $inner_ty, const MAX: $inner_ty> RangeIsValid for $ranged_ty<MIN, MAX> {
+ 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<CURRENT_MIN, CURRENT_MAX>, $ranged_ty<NEW_MIN, NEW_MAX>) {
+ 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<CURRENT_MIN, CURRENT_MAX>, $ranged_ty<NEW_MIN, NEW_MAX>) {
+ 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<MIN, VALUE>, $ranged_ty<VALUE, MAX>) {
+ const ASSERT: () = {
+ assert!(VALUE >= MIN);
+ assert!(VALUE <= MAX);
+ };
+ }
+ )*};
+}
+
+macro_rules! impl_traits_for_signed {
+ ($($ranged_ty:ident $inner_ty:ident),* $(,)?) => {$(
+ impl<const MIN: $inner_ty, const MAX: $inner_ty> AbsIsSafe for $ranged_ty<MIN, MAX> {
+ const ASSERT: () = {
+ assert!(MIN != <$inner_ty>::MIN);
+ assert!(-MIN <= MAX);
+ };
+ }
+
+ impl<const MIN: $inner_ty, const MAX: $inner_ty> NegIsSafe for $ranged_ty<MIN, MAX> {
+ 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<const MIN: $inner_ty, const MAX: $inner_ty> AbsIsSafe for $ranged_ty<MIN, MAX> {
+ const ASSERT: () = ();
+ }
+
+ impl<const MIN: $inner_ty, const MAX: $inner_ty> NegIsSafe for $ranged_ty<MIN, MAX> {
+ 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>(T);
+
+impl<T: core::fmt::Debug> core::fmt::Debug for Unsafe<T> {
+ fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+ self.0.fmt(f)
+ }
+}
+
+impl<T> Unsafe<T> {
+ pub(crate) const unsafe fn new(value: T) -> Self {
+ Self(value)
+ }
+
+ pub(crate) const fn get(&self) -> &T {
+ &self.0
+ }
+}
+
+impl<T> core::ops::Deref for Unsafe<T> {
+ 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 <jacob@jhpratt.dev>"]
+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<T> {
+ fn extend(self) -> T;
+ }
+
+ pub trait TruncateTargetSealed<T> {
+ 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<T>: sealed::ExtendTargetSealed<T> {}
+
+/// 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<T>: sealed::TruncateTargetSealed<T> {}
+
+/// Extend to an integer of the same size or larger, preserving its value.
+///
+/// ```rust
+/// # use num_conv::Extend;
+/// assert_eq!(0_u8.extend::<u16>(), 0_u16);
+/// assert_eq!(0_u16.extend::<u32>(), 0_u32);
+/// assert_eq!(0_u32.extend::<u64>(), 0_u64);
+/// assert_eq!(0_u64.extend::<u128>(), 0_u128);
+/// ```
+///
+/// ```rust
+/// # use num_conv::Extend;
+/// assert_eq!((-1_i8).extend::<i16>(), -1_i16);
+/// assert_eq!((-1_i16).extend::<i32>(), -1_i32);
+/// assert_eq!((-1_i32).extend::<i64>(), -1_i64);
+/// assert_eq!((-1_i64).extend::<i128>(), -1_i128);
+/// ```
+pub trait Extend: sealed::Integer {
+ /// Extend an integer to an integer of the same size or larger, preserving its value.
+ fn extend<T>(self) -> T
+ where
+ Self: ExtendTarget<T>;
+}
+
+impl<T: sealed::Integer> Extend for T {
+ fn extend<U>(self) -> U
+ where
+ T: ExtendTarget<U>,
+ {
+ 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>(), u8::MAX);
+/// assert_eq!(u32::MAX.truncate::<u16>(), u16::MAX);
+/// assert_eq!(u64::MAX.truncate::<u32>(), u32::MAX);
+/// assert_eq!(u128::MAX.truncate::<u64>(), u64::MAX);
+/// ```
+///
+/// ```rust
+/// # use num_conv::Truncate;
+/// assert_eq!((-1_i16).truncate::<i8>(), -1_i8);
+/// assert_eq!((-1_i32).truncate::<i16>(), -1_i16);
+/// assert_eq!((-1_i64).truncate::<i32>(), -1_i32);
+/// assert_eq!((-1_i128).truncate::<i64>(), -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<T>(self) -> T
+ where
+ Self: TruncateTarget<T>;
+}
+
+impl<T: sealed::Integer> Truncate for T {
+ fn truncate<U>(self) -> U
+ where
+ T: TruncateTarget<U>,
+ {
+ 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 <jacob@jhpratt.dev>"]
+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<const SIZE: usize> {
+ buf: [MaybeUninit<u8>; SIZE],
+ len: usize,
+}
+
+impl<const SIZE: usize> fmt::Debug for WriteBuffer<SIZE> {
+ 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<const SIZE: usize> WriteBuffer<SIZE> {
+ /// 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<const SIZE: usize> Default for WriteBuffer<SIZE> {
+ fn default() -> Self {
+ Self::new()
+ }
+}
+
+impl<const LEFT_SIZE: usize, const RIGHT_SIZE: usize> PartialOrd<WriteBuffer<RIGHT_SIZE>>
+ for WriteBuffer<LEFT_SIZE>
+{
+ fn partial_cmp(&self, other: &WriteBuffer<RIGHT_SIZE>) -> Option<core::cmp::Ordering> {
+ self.as_str().partial_cmp(other.as_str())
+ }
+}
+
+impl<const LEFT_SIZE: usize, const RIGHT_SIZE: usize> PartialEq<WriteBuffer<RIGHT_SIZE>>
+ for WriteBuffer<LEFT_SIZE>
+{
+ fn eq(&self, other: &WriteBuffer<RIGHT_SIZE>) -> bool {
+ self.as_str() == other.as_str()
+ }
+}
+
+impl<const SIZE: usize> Eq for WriteBuffer<SIZE> {}
+
+impl<const SIZE: usize> Ord for WriteBuffer<SIZE> {
+ fn cmp(&self, other: &Self) -> core::cmp::Ordering {
+ self.as_str().cmp(other.as_str())
+ }
+}
+
+impl<const SIZE: usize> Hash for WriteBuffer<SIZE> {
+ fn hash<H: Hasher>(&self, state: &mut H) {
+ self.as_str().hash(state)
+ }
+}
+
+impl<const SIZE: usize> AsRef<str> for WriteBuffer<SIZE> {
+ fn as_ref(&self) -> &str {
+ self
+ }
+}
+
+impl<const SIZE: usize> AsRef<[u8]> for WriteBuffer<SIZE> {
+ fn as_ref(&self) -> &[u8] {
+ self.as_bytes()
+ }
+}
+
+impl<const SIZE: usize> core::borrow::Borrow<str> for WriteBuffer<SIZE> {
+ fn borrow(&self) -> &str {
+ self
+ }
+}
+
+impl<const SIZE: usize> core::ops::Deref for WriteBuffer<SIZE> {
+ 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<const SIZE: usize> fmt::Display for WriteBuffer<SIZE> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.write_str(self)
+ }
+}
+
+impl<const SIZE: usize> SmartDisplay for WriteBuffer<SIZE> {
+ 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<const SIZE: usize> fmt::Write for WriteBuffer<SIZE> {
+ 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<T, const N: usize>() -> [MaybeUninit<T>; N] {
+ // SAFETY: An uninitialized `[MaybeUninit<_>; LEN]` is valid.
+ unsafe { MaybeUninit::<[MaybeUninit<T>; N]>::uninit().assume_init() }
+}
+
+/// Equivalent of [`MaybeUninit::write_slice`] that compiles on stable.
+fn maybe_uninit_write_slice<'a, T>(this: &'a mut [MaybeUninit<T>], src: &[T]) -> &'a mut [T]
+where
+ T: Copy,
+{
+ #[allow(trivial_casts)]
+ // SAFETY: T and MaybeUninit<T> have the same layout
+ let uninit_src = unsafe { &*(src as *const [T] as *const [MaybeUninit<T>]) };
+
+ 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<T, U>(slice: &mut [MaybeUninit<T>]) -> &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<T>] 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<T>(slice: &[MaybeUninit<T>]) -> &[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<T>] 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<Self>,
+//! ) -> 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<Self> {
+/// # 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<Alignment>,
+ width: MaybeUninit<usize>,
+ precision: MaybeUninit<usize>,
+}
+
+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<Alignment>) -> &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<usize>) -> &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<usize>) -> &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<Alignment> {
+ self.align
+ }
+
+ /// Optionally specified integer width that the output should be.
+ #[inline]
+ #[must_use]
+ pub const fn width(&self) -> Option<usize> {
+ 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<usize> {
+ 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<T> 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<T> 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<T> 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<Metadata = T::Metadata> + ?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<T>(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<T>(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<T> 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<dyn std::error::Error>>(())
+ /// ```
+ 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<Owned = O> + ?Sized,
+ O: SmartDisplay<Metadata = B::Metadata> + '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<T> 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<T> 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<T> 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<T> 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<T> 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<T> SmartDisplay for Wrapping<T>
+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<T> SmartDisplay for Rc<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)
+ }
+}
+
+#[cfg(feature = "alloc")]
+impl<T> SmartDisplay for Arc<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)
+ }
+}
+
+#[cfg(feature = "alloc")]
+impl<T> SmartDisplay for Box<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)
+ }
+}
+
+/// 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
+++ b/third_party/rust/time-core/.cargo-checksum.json
@@ -1 +1 @@
-{"files":{"Cargo.toml":"032c780eaf4ddfde703d5a6b260ad7bad35a5a1ee57a33cacf503f5e47dff6a9","LICENSE-Apache":"b8929fea28678da67251fb2daf9438f67503814211051861612441806d8edb05","LICENSE-MIT":"04620bf27e4a643dd47bf27652320c205acdb776c1f9f24bb8c3bfaba10804c5","src/convert.rs":"59566933f2977d62abbfe39b20be16a85df00db8627211471ccfe182dbbe684c","src/lib.rs":"18020c914b1cd561465e624ef3ea3eef980bd82bc93847e2543bce12da28b043","src/util.rs":"52c1fbf68b71c3582caf0d9a8255378c6c14a737e2df8d7e6d6603b0eb12ca06"},"package":"7300fbefb4dadc1af235a9cef3737cea692a9d97e1b9cbcd4ebdae6f8868e6fb"}
\ No newline at end of file
+{"files":{"Cargo.toml":"d90c41d20f37fc3dbc3d88f7715cacafb5aea973030f498e9b2833decdbe63f0","LICENSE-Apache":"b8929fea28678da67251fb2daf9438f67503814211051861612441806d8edb05","LICENSE-MIT":"04620bf27e4a643dd47bf27652320c205acdb776c1f9f24bb8c3bfaba10804c5","src/convert.rs":"354a1b05e8bb1e92eda5dcdecf33dc6cf2ce72b11115ae4cb0909dcd51d2b294","src/lib.rs":"461b752a45b0f819284e8d8e6b2f49d52b3b661026ab84ee64bf04f4daa0a2d2","src/util.rs":"52c1fbf68b71c3582caf0d9a8255378c6c14a737e2df8d7e6d6603b0eb12ca06"},"package":"ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3"}
\ No newline at end of file
diff --git a/third_party/rust/time-core/Cargo.toml b/third_party/rust/time-core/Cargo.toml
index bbc8a325e3..3d6555ebd5 100644
--- a/third_party/rust/time-core/Cargo.toml
+++ b/third_party/rust/time-core/Cargo.toml
@@ -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"
-rust-version = "1.65.0"
+rust-version = "1.67.0"
name = "time-core"
-version = "0.1.1"
+version = "0.1.2"
authors = [
"Jacob Pratt <open-source@jhpratt.dev>",
"Time contributors",
]
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"
+[package.metadata.docs.rs]
+rustdoc-args = ["--generate-link-to-definition"]
+
[dependencies]
diff --git a/third_party/rust/time-core/src/convert.rs b/third_party/rust/time-core/src/convert.rs
index dae77e9afa..9c28f02638 100644
--- a/third_party/rust/time-core/src/convert.rs
+++ b/third_party/rust/time-core/src/convert.rs
@@ -1,88 +1,104 @@
-#![allow(clippy::missing_docs_in_private_items)] // TODO temporary
+//! Conversion between units of time.
-macro_rules! declare_structs {
- ($($t:ident)*) => {$(
- #[derive(Debug, Copy, Clone)]
+use self::sealed::Per;
+
+mod sealed {
+ /// A trait for defining the ratio of two units of time.
+ ///
+ /// This trait is used to implement the `per` method on the various structs.
+ pub trait Per<T> {
+ /// The smallest unsigned integer type that can represent [`VALUE`](Self::VALUE).
+ type Output;
+
+ /// The number of one unit of time in the other.
+ const VALUE: Self::Output;
+ }
+}
+
+/// Declare and implement `Per` for all relevant types. Identity implementations are automatic.
+macro_rules! impl_per {
+ ($($t:ident ($str:literal) per {$(
+ $larger:ident : $output:ty = $value:expr
+ )*})*) => {$(
+ #[doc = concat!("A unit of time representing exactly one ", $str, ".")]
+ #[derive(Debug, Clone, Copy)]
pub struct $t;
impl $t {
- pub const fn per<T>(self, _: T) -> <(Self, T) as Per>::Output
+ #[doc = concat!("Obtain the number of times `", stringify!($t), "` can fit into `T`.")]
+ #[doc = concat!("If `T` is smaller than `", stringify!($t), "`, the code will fail to")]
+ /// compile. The return type is the smallest unsigned integer type that can represent
+ /// the value.
+ ///
+ /// Valid calls:
+ ///
+ #[doc = concat!(" - `", stringify!($t), "::per(", stringify!($t), ")` (returns `u8`)")]
+ $(#[doc = concat!(" - `", stringify!($t), "::per(", stringify!($larger), ")` (returns `", stringify!($output), "`)")])*
+ pub const fn per<T>(_larger: T) -> <Self as Per<T>>::Output
where
- (Self, T): Per,
+ Self: Per<T>,
T: Copy,
{
- <(Self, T)>::VALUE
+ Self::VALUE
}
}
- )*};
-}
-
-declare_structs! {
- Nanosecond
- Microsecond
- Millisecond
- Second
- Minute
- Hour
- Day
- Week
-}
-
-mod sealed {
- pub trait Sealed {}
-}
-pub trait Per: sealed::Sealed {
- type Output;
+ impl Per<$t> for $t {
+ type Output = u8;
- const VALUE: Self::Output;
-}
-
-macro_rules! impl_per {
- ($($t:ty : $x:ident in $y:ident = $val:expr)*) => {$(
- impl sealed::Sealed for ($x, $y) {}
+ const VALUE: u8 = 1;
+ }
- impl Per for ($x, $y) {
- type Output = $t;
+ $(impl Per<$larger> for $t {
+ type Output = $output;
- const VALUE: $t = $val;
- }
+ const VALUE: $output = $value;
+ })*
)*};
}
impl_per! {
- u16: Nanosecond in Microsecond = 1_000
- u32: Nanosecond in Millisecond = 1_000_000
- u32: Nanosecond in Second = 1_000_000_000
- u64: Nanosecond in Minute = 60_000_000_000
- u64: Nanosecond in Hour = 3_600_000_000_000
- u64: Nanosecond in Day = 86_400_000_000_000
- u64: Nanosecond in Week = 604_800_000_000_000
-
- u16: Microsecond in Millisecond = 1_000
- u32: Microsecond in Second = 1_000_000
- u32: Microsecond in Minute = 60_000_000
- u32: Microsecond in Hour = 3_600_000_000
- u64: Microsecond in Day = 86_400_000_000
- u64: Microsecond in Week = 604_800_000_000
-
- u16: Millisecond in Second = 1_000
- u16: Millisecond in Minute = 60_000
- u32: Millisecond in Hour = 3_600_000
- u32: Millisecond in Day = 86_400_000
- u32: Millisecond in Week = 604_800_000
-
- u8: Second in Minute = 60
- u16: Second in Hour = 3_600
- u32: Second in Day = 86_400
- u32: Second in Week = 604_800
-
- u8: Minute in Hour = 60
- u16: Minute in Day = 1_440
- u16: Minute in Week = 10_080
-
- u8: Hour in Day = 24
- u8: Hour in Week = 168
-
- u8: Day in Week = 7
+ Nanosecond ("nanosecond") per {
+ Microsecond: u16 = 1_000
+ Millisecond: u32 = 1_000_000
+ Second: u32 = 1_000_000_000
+ Minute: u64 = 60_000_000_000
+ Hour: u64 = 3_600_000_000_000
+ Day: u64 = 86_400_000_000_000
+ Week: u64 = 604_800_000_000_000
+ }
+ Microsecond ("microsecond") per {
+ Millisecond: u16 = 1_000
+ Second: u32 = 1_000_000
+ Minute: u32 = 60_000_000
+ Hour: u32 = 3_600_000_000
+ Day: u64 = 86_400_000_000
+ Week: u64 = 604_800_000_000
+ }
+ Millisecond ("millisecond") per {
+ Second: u16 = 1_000
+ Minute: u16 = 60_000
+ Hour: u32 = 3_600_000
+ Day: u32 = 86_400_000
+ Week: u32 = 604_800_000
+ }
+ Second ("second") per {
+ Minute: u8 = 60
+ Hour: u16 = 3_600
+ Day: u32 = 86_400
+ Week: u32 = 604_800
+ }
+ Minute ("minute") per {
+ Hour: u8 = 60
+ Day: u16 = 1_440
+ Week: u16 = 10_080
+ }
+ Hour ("hour") per {
+ Day: u8 = 24
+ Week: u8 = 168
+ }
+ Day ("day") per {
+ Week: u8 = 7
+ }
+ Week ("week") per {}
}
diff --git a/third_party/rust/time-core/src/lib.rs b/third_party/rust/time-core/src/lib.rs
index 4f1c53b12e..41c3547869 100644
--- a/third_party/rust/time-core/src/lib.rs
+++ b/third_party/rust/time-core/src/lib.rs
@@ -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]
-#![deny(
- anonymous_parameters,
- clippy::all,
- clippy::alloc_instead_of_core,
- clippy::explicit_auto_deref,
- clippy::obfuscated_if_else,
- clippy::std_instead_of_core,
- clippy::undocumented_unsafe_blocks,
- illegal_floating_point_literal_pattern,
- late_bound_lifetime_arguments,
- path_statements,
- patterns_in_fns_without_body,
- rust_2018_idioms,
- trivial_casts,
- trivial_numeric_casts,
- unreachable_pub,
- unsafe_op_in_unsafe_fn,
- unused_extern_crates,
- rustdoc::broken_intra_doc_links,
- rustdoc::private_intra_doc_links
-)]
-#![warn(
- clippy::dbg_macro,
- clippy::decimal_literal_representation,
- clippy::get_unwrap,
- clippy::missing_docs_in_private_items,
- clippy::nursery,
- clippy::print_stdout,
- clippy::todo,
- clippy::unimplemented,
- clippy::unnested_or_patterns,
- clippy::unwrap_in_result,
- clippy::unwrap_used,
- clippy::use_debug,
- deprecated_in_future,
- missing_copy_implementations,
- missing_debug_implementations,
- unused_qualifications,
- variant_size_differences
-)]
-#![allow(clippy::redundant_pub_crate)]
#![doc(html_favicon_url = "https://avatars0.githubusercontent.com/u/55999857")]
#![doc(html_logo_url = "https://avatars0.githubusercontent.com/u/55999857")]
#![doc(test(attr(deny(warnings))))]
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
+++ b/third_party/rust/time-macros/.cargo-checksum.json
@@ -1 +1 @@
-{"files":{"Cargo.toml":"97dbc36d7e8c8658e151c1cfe57397a116135a0d0efc97aacd339142da5d1c96","LICENSE-Apache":"b8929fea28678da67251fb2daf9438f67503814211051861612441806d8edb05","LICENSE-MIT":"04620bf27e4a643dd47bf27652320c205acdb776c1f9f24bb8c3bfaba10804c5","src/date.rs":"ffcd3d0998ec67abb43a3f8eccc6104172f5061b855312b89d53bb82fece2460","src/datetime.rs":"5c7f6e07dc2f0dcfcd86216664df53bc008dbc86f346df57a9ff57f52fe43bc6","src/error.rs":"b3dea92631092068dd73e57e1cbf548f7ae85762826dcdea7fd6454bf357a50a","src/format_description/ast.rs":"8ba87e3249766b89c42b040f623d3134aeec46b78208fdfee825ed0eeeb4591a","src/format_description/format_item.rs":"03ff10699383e5ad08fe690199d45288f13363337abbc811a70b03a8b1703ab1","src/format_description/lexer.rs":"e7db7b6431f00c81b8d15a162088a1622ecd65bfb58d4e642c3c93a8dd5ae4ad","src/format_description/mod.rs":"f48c0ff590bc74529f06a98f60a6af5814bc30d1456bf0b81ac334c0b3f41bba","src/format_description/public/component.rs":"e2c2c8a189e2eb9f9354ff1d9d8edeafa34303e91dc58457df373e7e61c38b78","src/format_description/public/mod.rs":"5260592b310ea9e30808d30c92ea94c7bf1bdb171250a1342279e927d2528d73","src/format_description/public/modifier.rs":"37661e1f7cd9fd11a82f5a1ce6d5971686afa91e6feebc7b9d32df297e8b667f","src/helpers/mod.rs":"a8f8ed59a72b239d7a530357d212873f2e75ea924ec19a6d5d6e24a2baa8100c","src/helpers/string.rs":"3af2d0c701ca978c705922a272e76506dbdf0f376d44ed9ae7283086c67852ba","src/lib.rs":"200678edc14d5920abc0516717b8e010667e58da8bdc65c1cb583fdde0353089","src/offset.rs":"4b9c001a954c1f121a572f5675073f7a4e46d00cc9eb77736bfea2df94ffd05b","src/quote.rs":"634a12b95236e4ab2b8ab70a1a4a2629113c3ce3cf6defefc7ffeb81544c1d89","src/serde_format_description.rs":"db5fb2dc94e01c5114cab3484e68334516d53c4642f31dae0d66f1183253a17c","src/time.rs":"d762e8f22f749d9546d5d2a78b8a0380510be27b4cd3fed375695d7982d8396e","src/to_tokens.rs":"6636ea489c7484bad9b39f72d6956a04c95ce82d8462b12079cc03db778fd263"},"package":"96ba15a897f3c86766b757e5ac7221554c6750054d74d5b28844fce5fb36a6c4"}
\ No newline at end of file
+{"files":{"Cargo.toml":"3330436e81a4de8b20b9a2931f9e857b7974a8423462d928b04cff55ad531cff","LICENSE-Apache":"edd65bdd88957a205c47d53fa499eed8865a70320f0f03f6391668cb304ea376","LICENSE-MIT":"231c837c45eb53f108fb48929e488965bc4fcc14e9ea21d35f50e6b99d98685b","src/date.rs":"be197c8a2ed37e8b3123a798a91697b0e61cf9b60e7b1898a0e1b458fe8e3ef1","src/datetime.rs":"5c7f6e07dc2f0dcfcd86216664df53bc008dbc86f346df57a9ff57f52fe43bc6","src/error.rs":"b3dea92631092068dd73e57e1cbf548f7ae85762826dcdea7fd6454bf357a50a","src/format_description/ast.rs":"697d5ce506b5386092d706bfe5bf4f81f50e1130796cb17c2fc61457fb165307","src/format_description/format_item.rs":"02d12976209c7af83c2aa4a3221a1a65420fae8c8ba12a28933fb738a2872ff9","src/format_description/lexer.rs":"e2c75f3dda5773a0c8301fdfc0d58a0b833923ba59ac04bcc49fd10aee20496c","src/format_description/mod.rs":"2109b77a8198769c6a6732a54233d7e0058bf4a6da724824103d107859795956","src/format_description/public/component.rs":"5d86912e247724957f7183d70745ced20a7408ed90c24bb47da73a0e26550899","src/format_description/public/mod.rs":"8030e767cb94d559dda2dddc72d42654a756362bd165e5c2cccf112f15d49610","src/format_description/public/modifier.rs":"e1d8fdababcaee2e181a7acb3a938baf309f5a0e2d3877585cf678fcc12f212a","src/helpers/mod.rs":"af47d6c053ffd1113788c5d7591d46fa7d879dc0c5cb2c6c02f9c05462499c4f","src/helpers/string.rs":"3af2d0c701ca978c705922a272e76506dbdf0f376d44ed9ae7283086c67852ba","src/lib.rs":"6ed2d4a41d15a1b5d9fef7d437a1520d967acbfbab98a88630062340f701ca54","src/offset.rs":"aed29d0da9fc65a7dc77314e0346dfdc6fdaf663f17adf9edf00933e8f8e605f","src/quote.rs":"d3003dafa3073825f188851a974846099681cc81145070affb033469cbc7bb31","src/serde_format_description.rs":"db5fb2dc94e01c5114cab3484e68334516d53c4642f31dae0d66f1183253a17c","src/time.rs":"299ddb54e44fb88e514592db5335f06352ebdd0dbf064752790657db85f4c13c","src/to_tokens.rs":"afb067f4d95d19c1b7a650cbcf60ae155b5a9619c89825867997f39ce163ac94"},"package":"3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf"}
\ No newline at end of file
diff --git a/third_party/rust/time-macros/Cargo.toml b/third_party/rust/time-macros/Cargo.toml
index 1c86d657d5..b817622154 100644
--- a/third_party/rust/time-macros/Cargo.toml
+++ b/third_party/rust/time-macros/Cargo.toml
@@ -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"
-rust-version = "1.65.0"
+rust-version = "1.67.0"
name = "time-macros"
-version = "0.2.10"
+version = "0.2.18"
authors = [
"Jacob Pratt <open-source@jhpratt.dev>",
"Time contributors",
]
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"
+[package.metadata.docs.rs]
+rustdoc-args = ["--generate-link-to-definition"]
+
[lib]
proc-macro = true
+[dependencies.num-conv]
+version = "0.1.0"
+
[dependencies.time-core]
-version = "=0.1.1"
+version = "=0.1.2"
[features]
formatting = []
large-dates = []
parsing = []
serde = []
+
+[lints.clippy]
+all = "warn"
+alloc-instead-of-core = "deny"
+dbg-macro = "warn"
+decimal-literal-representation = "warn"
+explicit-auto-deref = "warn"
+get-unwrap = "warn"
+manual-let-else = "warn"
+missing-docs-in-private-items = "warn"
+missing-enforced-import-renames = "warn"
+nursery = "warn"
+obfuscated-if-else = "warn"
+print-stdout = "warn"
+semicolon-outside-block = "warn"
+std-instead-of-core = "deny"
+todo = "warn"
+undocumented-unsafe-blocks = "deny"
+unimplemented = "warn"
+uninlined-format-args = "warn"
+unnested-or-patterns = "warn"
+unwrap-in-result = "warn"
+unwrap-used = "warn"
+use-debug = "warn"
+
+[lints.clippy.option-if-let-else]
+level = "allow"
+priority = 1
+
+[lints.clippy.redundant-pub-crate]
+level = "allow"
+priority = 1
+
+[lints.rust]
+ambiguous-glob-reexports = "deny"
+clashing-extern-declarations = "deny"
+const-item-mutation = "deny"
+deref-nullptr = "deny"
+drop-bounds = "deny"
+future-incompatible = "deny"
+hidden-glob-reexports = "deny"
+improper-ctypes = "deny"
+improper-ctypes-definitions = "deny"
+invalid-from-utf8 = "deny"
+invalid-macro-export-arguments = "deny"
+invalid-nan-comparisons = "deny"
+invalid-reference-casting = "deny"
+invalid-value = "deny"
+keyword-idents = "warn"
+let-underscore = "warn"
+macro-use-extern-crate = "warn"
+meta-variable-misuse = "warn"
+missing-abi = "warn"
+missing-copy-implementations = "warn"
+missing-debug-implementations = "warn"
+missing-docs = "warn"
+named-arguments-used-positionally = "deny"
+non-ascii-idents = "deny"
+noop-method-call = "warn"
+opaque-hidden-inferred-bound = "deny"
+overlapping-range-endpoints = "deny"
+single-use-lifetimes = "warn"
+suspicious-double-ref-op = "deny"
+temporary-cstring-as-ptr = "deny"
+trivial-casts = "warn"
+trivial-numeric-casts = "warn"
+unconditional-recursion = "deny"
+unnameable-test-items = "deny"
+unreachable-pub = "warn"
+unsafe-op-in-unsafe-fn = "deny"
+unstable-syntax-pre-expansion = "deny"
+unused = "warn"
+unused-import-braces = "warn"
+unused-lifetimes = "warn"
+unused-qualifications = "warn"
+variant-size-differences = "warn"
+
+[lints.rust.unstable-name-collisions]
+level = "warn"
+priority = 1
+
+[lints.rustdoc]
+private-doc-tests = "warn"
+unescaped-backticks = "warn"
diff --git a/third_party/rust/time-macros/LICENSE-Apache b/third_party/rust/time-macros/LICENSE-Apache
index 7646f21e37..c763a0c9de 100644
--- a/third_party/rust/time-macros/LICENSE-Apache
+++ b/third_party/rust/time-macros/LICENSE-Apache
@@ -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.
- Copyright 2022 Jacob Pratt et al.
+ Copyright 2024 Jacob Pratt et al.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
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,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,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};
use crate::helpers::{
consume_any_ident, consume_number, consume_punct, days_in_year_month, ymd_to_yo, ywd_to_yo,
};
use crate::to_tokens::ToTokenTree;
use crate::Error;
#[cfg(feature = "large-dates")]
@@ -86,21 +87,21 @@ pub(crate) fn parse(chars: &mut Peekable<token_stream::IntoIter>) -> Result<Date
let (day_span, day) = consume_number::<u8>("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),
});
}
- let month = month as _;
+ let month = month.truncate();
if day == 0 || day > days_in_year_month(year, month) {
return Err(Error::InvalidComponent {
name: "day",
value: day.to_string(),
span_start: Some(day_span),
span_end: Some(day_span),
});
}
let (year, ordinal) = ymd_to_yo(year, month, day);
@@ -120,18 +121,20 @@ pub(crate) fn parse(chars: &mut Peekable<token_stream::IntoIter>) -> Result<Date
});
}
Ok(Date { year, ordinal })
}
}
impl ToTokenTree for Date {
fn into_token_tree(self) -> TokenTree {
quote_group! {{
- const DATE: ::time::Date = ::time::Date::__from_ordinal_date_unchecked(
- #(self.year),
- #(self.ordinal),
- );
+ const DATE: ::time::Date = unsafe {
+ ::time::Date::__from_ordinal_date_unchecked(
+ #(self.year),
+ #(self.ordinal),
+ )
+ };
DATE
}}
}
}
diff --git a/third_party/rust/time-macros/src/format_description/ast.rs b/third_party/rust/time-macros/src/format_description/ast.rs
index 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,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<Location>,
_second: Unused<Location>,
},
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,11 +1,10 @@
-use std::boxed::Box;
use std::num::NonZeroU16;
use std::str::{self, FromStr};
use super::{ast, unused, Error, Span, Spanned, Unused};
pub(super) fn parse<'a>(
ast_items: impl Iterator<Item = Result<ast::Item<'a>, Error>>,
) -> impl Iterator<Item = Result<Item<'a>, Error>> {
ast_items.map(|ast_item| ast_item.and_then(Item::from_ast))
}
@@ -96,28 +95,23 @@ impl From<Item<'_>> for crate::format_description::public::OwnedFormatItem {
Item::First { value, _span: _ } => {
Self::First(value.into_vec().into_iter().map(Into::into).collect())
}
}
}
}
impl<'a> From<Box<[Item<'a>]>> for crate::format_description::public::OwnedFormatItem {
fn from(items: Box<[Item<'a>]>) -> Self {
let items = items.into_vec();
- if items.len() == 1 {
- if let Ok([item]) = <[_; 1]>::try_from(items) {
- item.into()
- } else {
- bug!("the length was just checked to be 1")
- }
- } else {
- Self::Compound(items.into_iter().map(Self::from).collect())
+ match <[_; 1]>::try_from(items) {
+ Ok([item]) => item.into(),
+ Err(vec) => Self::Compound(vec.into_iter().map(Into::into).collect()),
}
}
}
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<Self, Error>
{
+ #[allow(unused_mut)]
let mut this = Self {
$($field: None),*
};
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> => padding,
},
+ End = "end" {},
Hour = "hour" {
padding = "padding": Option<Padding> => padding,
base = "repr": Option<HourBase> => is_12_hour_clock,
},
Ignore = "ignore" {
#[required]
count = "count": Option<#[from_str] NonZeroU16> => count,
},
Minute = "minute" {
padding = "padding": Option<Padding> => 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
@@ -1,16 +1,16 @@
use core::iter;
use super::{Error, Location, Spanned, SpannedValue};
pub(super) struct Lexed<I: Iterator> {
- iter: core::iter::Peekable<I>,
+ iter: iter::Peekable<I>,
}
impl<I: Iterator> Iterator for Lexed<I> {
type Item = I::Item;
fn next(&mut self) -> Option<Self::Item> {
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,40 +1,38 @@
//! Parser for format descriptions.
-use std::vec::Vec;
-
macro_rules! version {
($range:expr) => {
$range.contains(&VERSION)
};
}
mod ast;
mod format_item;
mod lexer;
mod public;
pub(crate) fn parse_with_version(
version: Option<crate::FormatDescriptionVersion>,
s: &[u8],
proc_span: proc_macro::Span,
-) -> Result<Vec<crate::format_description::public::OwnedFormatItem>, crate::Error> {
+) -> Result<Vec<public::OwnedFormatItem>, crate::Error> {
match version {
Some(crate::FormatDescriptionVersion::V1) | None => parse::<1>(s, proc_span),
Some(crate::FormatDescriptionVersion::V2) => parse::<2>(s, proc_span),
}
}
fn parse<const VERSION: u8>(
s: &[u8],
proc_span: proc_macro::Span,
-) -> Result<Vec<crate::format_description::public::OwnedFormatItem>, crate::Error> {
+) -> Result<Vec<public::OwnedFormatItem>, crate::Error> {
let mut lexed = lexer::lex::<VERSION>(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::<Result<_, _>>()?)
}
#[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
@@ -39,11 +39,12 @@ declare_component! {
Hour
Minute
Period
Second
Subsecond
OffsetHour
OffsetMinute
OffsetSecond
Ignore
UnixTimestamp
+ End
}
diff --git a/third_party/rust/time-macros/src/format_description/public/mod.rs b/third_party/rust/time-macros/src/format_description/public/mod.rs
index 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
@@ -12,43 +12,43 @@ pub(crate) enum OwnedFormatItem {
Component(Component),
Compound(Box<[Self]>),
Optional(Box<Self>),
First(Box<[Self]>),
}
impl ToTokenStream for OwnedFormatItem {
fn append_to(self, ts: &mut TokenStream) {
match self {
Self::Literal(bytes) => quote_append! { ts
- ::time::format_description::FormatItem::Literal {
+ ::time::format_description::BorrowedFormatItem::Literal {
0: #(Literal::byte_string(bytes.as_ref()))
}
},
Self::Component(component) => quote_append! { ts
- ::time::format_description::FormatItem::Component { 0: #S(component) }
+ ::time::format_description::BorrowedFormatItem::Component { 0: #S(component) }
},
Self::Compound(items) => {
let items = items
.into_vec()
.into_iter()
.map(|item| quote! { #S(item), })
.collect::<TokenStream>();
quote_append! { ts
- ::time::format_description::FormatItem::Compound { 0: &[#S(items)] }
+ ::time::format_description::BorrowedFormatItem::Compound { 0: &[#S(items)] }
}
}
Self::Optional(item) => quote_append! {ts
- ::time::format_description::FormatItem::Optional { 0: &#S(*item) }
+ ::time::format_description::BorrowedFormatItem::Optional { 0: &#S(*item) }
},
Self::First(items) => {
let items = items
.into_vec()
.into_iter()
.map(|item| quote! { #S(item), })
.collect::<TokenStream>();
quote_append! { ts
- ::time::format_description::FormatItem::First { 0: &[#S(items)] }
+ ::time::format_description::BorrowedFormatItem::First { 0: &[#S(items)] }
}
}
}
}
}
diff --git a/third_party/rust/time-macros/src/format_description/public/modifier.rs b/third_party/rust/time-macros/src/format_description/public/modifier.rs
index 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
@@ -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
- ),+ $(,)?}
+ ),* $(,)?}
) => {
$(#[$struct_attr])*
$struct_vis struct $struct_name {$(
$(#[$field_attr])*
$field_vis $field_name: $field_ty
- ),+}
+ ),*}
impl ToTokenTree for $struct_name {
fn into_token_tree(self) -> TokenTree {
let mut tokens = TokenStream::new();
- let Self {$($field_name),+} = self;
+ let Self {$($field_name),*} = self;
quote_append! { tokens
let mut value = ::time::format_description::modifier::$struct_name::default();
};
$(
quote_append!(tokens value.$field_name =);
$field_name.append_to(&mut tokens);
quote_append!(tokens ;);
- )+
+ )*
quote_append!(tokens value);
proc_macro::TokenTree::Group(proc_macro::Group::new(
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,
}
}
+
+to_tokens! {
+ pub(crate) struct End {}
+}
diff --git a/third_party/rust/time-macros/src/helpers/mod.rs b/third_party/rust/time-macros/src/helpers/mod.rs
index 56300b3e65..0cca2002de 100644
--- a/third_party/rust/time-macros/src/helpers/mod.rs
+++ b/third_party/rust/time-macros/src/helpers/mod.rs
@@ -1,16 +1,17 @@
#[cfg(any(feature = "formatting", feature = "parsing"))]
mod string;
use std::iter::Peekable;
use std::str::FromStr;
+use num_conv::prelude::*;
use proc_macro::{token_stream, Span, TokenTree};
use time_core::util::{days_in_year, is_leap_year};
use crate::Error;
#[cfg(any(feature = "formatting", feature = "parsing"))]
pub(crate) fn get_string_literal(
mut tokens: impl Iterator<Item = TokenTree>,
) -> Result<(Span, Vec<u8>), 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;
- ((ordinal + adj_year + div_floor!(adj_year, 4) - div_floor!(adj_year, 100)
+ (ordinal + adj_year + div_floor!(adj_year, 4) - div_floor!(adj_year, 100)
+ div_floor!(adj_year, 400)
+ 6)
- .rem_euclid(7)) as _
+ .rem_euclid(7)
+ .cast_unsigned()
+ .truncate()
}
pub(crate) fn days_in_year_month(year: i32, month: u8) -> u8 {
- [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][month as usize - 1]
- + (month == 2 && is_leap_year(year)) as u8
+ [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][month.extend::<usize>() - 1]
+ + u8::from(month == 2 && is_leap_year(year))
}
pub(crate) fn ywd_to_yo(year: i32, week: u8, iso_weekday_number: u8) -> (i32, u16) {
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) {
- let ordinal = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334][month as usize - 1]
- + (month > 2 && is_leap_year(year)) as u16;
+ let ordinal = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334]
+ [month.extend::<usize>() - 1]
+ + u16::from(month > 2 && is_leap_year(year));
(year, ordinal + u16::from(day))
}
diff --git a/third_party/rust/time-macros/src/lib.rs b/third_party/rust/time-macros/src/lib.rs
index 0e8568cdad..65e24d7d3b 100644
--- a/third_party/rust/time-macros/src/lib.rs
+++ b/third_party/rust/time-macros/src/lib.rs
@@ -1,44 +1,19 @@
-#![deny(
- anonymous_parameters,
- clippy::all,
- illegal_floating_point_literal_pattern,
- late_bound_lifetime_arguments,
- path_statements,
- patterns_in_fns_without_body,
- rust_2018_idioms,
- trivial_casts,
- trivial_numeric_casts,
- unreachable_pub,
- unsafe_code,
- unused_extern_crates
-)]
-#![warn(
- clippy::dbg_macro,
- clippy::decimal_literal_representation,
- clippy::get_unwrap,
- clippy::nursery,
- clippy::print_stdout,
- clippy::todo,
- clippy::unimplemented,
- clippy::unnested_or_patterns,
- clippy::unwrap_used,
- clippy::use_debug,
- single_use_lifetimes,
- unused_qualifications,
- variant_size_differences
-)]
#![allow(
- clippy::missing_const_for_fn, // useless in proc macro
- clippy::redundant_pub_crate, // suggests bad style
- clippy::option_if_let_else, // suggests terrible code
+ clippy::missing_const_for_fn, // irrelevant for proc macros
+ clippy::missing_docs_in_private_items, // TODO remove
+ clippy::std_instead_of_core, // irrelevant for proc macros
+ clippy::std_instead_of_alloc, // irrelevant for proc macros
+ clippy::alloc_instead_of_core, // irrelevant for proc macros
+ missing_docs, // TODO remove
)]
+
#[allow(unused_macros)]
macro_rules! bug {
() => { compile_error!("provide an error message to help fix a possible bug") };
($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),
+ #[cfg_attr(not(feature = "serde"), allow(dead_code))]
ModuleName(Ident),
}
#[cfg(any(feature = "formatting", feature = "parsing"))]
fn parse_format_description_version<const NO_EQUALS_IS_MOD_NAME: bool>(
iter: &mut Peekable<proc_macro::token_stream::IntoIter>,
) -> Result<Option<VersionOrModuleName>, 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::<false>(&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! {{
- const DESCRIPTION: &[::time::format_description::FormatItem<'_>] = &[#S(
+ const DESCRIPTION: &[::time::format_description::BorrowedFormatItem<'_>] = &[#S(
items
.into_iter()
.map(|item| quote! { #S(item), })
.collect::<TokenStream>()
)];
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! {
- const ITEMS: &[::time::format_description::FormatItem<'_>] = &[#S(items)];
+ const ITEMS: &[::time::format_description::BorrowedFormatItem<'_>]
+ = &[#S(items)];
ITEMS
};
(items, String::from_utf8_lossy(&format_string).into_owned())
}
// path
Some(_) => {
let tokens = tokens.collect::<TokenStream>();
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,12 +1,13 @@
use std::iter::Peekable;
+use num_conv::prelude::*;
use proc_macro::{token_stream, Span, TokenTree};
use time_core::convert::*;
use crate::helpers::{consume_any_ident, consume_number, consume_punct};
use crate::to_tokens::ToTokenTree;
use crate::Error;
pub(crate) struct Offset {
pub(crate) hours: i8,
pub(crate) minutes: i8,
@@ -45,52 +46,54 @@ pub(crate) fn parse(chars: &mut Peekable<token_stream::IntoIter>) -> Result<Offs
minutes_span = min.0;
minutes = min.1;
if consume_punct(':', chars).is_ok() {
let sec = consume_number::<i8>("second", chars)?;
seconds_span = sec.0;
seconds = sec.1;
}
}
- if hours >= 24 {
+ if hours > 25 {
Err(Error::InvalidComponent {
name: "hour",
value: hours.to_string(),
span_start: Some(hours_span),
span_end: Some(hours_span),
})
- } else if minutes >= Minute.per(Hour) as _ {
+ } else if minutes >= Minute::per(Hour).cast_signed() {
Err(Error::InvalidComponent {
name: "minute",
value: minutes.to_string(),
span_start: Some(minutes_span),
span_end: Some(minutes_span),
})
- } else if seconds >= Second.per(Minute) as _ {
+ } else if seconds >= Second::per(Minute).cast_signed() {
Err(Error::InvalidComponent {
name: "second",
value: seconds.to_string(),
span_start: Some(seconds_span),
span_end: Some(seconds_span),
})
} else {
Ok(Offset {
hours: sign * hours,
minutes: sign * minutes,
seconds: sign * seconds,
})
}
}
impl ToTokenTree for Offset {
fn into_token_tree(self) -> TokenTree {
quote_group! {{
- const OFFSET: ::time::UtcOffset = ::time::UtcOffset::__from_hms_unchecked(
- #(self.hours),
- #(self.minutes),
- #(self.seconds),
- );
+ const OFFSET: ::time::UtcOffset = unsafe {
+ ::time::UtcOffset::__from_hms_unchecked(
+ #(self.hours),
+ #(self.minutes),
+ #(self.seconds),
+ )
+ };
OFFSET
}}
}
}
diff --git a/third_party/rust/time-macros/src/quote.rs b/third_party/rust/time-macros/src/quote.rs
index 4d3dcbca03..8603f4fa46 100644
--- a/third_party/rust/time-macros/src/quote.rs
+++ b/third_party/rust/time-macros/src/quote.rs
@@ -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,
))]);
};
}
+#[allow(unused_macro_rules)] // Varies by feature flag combination.
macro_rules! quote_inner {
// Base case
($ts:ident) => {};
// Single or double symbols
($ts:ident :: $($tail:tt)*) => { sym!($ts ':' ':'); quote_inner!($ts $($tail)*); };
- ($ts:ident .. $($tail:tt)*) => { sym!($ts '.' '.'); quote_inner!($ts $($tail)*); };
($ts:ident : $($tail:tt)*) => { sym!($ts ':'); quote_inner!($ts $($tail)*); };
($ts:ident = $($tail:tt)*) => { sym!($ts '='); quote_inner!($ts $($tail)*); };
($ts:ident ; $($tail:tt)*) => { sym!($ts ';'); quote_inner!($ts $($tail)*); };
($ts:ident , $($tail:tt)*) => { sym!($ts ','); quote_inner!($ts $($tail)*); };
($ts:ident . $($tail:tt)*) => { sym!($ts '.'); quote_inner!($ts $($tail)*); };
($ts:ident & $($tail:tt)*) => { sym!($ts '&'); quote_inner!($ts $($tail)*); };
- ($ts:ident << $($tail:tt)*) => { sym!($ts '<' '<'); quote_inner!($ts $($tail)*); };
($ts:ident < $($tail:tt)*) => { sym!($ts '<'); quote_inner!($ts $($tail)*); };
($ts:ident >> $($tail:tt)*) => { sym!($ts '>' '>'); quote_inner!($ts $($tail)*); };
($ts:ident > $($tail:tt)*) => { sym!($ts '>'); quote_inner!($ts $($tail)*); };
($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
@@ -66,54 +66,56 @@ pub(crate) fn parse(chars: &mut Peekable<token_stream::IntoIter>) -> Result<Time
span_start: Some(hour_span),
span_end: Some(period_span.unwrap_or(hour_span)),
});
}
(12, Period::Am) => 0,
(12, Period::Pm) => 12,
(hour, Period::Am | Period::_24) => hour,
(hour, Period::Pm) => hour + 12,
};
- if hour >= Hour.per(Day) {
+ if hour >= Hour::per(Day) {
Err(Error::InvalidComponent {
name: "hour",
value: hour.to_string(),
span_start: Some(hour_span),
span_end: Some(period_span.unwrap_or(hour_span)),
})
- } else if minute >= Minute.per(Hour) {
+ } else if minute >= Minute::per(Hour) {
Err(Error::InvalidComponent {
name: "minute",
value: minute.to_string(),
span_start: Some(minute_span),
span_end: Some(minute_span),
})
- } else if second >= Second.per(Minute) as _ {
+ } else if second >= Second::per(Minute) as _ {
Err(Error::InvalidComponent {
name: "second",
value: second.to_string(),
span_start: Some(second_span),
span_end: Some(second_span),
})
} else {
Ok(Time {
hour,
minute,
second: second.trunc() as _,
- nanosecond: (second.fract() * Nanosecond.per(Second) as f64).round() as _,
+ nanosecond: (second.fract() * Nanosecond::per(Second) as f64).round() as _,
})
}
}
impl ToTokenTree for Time {
fn into_token_tree(self) -> TokenTree {
quote_group! {{
- const TIME: ::time::Time = ::time::Time::__from_hms_nanos_unchecked(
- #(self.hour),
- #(self.minute),
- #(self.second),
- #(self.nanosecond),
- );
+ const TIME: ::time::Time = unsafe {
+ ::time::Time::__from_hms_nanos_unchecked(
+ #(self.hour),
+ #(self.minute),
+ #(self.second),
+ #(self.nanosecond),
+ )
+ };
TIME
}}
}
}
diff --git a/third_party/rust/time-macros/src/to_tokens.rs b/third_party/rust/time-macros/src/to_tokens.rs
index 7e73211533..7802158cea 100644
--- a/third_party/rust/time-macros/src/to_tokens.rs
+++ b/third_party/rust/time-macros/src/to_tokens.rs
@@ -1,14 +1,15 @@
use std::num::NonZeroU16;
use proc_macro::{Group, Ident, Literal, Punct, Span, TokenStream, TokenTree};
+/// Turn a type into a [`TokenStream`].
pub(crate) trait ToTokenStream: Sized {
fn append_to(self, ts: &mut TokenStream);
}
pub(crate) trait ToTokenTree: Sized {
fn into_token_tree(self) -> TokenTree;
}
impl<T: ToTokenTree> ToTokenStream for T {
fn append_to(self, ts: &mut TokenStream) {
diff --git a/third_party/rust/time/.cargo-checksum.json b/third_party/rust/time/.cargo-checksum.json
index 3b5423f531..9997a5d1af 100644
--- a/third_party/rust/time/.cargo-checksum.json
+++ b/third_party/rust/time/.cargo-checksum.json
@@ -1 +1 @@
-{"files":{"Cargo.toml":"a011e69d5ae4484afdc3df407f6fcfdc91461b412730302a3d4fb4f4f731fe3f","LICENSE-Apache":"b8929fea28678da67251fb2daf9438f67503814211051861612441806d8edb05","LICENSE-MIT":"04620bf27e4a643dd47bf27652320c205acdb776c1f9f24bb8c3bfaba10804c5","README.md":"91fdb919a41d2c1ee240a66d548adf1e1a40db95bcfe3adaf8c55f37c2afe1e9","src/date.rs":"5c0ae4a98530dd954c19b972df517b80bd6f35c73747e422b55ea0162d4c5fcf","src/date_time.rs":"37c97a93ca15be9b9bbd64b497429c430e91b6bcf51a50f571f79646b4348707","src/duration.rs":"469e62e0b545f45b99c37a7033c269474a932b52a2dc9399d62bf7877be1a8ce","src/error/component_range.rs":"26a1aa4ea2d0f9887efcbe9584d5aa14b1e5d37525a52dc9f18e1e282599625d","src/error/conversion_range.rs":"972abb765370070de01e2fc2e1bb1e80808a069e6213577d7beaca02e1d707c3","src/error/different_variant.rs":"107bef7b3addd7108b36a2da8389f611d4482f34a5b63429841141e05c8cb30c","src/error/format.rs":"d87846c2ac62dec421402ea21e5d2a8d73add6658df4ac914067a4b43cb0ef20","src/error/indeterminate_offset.rs":"1f52f9ea107847fa781399cfcc8046451d70155fb497486c80b2138f82782941","src/error/invalid_format_description.rs":"cf617348b55d9c3273060fa2d99bd4eda215452270025f2b6caef6ef9f387af5","src/error/invalid_variant.rs":"b653a3e6e902f06cb9f2e0366c4da84b92e8bdb03164c2f8cb15fe66415706e4","src/error/mod.rs":"15fb848b1919d9cfb50fb9091abfcea6a8c7db5a2fcd6cb8f32c4af5f1ea4464","src/error/parse.rs":"3bdc8201a14469d2cc7a12a295058569098f9cfc9bd1e8fc9f526ada8298e4f8","src/error/parse_from_description.rs":"990359eb5fcb64c1ee363b044147b7330a92a4cb7373dc2f17f6fd3bcc6411a0","src/error/try_from_parsed.rs":"8c227be52653a1d33af01a8024c0fc56f1f9803f08ef01487a7eaa5833adbb57","src/ext.rs":"f31cdcf38c23a0111524ae431420f299d4d4735d99fc9a873d3472a3699de7ef","src/format_description/borrowed_format_item.rs":"afab66e65a84895751d3557fc5b8a3a5e63f9c483a6a534aa4f86fd2a5145f0b","src/format_description/component.rs":"289469371588f24de6c7afdd40e7ce65f6b08c3e05434900eafdca7dde59ab07","src/format_description/mod.rs":"955a227e9bb13e3085a43457bf8028085db92c0266b6573ddf1e12df3b937c0f","src/format_description/modifier.rs":"5c6330b3557a156d2acfd4eb454783a41a6edf62c5046e2ca60dc060caf31451","src/format_description/owned_format_item.rs":"419f5354bf504562c9225dfe90b61eee9bc959211a86a327197b4f54283da775","src/format_description/parse/ast.rs":"c20fedf0314a2ede5e3ee66024534ca8a65a2b623959386872250e19d5d43b6e","src/format_description/parse/format_item.rs":"4639e23fb86dbbef6d764e8279cc43dd5f6e09d8b14b277e6f6b9bce81f5c3ff","src/format_description/parse/lexer.rs":"c10105640a618e1e850eb6e4fd888c47d881b3f85bde691fdf204199a693e127","src/format_description/parse/mod.rs":"210cd68a37b5cbbc6a6e3b3d5161f03ad94b2902bb01899d0c02d0278f420c8c","src/format_description/well_known/iso8601.rs":"8313905039a637d4d132f8318a59c06246e7b61550b4e4bc7d129232ac022e43","src/format_description/well_known/iso8601/adt_hack.rs":"59a5182dc200a26654944a64a81488a55c7a387485f219371503a010c751e338","src/format_description/well_known/rfc2822.rs":"36c23394724ae12250d4193cab26887a6ff8f82ca441ea6b0d03c4f1c928b3dd","src/format_description/well_known/rfc3339.rs":"1a6318dffd3ebb6ac7cf96eae3d9b1eb44b1089cf4284fa6a7e935c6fcf1b43c","src/formatting/formattable.rs":"fe75a835d20f144faf8f1297d9b501e72fcce321c7dc1077805e4a2b2f9b9390","src/formatting/iso8601.rs":"3dc83bf234b60e80ab499bf3ec490b2772d69a02b452f93cbc8e843ebf340fc2","src/formatting/mod.rs":"b4f98455609968a28e6479077d01eec60e3331064dbcd453f29c6d5a768d9857","src/instant.rs":"f1724e49b173b16b08818bfd06133ce4f61da7df286ff61982113cc184efe1c0","src/lib.rs":"be86048ca1c4ab497384edd9507b41c5683d946b3149ff978277c1323cbc2889","src/macros.rs":"eb9e02a1f97bb8befab7bc27c937136817e4f65e0b3e040a81394ae938980558","src/month.rs":"91b20ea12b1a36a9ddb828279572c6098b8504a048627088137963a612b572b8","src/offset_date_time.rs":"288d7a34eecbbd9345e13804302bcb55df716fdef0fafe1d2d06e52c0d77829e","src/parsing/combinator/mod.rs":"b342fbd95dd986309d81e8910363920ba6db00958b459f6d97f57da3ae3e550d","src/parsing/combinator/rfc/iso8601.rs":"13289a0d58de273327830a3001167a8964edc5045486301efdf3ddc2e4079c32","src/parsing/combinator/rfc/mod.rs":"f30b75d248f5ae92c27646d504703f5489185afb76c998cc4375437b3d15c822","src/parsing/combinator/rfc/rfc2234.rs":"08e2813c6d40c0dae881875fe0417ae06886c73679256587e33186e46b3c3bae","src/parsing/combinator/rfc/rfc2822.rs":"2aff3a6a2778bc806031cff92ad2f43f0874620b5d484b5b39ee2d2507212f06","src/parsing/component.rs":"09008cf9d08c4b0c3cb051b986291a29e977a780bb1455f9c33e472db983e8da","src/parsing/iso8601.rs":"8dd99317a6fcde7d85f8bf5c492675efde751731b687b0cde0d5653cb129743a","src/parsing/mod.rs":"37082ac824c6c3f4900766a0a3140dc7aa46b3f85cb6098f11da7da333e421b0","src/parsing/parsable.rs":"d1b3c001f57c735af395553d35e76f9342a83da87b5a843d1eb015807a076db9","src/parsing/parsed.rs":"5cafd2220fe325817b8b65729274a0ca7c741f4579d99bc888cb1997436ef127","src/parsing/shim.rs":"46efc374bc3129e28936a850143fff8e42aafe10c69ebbb904195aaeca26adc9","src/primitive_date_time.rs":"ce557a9db6d7ed663ff78d62a60af5ee8a287f04f6fc979e81047c339d50495a","src/quickcheck.rs":"94640161a21319b75c9b31a6bbc6e34a4d573c20606b954db1bd12ddef366af8","src/rand.rs":"889c98938427a4885036673df8fcebd84c7cc20fb4b3ca82c447ff4b977c8a15","src/serde/iso8601.rs":"997bbf4fe4018f8fdc9335ac863b543fb24a58b2dee394615505a24311331516","src/serde/mod.rs":"a575be28adaf89ff5b1476b367e2019ea09d68368f227d211bc6f33f192bc1e3","src/serde/rfc2822.rs":"fe97aa1311037a362eb477fe8c6729b3b85ff2d0afab7148f10f64d109081f90","src/serde/rfc3339.rs":"9835c8b8fb24b53657769b81a71188fe4261e5869917779e1702b3a0aa854654","src/serde/timestamp.rs":"30971ad5d1fef11e396eee48d476b828ed4e99f6eac587383b864dd95c120fe4","src/serde/visitor.rs":"c5293181f337ab09ae98ce4ef41eae558ae5e3b86f961e4a0c9c93cb034647ed","src/sys/local_offset_at/imp.rs":"4b6e57f02566364270ac9b7e1540290a5658a296f7e911f988264d103e420326","src/sys/local_offset_at/mod.rs":"95b042824b414b3021eda2bcf0821afc529bfd8d4cfcad0b893edb197e48461b","src/sys/local_offset_at/unix.rs":"339ab502e121c24c6ea617f444a58fb7e23cf5afd13c5f7a52eda6d69591d580","src/sys/local_offset_at/wasm_js.rs":"e49ef256c874d6b8d15ef264a66c0b837ac42cd0683e38f3f31af2c2e8fca459","src/sys/local_offset_at/windows.rs":"0836e20249421b1f32e77f0ce4be0d3db30be00478f4c56fda9ddbff0bbb0c5d","src/sys/mod.rs":"0a43797e55e986233a71f1cc4b3a21997da42bc15db7d912373296cd535e49bc","src/tests.rs":"38d1f794892e6ab3fece55839a8e4ab6d0d2c325323310eda32144eb7240bf59","src/time.rs":"197c53ef2b49f73c363eabe2332ffd4eaba18f91f2d17070e8d568069a977c64","src/utc_offset.rs":"ce39c34ec5419a1bf51f7b8401e38a4e0daab7e827fe2fd239fae8089a212c7e","src/util.rs":"1fff6c7d712a4d2665cca55db9c142185cc13afa20f925912cb85abbcc366938","src/weekday.rs":"0a9f79b6aef6bb085204216d0be1c7666426c589c3263b63384c4b74e8b54658"},"package":"59e399c068f43a5d116fedaf73b203fa4f9c519f17e2b34f63221d3792f81446"}
\ No newline at end of file
+{"files":{"Cargo.toml":"bd60027c57b6255a65c218363b8e018ccbc7339af846b30ecc14c7781e9dc701","LICENSE-Apache":"edd65bdd88957a205c47d53fa499eed8865a70320f0f03f6391668cb304ea376","LICENSE-MIT":"231c837c45eb53f108fb48929e488965bc4fcc14e9ea21d35f50e6b99d98685b","README.md":"36c735ebe90cdc962dec7e240607a052088697d0cefd555f093746039b0943cd","src/date.rs":"a20df0cc036d1c09f5554bf2c52a7dc7ceb2d08af355a5079282b3660c1bf062","src/duration.rs":"dc394786d19a1fae779ebfbd14e1502a5701756a500224fc82f116ecfeef39b2","src/error/component_range.rs":"26a1aa4ea2d0f9887efcbe9584d5aa14b1e5d37525a52dc9f18e1e282599625d","src/error/conversion_range.rs":"972abb765370070de01e2fc2e1bb1e80808a069e6213577d7beaca02e1d707c3","src/error/different_variant.rs":"107bef7b3addd7108b36a2da8389f611d4482f34a5b63429841141e05c8cb30c","src/error/format.rs":"d87846c2ac62dec421402ea21e5d2a8d73add6658df4ac914067a4b43cb0ef20","src/error/indeterminate_offset.rs":"1f52f9ea107847fa781399cfcc8046451d70155fb497486c80b2138f82782941","src/error/invalid_format_description.rs":"cf617348b55d9c3273060fa2d99bd4eda215452270025f2b6caef6ef9f387af5","src/error/invalid_variant.rs":"b653a3e6e902f06cb9f2e0366c4da84b92e8bdb03164c2f8cb15fe66415706e4","src/error/mod.rs":"a30edbd2cdc701d0327291ef1201aa1531ab8bb1a1318945085ab71f8918bb16","src/error/parse.rs":"a3f23c10cab2e4ce159c5b3d5774d54214bd0098b70a59c48a2407777cdee9e5","src/error/parse_from_description.rs":"2de1f5b5af3d9bb358cee1c66a2721a78ee99f0ee5e0c06f159e62de7a294a5f","src/error/try_from_parsed.rs":"8c227be52653a1d33af01a8024c0fc56f1f9803f08ef01487a7eaa5833adbb57","src/ext/digit_count.rs":"d32af3cac57077870f6b31c5bd6e0da3a530a7c713aaebdcd27125d9fd8857a3","src/ext/instant.rs":"7598e0fab29b6a9c2a7d1f5fa761ed7d2d8b54deac801189fec652a358ed7e08","src/ext/mod.rs":"dc70d230a170ddd1bf7798703d9ff7cac419aaadc82b5f8dbf46d5320322e2c5","src/ext/numerical_duration.rs":"d6c74898e4f6025158ef8daf35abe53268161db2c18d12e718fec17cfe442d6d","src/ext/numerical_std_duration.rs":"0dc24fc48c3eb94c22aca75423e652e13322262c63edaf5393a28bc8470d6082","src/format_description/borrowed_format_item.rs":"529cb5c91a0ff08a6f8615ccf1289c9b5f01996f691db1bd1cb5a451c25ead4c","src/format_description/component.rs":"b65c0ca896ea6ec8dbfc7570c69849c88cbba6031a2847dcfdce06d721a59397","src/format_description/mod.rs":"640958320fcda04287a84767b6fe2f941a0e5f7498b58a531f6dffbfd0ed1757","src/format_description/modifier.rs":"450e6fc64353f80304c2b616bf3e8c58f1ae02e0a2875e946cf0ea27e87a4e10","src/format_description/owned_format_item.rs":"8834a705f3f0c61a1851ffb4b897766b4216ae2dbac6ae34ad0d4aa3fd46a064","src/format_description/parse/ast.rs":"f96b423629e265d85f4068c7590a27405cb5b4275bf9f547e674f337b281a58e","src/format_description/parse/format_item.rs":"0a5b013e5dbb226a90bcda533866d9a141d1270412f44a42ba0dc053479e782c","src/format_description/parse/lexer.rs":"fae06f81ace377f716857b60bb5c0d6591123478a99b4c9823f4b19bb3f73af1","src/format_description/parse/mod.rs":"99ac90c3cdb73ce52b3e2866c4f3f7dae0557b88bce5846161c901e11fbd80c4","src/format_description/well_known/iso8601.rs":"30b2f495044ab4e1f3ff6a317a8b4ca2ffb46b7cc23428eca8b685c0f16518cc","src/format_description/well_known/iso8601/adt_hack.rs":"8f1d5f4a0959070ab96343868086adfa6fa3f5a5823f50a111c824b4a9bcd39b","src/format_description/well_known/rfc2822.rs":"36c23394724ae12250d4193cab26887a6ff8f82ca441ea6b0d03c4f1c928b3dd","src/format_description/well_known/rfc3339.rs":"1a6318dffd3ebb6ac7cf96eae3d9b1eb44b1089cf4284fa6a7e935c6fcf1b43c","src/formatting/formattable.rs":"7cd6416af947b92316b6505c2d3c45b163c87257302ae18868b1f83fbba034a4","src/formatting/iso8601.rs":"1d265461a515406c1253350b5eb870b5416f9ce76cdf96a8fbfb7c4b57dcb623","src/formatting/mod.rs":"115af930b598ed77a9b55ba8f7d89befefb1f21cec960ebd2c315188a25ba07d","src/instant.rs":"96a5aba377443945c1a75d669347711c40b33ea842f7d609eb3a7572b6ce64bf","src/internal_macros.rs":"cf2479614cc66f3b9c533f5f5c4584fe90136c7ad4d0c2137d5aa386ffa5319f","src/lib.rs":"f2fdfeaf6f85d71cbb875d54645964997a741d9c012cecdfda9c92ee82f01d15","src/macros.rs":"bea9e61c00ac2b523357300a51830d32e46f03456fc03d18fe26928042e10a2d","src/month.rs":"cee05aaad0088a38f0bec22dc913590c18d3bc74208aa72394512424677ffd25","src/offset_date_time.rs":"7d2a476c69c7f3c16c466347a9dff2c6d9687e82044b7c39034218f14adfa404","src/parsing/combinator/mod.rs":"e49d14f7d0c38a847d9f5f2e63d466464641d365f1d39c48d1a455ebe6632ec5","src/parsing/combinator/rfc/iso8601.rs":"8b819a8e3ef4b160b1bce94cad6534e3c0acbeef98cfc494606815468781c19a","src/parsing/combinator/rfc/mod.rs":"f30b75d248f5ae92c27646d504703f5489185afb76c998cc4375437b3d15c822","src/parsing/combinator/rfc/rfc2234.rs":"08e2813c6d40c0dae881875fe0417ae06886c73679256587e33186e46b3c3bae","src/parsing/combinator/rfc/rfc2822.rs":"2aff3a6a2778bc806031cff92ad2f43f0874620b5d484b5b39ee2d2507212f06","src/parsing/component.rs":"32d69c4f86e3a2e90dffbd74af8b19ea25dbeaa6f60aa89637f3988d64fd7967","src/parsing/iso8601.rs":"6cc42738e8e8ed5b2800b0fbdd85c120705ec3452483f625c75a5a1e5bf0fcc9","src/parsing/mod.rs":"7d790f512710dc33b9f6f51e4237c04812aafd7d68710df01d02d755062568d7","src/parsing/parsable.rs":"8fa8f9b2653fa5e911a6528fd3f62cd371e3b2bb2c2727447fd2a33381ffed68","src/parsing/parsed.rs":"32289f2495ad6826eab3c745c8df1c2590510c88bb4d7c21b2dbc6bfd664ed35","src/parsing/shim.rs":"46efc374bc3129e28936a850143fff8e42aafe10c69ebbb904195aaeca26adc9","src/primitive_date_time.rs":"faca737cbe28bce420dfb4468021f46968c29abdde449479af225e597441c96e","src/quickcheck.rs":"09baaccea54f77cc60d1c43a726275d15db42e1c8b4ec157d4417d3cded36762","src/rand.rs":"dcedb2473e240c46de00aa4b156d66cb755da9d5fd04adaedb3682cb6a12218f","src/serde/iso8601.rs":"997bbf4fe4018f8fdc9335ac863b543fb24a58b2dee394615505a24311331516","src/serde/mod.rs":"99a93f87c0dd35f818af72228ca74a0952ce4db4d426ddf7c21a0220caee890e","src/serde/rfc2822.rs":"fe97aa1311037a362eb477fe8c6729b3b85ff2d0afab7148f10f64d109081f90","src/serde/rfc3339.rs":"9835c8b8fb24b53657769b81a71188fe4261e5869917779e1702b3a0aa854654","src/serde/timestamp/microseconds.rs":"7b8a0f6e455ddb6c50ed4a8473ea68cf9aacb31af36ccc058b68fa3bb5f3246d","src/serde/timestamp/milliseconds.rs":"df995f05f340f0429697269aa152a39b0beca45e1b4d8e60f4a6bb27f09a4d88","src/serde/timestamp/mod.rs":"20ddd158fe5e491f9f7e74ef074d2bb10c8b02b1fcd399c0429fd796d799aa01","src/serde/timestamp/nanoseconds.rs":"a46b1e60e8b19792a26ebdab33cba53db95cb13e3c4d9675a17b1491e9fb2940","src/serde/visitor.rs":"973ba2826134d09b109ef7c09a80c48ab724bd9051706bfde85e1ba930e00134","src/sys/local_offset_at/imp.rs":"4b6e57f02566364270ac9b7e1540290a5658a296f7e911f988264d103e420326","src/sys/local_offset_at/mod.rs":"95b042824b414b3021eda2bcf0821afc529bfd8d4cfcad0b893edb197e48461b","src/sys/local_offset_at/unix.rs":"ce02c86c4b4588ef3ebfa56bc82ff09e83677fc7679e61843b0bd80c4308fcbc","src/sys/local_offset_at/wasm_js.rs":"7cdacf5548a89a00265764ef07c1abb2ea1a3211f1ec720932b066f1d18b2f20","src/sys/local_offset_at/windows.rs":"90c16515d7ac29961bd0b8af92e38c7999f260bfd3438c9935fee65e8c0cc1e9","src/sys/mod.rs":"0a43797e55e986233a71f1cc4b3a21997da42bc15db7d912373296cd535e49bc","src/tests.rs":"c858af9559784ec4fe82ddcd0f4029b1c519ef7699288995d8fac007feada859","src/time.rs":"418ae2964af95fbbfba070598b09e36475846792c1916d990e20f5481e0ea9e7","src/utc_offset.rs":"0e51f4ebb740f95b0f2b2d956b72ff49dcc993b81e4d14d1d1809fb355be5817","src/util.rs":"34b4d98803dd99d0db813d0e3cda9dcbb235a8315b6cf1438e0048d14d76663a","src/weekday.rs":"76ed201232f03077474c05e53f34b0403ead637c40a5228b9b61b342048f2917"},"package":"5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885"}
\ No newline at end of file
diff --git a/third_party/rust/time/Cargo.toml b/third_party/rust/time/Cargo.toml
index ad2e8c94b3..8f1996f42e 100644
--- a/third_party/rust/time/Cargo.toml
+++ b/third_party/rust/time/Cargo.toml
@@ -4,23 +4,23 @@
# "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.65.0"
+rust-version = "1.67.0"
name = "time"
-version = "0.3.23"
+version = "0.3.36"
authors = [
"Jacob Pratt <open-source@jhpratt.dev>",
"Time contributors",
]
include = [
"src/**/*",
"LICENSE-*",
"README.md",
]
description = "Date and time library. Fully interoperable with the standard library. Mostly compatible with #![no_std]."
@@ -39,128 +39,243 @@ categories = [
"value-formatting",
]
license = "MIT OR Apache-2.0"
repository = "https://github.com/time-rs/time"
[package.metadata.docs.rs]
all-features = true
rustdoc-args = [
"--cfg",
"__time_03_docs",
+ "--generate-link-to-definition",
]
targets = ["x86_64-unknown-linux-gnu"]
[lib]
bench = false
[[test]]
name = "tests"
path = "../tests/main.rs"
[[bench]]
name = "benchmarks"
path = "../benchmarks/main.rs"
harness = false
+[dependencies.deranged]
+version = "0.3.9"
+features = ["powerfmt"]
+default-features = false
+
[dependencies.itoa]
version = "1.0.1"
optional = true
+[dependencies.num-conv]
+version = "0.1.0"
+
+[dependencies.powerfmt]
+version = "0.2.0"
+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"
+version = "1.0.184"
optional = true
default-features = false
[dependencies.time-core]
-version = "=0.1.1"
+version = "=0.1.2"
[dependencies.time-macros]
-version = "=0.2.10"
+version = "=0.2.18"
optional = true
+[dev-dependencies.num-conv]
+version = "0.1.0"
+
[dev-dependencies.quickcheck_macros]
version = "1.0.0"
[dev-dependencies.rand]
version = "0.8.4"
default-features = false
+[dev-dependencies.rstest]
+version = "0.18.2"
+default-features = false
+
+[dev-dependencies.rstest_reuse]
+version = "0.6.0"
+
[dev-dependencies.serde]
-version = "1.0.126"
+version = "1.0.184"
features = ["derive"]
default-features = false
[dev-dependencies.serde_json]
version = "1.0.68"
[dev-dependencies.serde_test]
version = "1.0.126"
[dev-dependencies.time-macros]
-version = "=0.2.10"
+version = "=0.2.18"
[features]
alloc = ["serde?/alloc"]
default = ["std"]
formatting = [
"dep:itoa",
"std",
"time-macros?/formatting",
]
large-dates = ["time-macros?/large-dates"]
local-offset = [
"std",
"dep:libc",
"dep:num_threads",
]
macros = ["dep:time-macros"]
parsing = ["time-macros?/parsing"]
quickcheck = [
"dep:quickcheck",
"alloc",
+ "deranged/quickcheck",
+]
+rand = [
+ "dep:rand",
+ "deranged/rand",
]
-rand = ["dep:rand"]
serde = [
"dep:serde",
"time-macros?/serde",
+ "deranged/serde",
]
serde-human-readable = [
"serde",
"formatting",
"parsing",
]
serde-well-known = [
"serde",
"formatting",
"parsing",
]
-std = ["alloc"]
+std = [
+ "alloc",
+ "deranged/std",
+]
wasm-bindgen = ["dep:js-sys"]
[target."cfg(__ui_tests)".dev-dependencies.trybuild]
version = "1.0.68"
[target."cfg(all(target_family = \"wasm\", not(any(target_os = \"emscripten\", target_os = \"wasi\"))))".dependencies.js-sys]
version = "0.3.58"
optional = true
[target."cfg(bench)".dev-dependencies.criterion]
-version = "0.4.0"
+version = "0.5.1"
default-features = false
[target."cfg(target_family = \"unix\")".dependencies.libc]
version = "0.2.98"
optional = true
[target."cfg(target_family = \"unix\")".dependencies.num_threads]
version = "0.1.2"
optional = true
+
+[lints.clippy]
+all = "warn"
+alloc-instead-of-core = "deny"
+dbg-macro = "warn"
+decimal-literal-representation = "warn"
+explicit-auto-deref = "warn"
+get-unwrap = "warn"
+manual-let-else = "warn"
+missing-docs-in-private-items = "warn"
+missing-enforced-import-renames = "warn"
+nursery = "warn"
+obfuscated-if-else = "warn"
+print-stdout = "warn"
+semicolon-outside-block = "warn"
+std-instead-of-core = "deny"
+todo = "warn"
+undocumented-unsafe-blocks = "deny"
+unimplemented = "warn"
+uninlined-format-args = "warn"
+unnested-or-patterns = "warn"
+unwrap-in-result = "warn"
+unwrap-used = "warn"
+use-debug = "warn"
+
+[lints.clippy.option-if-let-else]
+level = "allow"
+priority = 1
+
+[lints.clippy.redundant-pub-crate]
+level = "allow"
+priority = 1
+
+[lints.rust]
+ambiguous-glob-reexports = "deny"
+clashing-extern-declarations = "deny"
+const-item-mutation = "deny"
+deref-nullptr = "deny"
+drop-bounds = "deny"
+future-incompatible = "deny"
+hidden-glob-reexports = "deny"
+improper-ctypes = "deny"
+improper-ctypes-definitions = "deny"
+invalid-from-utf8 = "deny"
+invalid-macro-export-arguments = "deny"
+invalid-nan-comparisons = "deny"
+invalid-reference-casting = "deny"
+invalid-value = "deny"
+keyword-idents = "warn"
+let-underscore = "warn"
+macro-use-extern-crate = "warn"
+meta-variable-misuse = "warn"
+missing-abi = "warn"
+missing-copy-implementations = "warn"
+missing-debug-implementations = "warn"
+missing-docs = "warn"
+named-arguments-used-positionally = "deny"
+non-ascii-idents = "deny"
+noop-method-call = "warn"
+opaque-hidden-inferred-bound = "deny"
+overlapping-range-endpoints = "deny"
+single-use-lifetimes = "warn"
+suspicious-double-ref-op = "deny"
+temporary-cstring-as-ptr = "deny"
+trivial-casts = "warn"
+trivial-numeric-casts = "warn"
+unconditional-recursion = "deny"
+unnameable-test-items = "deny"
+unreachable-pub = "warn"
+unsafe-op-in-unsafe-fn = "deny"
+unstable-syntax-pre-expansion = "deny"
+unused = "warn"
+unused-import-braces = "warn"
+unused-lifetimes = "warn"
+unused-qualifications = "warn"
+variant-size-differences = "warn"
+
+[lints.rust.unstable-name-collisions]
+level = "warn"
+priority = 1
+
+[lints.rustdoc]
+private-doc-tests = "warn"
+unescaped-backticks = "warn"
diff --git a/third_party/rust/time/LICENSE-Apache b/third_party/rust/time/LICENSE-Apache
index 7646f21e37..c763a0c9de 100644
--- a/third_party/rust/time/LICENSE-Apache
+++ b/third_party/rust/time/LICENSE-Apache
@@ -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.
- Copyright 2022 Jacob Pratt et al.
+ Copyright 2024 Jacob Pratt et al.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
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/LICENSE-MIT b/third_party/rust/time/LICENSE-MIT
index a11a755732..5cc097f1c0 100644
--- a/third_party/rust/time/LICENSE-MIT
+++ b/third_party/rust/time/LICENSE-MIT
@@ -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/README.md b/third_party/rust/time/README.md
index 173b3c4a2b..b2991f93d8 100644
--- a/third_party/rust/time/README.md
+++ b/third_party/rust/time/README.md
@@ -1,13 +1,13 @@
# time
-[![minimum rustc: 1.65](https://img.shields.io/badge/minimum%20rustc-1.65-yellowgreen?logo=rust&style=flat-square)](https://www.whatrustisit.com)
+[![minimum rustc: 1.67](https://img.shields.io/badge/minimum%20rustc-1.67-yellowgreen?logo=rust&style=flat-square)](https://www.whatrustisit.com)
[![version](https://img.shields.io/crates/v/time?color=blue&logo=rust&style=flat-square)](https://crates.io/crates/time)
[![build status](https://img.shields.io/github/actions/workflow/status/time-rs/time/build.yaml?branch=main&style=flat-square)](https://github.com/time-rs/time/actions)
[![codecov](https://codecov.io/gh/time-rs/time/branch/main/graph/badge.svg?token=yt4XSmQNKQ)](https://codecov.io/gh/time-rs/time)
Documentation:
- [latest release](https://docs.rs/time)
- [main branch](https://time-rs.github.io/api/time)
- [book](https://time-rs.github.io/book)
diff --git a/third_party/rust/time/src/date.rs b/third_party/rust/time/src/date.rs
index ad8565a8e1..ed443c5fd1 100644
--- a/third_party/rust/time/src/date.rs
+++ b/third_party/rust/time/src/date.rs
@@ -1,26 +1,41 @@
//! The [`Date`] struct and its associated `impl`s.
-use core::fmt;
+#[cfg(feature = "formatting")]
+use alloc::string::String;
+use core::num::NonZeroI32;
use core::ops::{Add, Sub};
use core::time::Duration as StdDuration;
+use core::{cmp, fmt};
#[cfg(feature = "formatting")]
use std::io;
+use deranged::RangedI32;
+use num_conv::prelude::*;
+use powerfmt::ext::FormatterExt;
+use powerfmt::smart_display::{self, FormatterOptions, Metadata, SmartDisplay};
+
use crate::convert::*;
+use crate::ext::DigitCount;
#[cfg(feature = "formatting")]
use crate::formatting::Formattable;
+use crate::internal_macros::{
+ cascade, const_try, const_try_opt, div_floor, ensure_ranged, expect_opt, impl_add_assign,
+ impl_sub_assign,
+};
#[cfg(feature = "parsing")]
use crate::parsing::Parsable;
use crate::util::{days_in_year, days_in_year_month, is_leap_year, weeks_in_year};
use crate::{error, Duration, Month, PrimitiveDateTime, Time, Weekday};
+type Year = RangedI32<MIN_YEAR, MAX_YEAR>;
+
/// The minimum valid year.
pub(crate) const MIN_YEAR: i32 = if cfg!(feature = "large-dates") {
-999_999
} else {
-9999
};
/// The maximum valid year.
pub(crate) const MAX_YEAR: i32 = if cfg!(feature = "large-dates") {
999_999
} else {
@@ -32,46 +47,57 @@ pub(crate) const MAX_YEAR: i32 = if cfg!(feature = "large-dates") {
/// By default, years between ±9999 inclusive are representable. This can be expanded to ±999,999
/// inclusive by enabling the `large-dates` crate feature. Doing so has performance implications
/// and introduces some ambiguities when parsing.
#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct Date {
/// Bitpacked field containing both the year and ordinal.
// | xx | xxxxxxxxxxxxxxxxxxxxx | xxxxxxxxx |
// | 2 bits | 21 bits | 9 bits |
// | unassigned | year | ordinal |
// The year is 15 bits when `large-dates` is not enabled.
- value: i32,
+ value: NonZeroI32,
}
impl Date {
/// The minimum valid `Date`.
///
/// The value of this may vary depending on the feature flags enabled.
- pub const MIN: Self = Self::__from_ordinal_date_unchecked(MIN_YEAR, 1);
+ // Safety: `ordinal` is not zero.
+ #[allow(clippy::undocumented_unsafe_blocks)]
+ pub const MIN: Self = unsafe { Self::__from_ordinal_date_unchecked(MIN_YEAR, 1) };
/// The maximum valid `Date`.
///
/// The value of this may vary depending on the feature flags enabled.
- pub const MAX: Self = Self::__from_ordinal_date_unchecked(MAX_YEAR, days_in_year(MAX_YEAR));
+ // Safety: `ordinal` is not zero.
+ #[allow(clippy::undocumented_unsafe_blocks)]
+ pub const MAX: Self =
+ unsafe { Self::__from_ordinal_date_unchecked(MAX_YEAR, days_in_year(MAX_YEAR)) };
// region: constructors
/// Construct a `Date` from the year and ordinal values, the validity of which must be
/// guaranteed by the caller.
+ ///
+ /// # Safety
+ ///
+ /// `ordinal` must not be zero. `year` should be in the range `MIN_YEAR..=MAX_YEAR`, but this
+ /// is not a safety invariant.
#[doc(hidden)]
- pub const fn __from_ordinal_date_unchecked(year: i32, ordinal: u16) -> Self {
+ pub const unsafe fn __from_ordinal_date_unchecked(year: i32, ordinal: u16) -> Self {
debug_assert!(year >= MIN_YEAR);
debug_assert!(year <= MAX_YEAR);
debug_assert!(ordinal != 0);
debug_assert!(ordinal <= days_in_year(year));
Self {
- value: (year << 9) | ordinal as i32,
+ // Safety: The caller must guarantee that `ordinal` is not zero.
+ value: unsafe { NonZeroI32::new_unchecked((year << 9) | ordinal as i32) },
}
}
/// Attempt to create a `Date` from the year, month, and day.
///
/// ```rust
/// # use time::{Date, Month};
/// assert!(Date::from_calendar_date(2019, Month::January, 1).is_ok());
/// assert!(Date::from_calendar_date(2019, Month::December, 31).is_ok());
/// ```
@@ -84,113 +110,160 @@ impl Date {
year: i32,
month: Month,
day: u8,
) -> Result<Self, error::ComponentRange> {
/// Cumulative days through the beginning of a month in both common and leap years.
const DAYS_CUMULATIVE_COMMON_LEAP: [[u16; 12]; 2] = [
[0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334],
[0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335],
];
- ensure_value_in_range!(year in MIN_YEAR => MAX_YEAR);
- ensure_value_in_range!(day conditionally in 1 => days_in_year_month(year, month));
+ ensure_ranged!(Year: year);
+ match day {
+ 1..=28 => {}
+ 29..=31 if day <= days_in_year_month(year, month) => {}
+ _ => {
+ return Err(error::ComponentRange {
+ name: "day",
+ minimum: 1,
+ maximum: days_in_year_month(year, month) as _,
+ value: day as _,
+ conditional_range: true,
+ });
+ }
+ }
- Ok(Self::__from_ordinal_date_unchecked(
- year,
- DAYS_CUMULATIVE_COMMON_LEAP[is_leap_year(year) as usize][month as usize - 1]
- + day as u16,
- ))
+ // Safety: `ordinal` is not zero.
+ Ok(unsafe {
+ Self::__from_ordinal_date_unchecked(
+ year,
+ DAYS_CUMULATIVE_COMMON_LEAP[is_leap_year(year) as usize][month as usize - 1]
+ + day as u16,
+ )
+ })
}
/// Attempt to create a `Date` from the year and ordinal day number.
///
/// ```rust
/// # use time::Date;
/// assert!(Date::from_ordinal_date(2019, 1).is_ok());
/// assert!(Date::from_ordinal_date(2019, 365).is_ok());
/// ```
///
/// ```rust
/// # use time::Date;
/// assert!(Date::from_ordinal_date(2019, 366).is_err()); // 2019 isn't a leap year.
/// ```
pub const fn from_ordinal_date(year: i32, ordinal: u16) -> Result<Self, error::ComponentRange> {
- ensure_value_in_range!(year in MIN_YEAR => MAX_YEAR);
- ensure_value_in_range!(ordinal conditionally in 1 => days_in_year(year));
- Ok(Self::__from_ordinal_date_unchecked(year, ordinal))
+ ensure_ranged!(Year: year);
+ match ordinal {
+ 1..=365 => {}
+ 366 if is_leap_year(year) => {}
+ _ => {
+ return Err(error::ComponentRange {
+ name: "ordinal",
+ minimum: 1,
+ maximum: days_in_year(year) as _,
+ value: ordinal as _,
+ conditional_range: true,
+ });
+ }
+ }
+
+ // Safety: `ordinal` is not zero.
+ Ok(unsafe { Self::__from_ordinal_date_unchecked(year, ordinal) })
}
/// Attempt to create a `Date` from the ISO year, week, and weekday.
///
/// ```rust
/// # use time::{Date, Weekday::*};
/// assert!(Date::from_iso_week_date(2019, 1, Monday).is_ok());
/// assert!(Date::from_iso_week_date(2019, 1, Tuesday).is_ok());
/// assert!(Date::from_iso_week_date(2020, 53, Friday).is_ok());
/// ```
///
/// ```rust
/// # use time::{Date, Weekday::*};
/// assert!(Date::from_iso_week_date(2019, 53, Monday).is_err()); // 2019 doesn't have 53 weeks.
/// ```
pub const fn from_iso_week_date(
year: i32,
week: u8,
weekday: Weekday,
) -> Result<Self, error::ComponentRange> {
- ensure_value_in_range!(year in MIN_YEAR => MAX_YEAR);
- ensure_value_in_range!(week conditionally in 1 => weeks_in_year(year));
+ ensure_ranged!(Year: year);
+ match week {
+ 1..=52 => {}
+ 53 if week <= weeks_in_year(year) => {}
+ _ => {
+ return Err(error::ComponentRange {
+ name: "week",
+ minimum: 1,
+ maximum: weeks_in_year(year) as _,
+ value: week as _,
+ conditional_range: true,
+ });
+ }
+ }
let adj_year = year - 1;
let raw = 365 * adj_year + div_floor!(adj_year, 4) - div_floor!(adj_year, 100)
+ div_floor!(adj_year, 400);
let jan_4 = match (raw % 7) as i8 {
-6 | 1 => 8,
-5 | 2 => 9,
-4 | 3 => 10,
-3 | 4 => 4,
-2 | 5 => 5,
-1 | 6 => 6,
_ => 7,
};
let ordinal = week as i16 * 7 + weekday.number_from_monday() as i16 - jan_4;
Ok(if ordinal <= 0 {
- Self::__from_ordinal_date_unchecked(
- year - 1,
- (ordinal as u16).wrapping_add(days_in_year(year - 1)),
- )
+ // Safety: `ordinal` is not zero.
+ unsafe {
+ Self::__from_ordinal_date_unchecked(
+ year - 1,
+ (ordinal as u16).wrapping_add(days_in_year(year - 1)),
+ )
+ }
} else if ordinal > days_in_year(year) as i16 {
- Self::__from_ordinal_date_unchecked(year + 1, ordinal as u16 - days_in_year(year))
+ // Safety: `ordinal` is not zero.
+ unsafe {
+ Self::__from_ordinal_date_unchecked(year + 1, ordinal as u16 - days_in_year(year))
+ }
} else {
- Self::__from_ordinal_date_unchecked(year, ordinal as _)
+ // Safety: `ordinal` is not zero.
+ unsafe { Self::__from_ordinal_date_unchecked(year, ordinal as _) }
})
}
/// Create a `Date` from the Julian day.
///
/// The algorithm to perform this conversion is derived from one provided by Peter Baum; it is
/// freely available [here](https://www.researchgate.net/publication/316558298_Date_Algorithms).
///
/// ```rust
/// # use time::Date;
/// # use time_macros::date;
/// assert_eq!(Date::from_julian_day(0), Ok(date!(-4713 - 11 - 24)));
/// assert_eq!(Date::from_julian_day(2_451_545), Ok(date!(2000 - 01 - 01)));
/// assert_eq!(Date::from_julian_day(2_458_485), Ok(date!(2019 - 01 - 01)));
/// assert_eq!(Date::from_julian_day(2_458_849), Ok(date!(2019 - 12 - 31)));
/// ```
#[doc(alias = "from_julian_date")]
pub const fn from_julian_day(julian_day: i32) -> Result<Self, error::ComponentRange> {
- ensure_value_in_range!(
- julian_day in Self::MIN.to_julian_day() => Self::MAX.to_julian_day()
- );
+ type JulianDay = RangedI32<{ Date::MIN.to_julian_day() }, { Date::MAX.to_julian_day() }>;
+ ensure_ranged!(JulianDay: julian_day);
Ok(Self::from_julian_day_unchecked(julian_day))
}
/// Create a `Date` from the Julian day.
///
/// This does not check the validity of the provided Julian day, and as such may result in an
/// internally invalid value.
#[doc(alias = "from_julian_date_unchecked")]
pub(crate) const fn from_julian_day_unchecked(julian_day: i32) -> Self {
debug_assert!(julian_day >= Self::MIN.to_julian_day());
@@ -216,35 +289,36 @@ impl Date {
};
if is_leap_year(year) {
ordinal += 60;
cascade!(ordinal in 1..367 => year);
} else {
ordinal += 59;
cascade!(ordinal in 1..366 => year);
}
- Self::__from_ordinal_date_unchecked(year, ordinal)
+ // Safety: `ordinal` is not zero.
+ unsafe { Self::__from_ordinal_date_unchecked(year, ordinal) }
}
// endregion constructors
// region: getters
/// Get the year of the date.
///
/// ```rust
/// # use time_macros::date;
/// assert_eq!(date!(2019 - 01 - 01).year(), 2019);
/// assert_eq!(date!(2019 - 12 - 31).year(), 2019);
/// assert_eq!(date!(2020 - 01 - 01).year(), 2020);
/// ```
pub const fn year(self) -> i32 {
- self.value >> 9
+ self.value.get() >> 9
}
/// Get the month.
///
/// ```rust
/// # use time::Month;
/// # use time_macros::date;
/// assert_eq!(date!(2019 - 01 - 01).month(), Month::January);
/// assert_eq!(date!(2019 - 12 - 31).month(), Month::December);
/// ```
@@ -309,21 +383,21 @@ impl Date {
/// Get the day of the year.
///
/// The returned value will always be in the range `1..=366` (`1..=365` for common years).
///
/// ```rust
/// # use time_macros::date;
/// assert_eq!(date!(2019 - 01 - 01).ordinal(), 1);
/// assert_eq!(date!(2019 - 12 - 31).ordinal(), 365);
/// ```
pub const fn ordinal(self) -> u16 {
- (self.value & 0x1FF) as _
+ (self.value.get() & 0x1FF) as _
}
/// Get the ISO 8601 year and week number.
pub(crate) const fn iso_year_week(self) -> (i32, u8) {
let (year, ordinal) = self.to_ordinal_date();
match ((ordinal + 10 - self.weekday().number_from_monday() as u16) / 7) as _ {
0 => (year - 1, weeks_in_year(year - 1)),
53 if weeks_in_year(year) == 52 => (year + 1, 1),
week => (year, week),
@@ -476,28 +550,30 @@ impl Date {
/// Some(date!(2019 - 02 - 01))
/// );
/// assert_eq!(
/// date!(2019 - 12 - 31).next_day(),
/// Some(date!(2020 - 01 - 01))
/// );
/// assert_eq!(Date::MAX.next_day(), None);
/// ```
pub const fn next_day(self) -> Option<Self> {
if self.ordinal() == 366 || (self.ordinal() == 365 && !is_leap_year(self.year())) {
- if self.value == Self::MAX.value {
+ if self.value.get() == Self::MAX.value.get() {
None
} else {
- Some(Self::__from_ordinal_date_unchecked(self.year() + 1, 1))
+ // Safety: `ordinal` is not zero.
+ unsafe { Some(Self::__from_ordinal_date_unchecked(self.year() + 1, 1)) }
}
} else {
Some(Self {
- value: self.value + 1,
+ // Safety: `ordinal` is not zero.
+ value: unsafe { NonZeroI32::new_unchecked(self.value.get() + 1) },
})
}
}
/// Get the previous calendar date.
///
/// ```rust
/// # use time::Date;
/// # use time_macros::date;
/// assert_eq!(
@@ -510,29 +586,30 @@ impl Date {
/// );
/// assert_eq!(
/// date!(2020 - 01 - 01).previous_day(),
/// Some(date!(2019 - 12 - 31))
/// );
/// assert_eq!(Date::MIN.previous_day(), None);
/// ```
pub const fn previous_day(self) -> Option<Self> {
if self.ordinal() != 1 {
Some(Self {
- value: self.value - 1,
+ // Safety: `ordinal` is not zero.
+ value: unsafe { NonZeroI32::new_unchecked(self.value.get() - 1) },
})
- } else if self.value == Self::MIN.value {
+ } else if self.value.get() == Self::MIN.value.get() {
None
} else {
- Some(Self::__from_ordinal_date_unchecked(
- self.year() - 1,
- days_in_year(self.year() - 1),
- ))
+ // Safety: `ordinal` is not zero.
+ Some(unsafe {
+ Self::__from_ordinal_date_unchecked(self.year() - 1, days_in_year(self.year() - 1))
+ })
}
}
/// Calculates the first occurrence of a weekday that is strictly later than a given `Date`.
///
/// # Panics
/// Panics if an overflow occurred.
///
/// # Examples
/// ```
@@ -690,20 +767,63 @@ impl Date {
}
let julian_day = const_try_opt!(self.to_julian_day().checked_add(whole_days as _));
if let Ok(date) = Self::from_julian_day(julian_day) {
Some(date)
} else {
None
}
}
+ /// Computes `self + duration`, returning `None` if an overflow occurred.
+ ///
+ /// ```rust
+ /// # use time::{Date, ext::NumericalStdDuration};
+ /// # use time_macros::date;
+ /// assert_eq!(Date::MAX.checked_add_std(1.std_days()), None);
+ /// assert_eq!(
+ /// date!(2020 - 12 - 31).checked_add_std(2.std_days()),
+ /// Some(date!(2021 - 01 - 02))
+ /// );
+ /// ```
+ ///
+ /// # Note
+ ///
+ /// This function only takes whole days into account.
+ ///
+ /// ```rust
+ /// # use time::{Date, ext::NumericalStdDuration};
+ /// # use time_macros::date;
+ /// assert_eq!(Date::MAX.checked_add_std(23.std_hours()), Some(Date::MAX));
+ /// assert_eq!(
+ /// date!(2020 - 12 - 31).checked_add_std(23.std_hours()),
+ /// Some(date!(2020 - 12 - 31))
+ /// );
+ /// assert_eq!(
+ /// date!(2020 - 12 - 31).checked_add_std(47.std_hours()),
+ /// Some(date!(2021 - 01 - 01))
+ /// );
+ /// ```
+ pub const fn checked_add_std(self, duration: StdDuration) -> Option<Self> {
+ let whole_days = duration.as_secs() / Second::per(Day) as u64;
+ if whole_days > i32::MAX as u64 {
+ return None;
+ }
+
+ let julian_day = const_try_opt!(self.to_julian_day().checked_add(whole_days as _));
+ if let Ok(date) = Self::from_julian_day(julian_day) {
+ Some(date)
+ } else {
+ None
+ }
+ }
+
/// Computes `self - duration`, returning `None` if an overflow occurred.
///
/// ```
/// # use time::{Date, ext::NumericalDuration};
/// # use time_macros::date;
/// assert_eq!(Date::MAX.checked_sub((-2).days()), None);
/// assert_eq!(Date::MIN.checked_sub(1.days()), None);
/// assert_eq!(
/// date!(2020 - 12 - 31).checked_sub(2.days()),
/// Some(date!(2020 - 12 - 29))
@@ -735,20 +855,63 @@ impl Date {
}
let julian_day = const_try_opt!(self.to_julian_day().checked_sub(whole_days as _));
if let Ok(date) = Self::from_julian_day(julian_day) {
Some(date)
} else {
None
}
}
+ /// Computes `self - duration`, returning `None` if an overflow occurred.
+ ///
+ /// ```
+ /// # use time::{Date, ext::NumericalStdDuration};
+ /// # use time_macros::date;
+ /// assert_eq!(Date::MIN.checked_sub_std(1.std_days()), None);
+ /// assert_eq!(
+ /// date!(2020 - 12 - 31).checked_sub_std(2.std_days()),
+ /// Some(date!(2020 - 12 - 29))
+ /// );
+ /// ```
+ ///
+ /// # Note
+ ///
+ /// This function only takes whole days into account.
+ ///
+ /// ```
+ /// # use time::{Date, ext::NumericalStdDuration};
+ /// # use time_macros::date;
+ /// assert_eq!(Date::MIN.checked_sub_std(23.std_hours()), Some(Date::MIN));
+ /// assert_eq!(
+ /// date!(2020 - 12 - 31).checked_sub_std(23.std_hours()),
+ /// Some(date!(2020 - 12 - 31))
+ /// );
+ /// assert_eq!(
+ /// date!(2020 - 12 - 31).checked_sub_std(47.std_hours()),
+ /// Some(date!(2020 - 12 - 30))
+ /// );
+ /// ```
+ pub const fn checked_sub_std(self, duration: StdDuration) -> Option<Self> {
+ let whole_days = duration.as_secs() / Second::per(Day) as u64;
+ if whole_days > i32::MAX as u64 {
+ return None;
+ }
+
+ let julian_day = const_try_opt!(self.to_julian_day().checked_sub(whole_days as _));
+ if let Ok(date) = Self::from_julian_day(julian_day) {
+ Some(date)
+ } else {
+ None
+ }
+ }
+
/// Calculates the first occurrence of a weekday that is strictly later than a given `Date`.
/// Returns `None` if an overflow occurred.
pub(crate) const fn checked_next_occurrence(self, weekday: Weekday) -> Option<Self> {
let day_diff = match weekday as i8 - self.weekday() as i8 {
1 | -6 => 1,
2 | -5 => 2,
3 | -4 => 3,
4 | -3 => 4,
5 | -2 => 5,
6 | -1 => 6,
@@ -780,41 +943,33 @@ impl Date {
self.checked_sub(Duration::days(day_diff))
}
/// Calculates the `n`th occurrence of a weekday that is strictly later than a given `Date`.
/// Returns `None` if an overflow occurred or if `n == 0`.
pub(crate) const fn checked_nth_next_occurrence(self, weekday: Weekday, n: u8) -> Option<Self> {
if n == 0 {
return None;
}
- let next_occ = self.checked_next_occurrence(weekday);
- if let Some(val) = next_occ {
- val.checked_add(Duration::weeks(n as i64 - 1))
- } else {
- None
- }
+ const_try_opt!(self.checked_next_occurrence(weekday))
+ .checked_add(Duration::weeks(n as i64 - 1))
}
/// Calculates the `n`th occurrence of a weekday that is strictly earlier than a given `Date`.
/// Returns `None` if an overflow occurred or if `n == 0`.
pub(crate) const fn checked_nth_prev_occurrence(self, weekday: Weekday, n: u8) -> Option<Self> {
if n == 0 {
return None;
}
- let next_occ = self.checked_prev_occurrence(weekday);
- if let Some(val) = next_occ {
- val.checked_sub(Duration::weeks(n as i64 - 1))
- } else {
- None
- }
+ const_try_opt!(self.checked_prev_occurrence(weekday))
+ .checked_sub(Duration::weeks(n as i64 - 1))
}
// endregion: checked arithmetic
// region: saturating arithmetic
/// Computes `self + duration`, saturating value on overflow.
///
/// ```rust
/// # use time::{Date, ext::NumericalDuration};
/// # use time_macros::date;
/// assert_eq!(Date::MAX.saturating_add(1.days()), Date::MAX);
@@ -900,45 +1055,51 @@ impl Date {
/// # use time_macros::date;
/// assert_eq!(
/// date!(2022 - 02 - 18).replace_year(2019),
/// Ok(date!(2019 - 02 - 18))
/// );
/// assert!(date!(2022 - 02 - 18).replace_year(-1_000_000_000).is_err()); // -1_000_000_000 isn't a valid year
/// assert!(date!(2022 - 02 - 18).replace_year(1_000_000_000).is_err()); // 1_000_000_000 isn't a valid year
/// ```
#[must_use = "This method does not mutate the original `Date`."]
pub const fn replace_year(self, year: i32) -> Result<Self, error::ComponentRange> {
- ensure_value_in_range!(year in MIN_YEAR => MAX_YEAR);
+ ensure_ranged!(Year: year);
let ordinal = self.ordinal();
// Dates in January and February are unaffected by leap years.
if ordinal <= 59 {
- return Ok(Self::__from_ordinal_date_unchecked(year, ordinal));
+ // Safety: `ordinal` is not zero.
+ return Ok(unsafe { Self::__from_ordinal_date_unchecked(year, ordinal) });
}
match (is_leap_year(self.year()), is_leap_year(year)) {
- (false, false) | (true, true) => Ok(Self::__from_ordinal_date_unchecked(year, ordinal)),
+ (false, false) | (true, true) => {
+ // Safety: `ordinal` is not zero.
+ Ok(unsafe { Self::__from_ordinal_date_unchecked(year, ordinal) })
+ }
// February 29 does not exist in common years.
(true, false) if ordinal == 60 => Err(error::ComponentRange {
name: "day",
value: 29,
minimum: 1,
maximum: 28,
conditional_range: true,
}),
// We're going from a common year to a leap year. Shift dates in March and later by
// one day.
- (false, true) => Ok(Self::__from_ordinal_date_unchecked(year, ordinal + 1)),
+ // Safety: `ordinal` is not zero.
+ (false, true) => Ok(unsafe { Self::__from_ordinal_date_unchecked(year, ordinal + 1) }),
// We're going from a leap year to a common year. Shift dates in January and
// February by one day.
- (true, false) => Ok(Self::__from_ordinal_date_unchecked(year, ordinal - 1)),
+ // Safety: `ordinal` is not zero.
+ (true, false) => Ok(unsafe { Self::__from_ordinal_date_unchecked(year, ordinal - 1) }),
}
}
/// Replace the month of the year.
///
/// ```rust
/// # use time_macros::date;
/// # use time::Month;
/// assert_eq!(
/// date!(2022 - 02 - 18).replace_month(Month::January),
@@ -962,31 +1123,69 @@ impl Date {
/// # use time_macros::date;
/// assert_eq!(
/// date!(2022 - 02 - 18).replace_day(1),
/// Ok(date!(2022 - 02 - 01))
/// );
/// assert!(date!(2022 - 02 - 18).replace_day(0).is_err()); // 0 isn't a valid day
/// assert!(date!(2022 - 02 - 18).replace_day(30).is_err()); // 30 isn't a valid day in February
/// ```
#[must_use = "This method does not mutate the original `Date`."]
pub const fn replace_day(self, day: u8) -> Result<Self, error::ComponentRange> {
- // Days 1-28 are present in every month, so we can skip checking.
- if day == 0 || day >= 29 {
- ensure_value_in_range!(
- day conditionally in 1 => days_in_year_month(self.year(), self.month())
- );
+ match day {
+ 1..=28 => {}
+ 29..=31 if day <= days_in_year_month(self.year(), self.month()) => {}
+ _ => {
+ return Err(error::ComponentRange {
+ name: "day",
+ minimum: 1,
+ maximum: days_in_year_month(self.year(), self.month()) as _,
+ value: day as _,
+ conditional_range: true,
+ });
+ }
}
- Ok(Self::__from_ordinal_date_unchecked(
- self.year(),
- (self.ordinal() as i16 - self.day() as i16 + day as i16) as _,
- ))
+ // Safety: `ordinal` is not zero.
+ Ok(unsafe {
+ Self::__from_ordinal_date_unchecked(
+ self.year(),
+ (self.ordinal() as i16 - self.day() as i16 + day as i16) as _,
+ )
+ })
+ }
+
+ /// Replace the day of the year.
+ ///
+ /// ```rust
+ /// # use time_macros::date;
+ /// assert_eq!(date!(2022 - 049).replace_ordinal(1), Ok(date!(2022 - 001)));
+ /// assert!(date!(2022 - 049).replace_ordinal(0).is_err()); // 0 isn't a valid ordinal
+ /// assert!(date!(2022 - 049).replace_ordinal(366).is_err()); // 2022 isn't a leap year
+ /// ````
+ #[must_use = "This method does not mutate the original `Date`."]
+ pub const fn replace_ordinal(self, ordinal: u16) -> Result<Self, error::ComponentRange> {
+ match ordinal {
+ 1..=365 => {}
+ 366 if is_leap_year(self.year()) => {}
+ _ => {
+ return Err(error::ComponentRange {
+ name: "ordinal",
+ minimum: 1,
+ maximum: days_in_year(self.year()) as _,
+ value: ordinal as _,
+ conditional_range: true,
+ });
+ }
+ }
+
+ // Safety: `ordinal` is in range.
+ Ok(unsafe { Self::__from_ordinal_date_unchecked(self.year(), ordinal) })
}
// endregion replacement
}
// region: attach time
/// Methods to add a [`Time`] component, resulting in a [`PrimitiveDateTime`].
impl Date {
/// Create a [`PrimitiveDateTime`] using the existing date. The [`Time`] component will be set
/// to midnight.
///
@@ -1131,93 +1330,164 @@ impl Date {
/// # Ok::<_, time::Error>(())
/// ```
pub fn parse(
input: &str,
description: &(impl Parsable + ?Sized),
) -> Result<Self, error::Parse> {
description.parse_date(input.as_bytes())
}
}
-impl fmt::Display for Date {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- if cfg!(feature = "large-dates") && self.year().abs() >= 10_000 {
- write!(
- f,
- "{:+}-{:02}-{:02}",
- self.year(),
- self.month() as u8,
- self.day()
+mod private {
+ #[non_exhaustive]
+ #[derive(Debug, Clone, Copy)]
+ pub struct DateMetadata {
+ /// The width of the year component, including the sign.
+ pub(super) year_width: u8,
+ /// Whether the sign should be displayed.
+ pub(super) display_sign: bool,
+ pub(super) year: i32,
+ pub(super) month: u8,
+ pub(super) day: u8,
+ }
+}
+use private::DateMetadata;
+
+impl SmartDisplay for Date {
+ type Metadata = DateMetadata;
+
+ fn metadata(&self, _: FormatterOptions) -> Metadata<Self> {
+ let (year, month, day) = self.to_calendar_date();
+
+ // There is a minimum of four digits for any year.
+ let mut year_width = cmp::max(year.unsigned_abs().num_digits(), 4);
+ let display_sign = if !(0..10_000).contains(&year) {
+ // An extra character is required for the sign.
+ year_width += 1;
+ true
+ } else {
+ false
+ };
+
+ let formatted_width = year_width.extend::<usize>()
+ + smart_display::padded_width_of!(
+ "-",
+ u8::from(month) => width(2),
+ "-",
+ day => width(2),
+ );
+
+ Metadata::new(
+ formatted_width,
+ self,
+ DateMetadata {
+ year_width,
+ display_sign,
+ year,
+ month: u8::from(month),
+ day,
+ },
+ )
+ }
+
+ fn fmt_with_metadata(
+ &self,
+ f: &mut fmt::Formatter<'_>,
+ metadata: Metadata<Self>,
+ ) -> fmt::Result {
+ let DateMetadata {
+ year_width,
+ display_sign,
+ year,
+ month,
+ day,
+ } = *metadata;
+ let year_width = year_width.extend();
+
+ if display_sign {
+ f.pad_with_width(
+ metadata.unpadded_width(),
+ format_args!("{year:+0year_width$}-{month:02}-{day:02}"),
)
} else {
- write!(
- f,
- "{:0width$}-{:02}-{:02}",
- self.year(),
- self.month() as u8,
- self.day(),
- width = 4 + (self.year() < 0) as usize
+ f.pad_with_width(
+ metadata.unpadded_width(),
+ format_args!("{year:0year_width$}-{month:02}-{day:02}"),
)
}
}
}
+impl fmt::Display for Date {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ SmartDisplay::fmt(self, f)
+ }
+}
+
impl fmt::Debug for Date {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
fmt::Display::fmt(self, f)
}
}
// endregion formatting & parsing
// region: trait impls
impl Add<Duration> for Date {
type Output = Self;
+ /// # Panics
+ ///
+ /// This may panic if an overflow occurs.
fn add(self, duration: Duration) -> Self::Output {
self.checked_add(duration)
.expect("overflow adding duration to date")
}
}
impl Add<StdDuration> for Date {
type Output = Self;
+ /// # Panics
+ ///
+ /// This may panic if an overflow occurs.
fn add(self, duration: StdDuration) -> Self::Output {
- Self::from_julian_day(
- self.to_julian_day() + (duration.as_secs() / Second.per(Day) as u64) as i32,
- )
- .expect("overflow adding duration to date")
+ self.checked_add_std(duration)
+ .expect("overflow adding duration to date")
}
}
impl_add_assign!(Date: Duration, StdDuration);
impl Sub<Duration> for Date {
type Output = Self;
+ /// # Panics
+ ///
+ /// This may panic if an overflow occurs.
fn sub(self, duration: Duration) -> Self::Output {
self.checked_sub(duration)
.expect("overflow subtracting duration from date")
}
}
impl Sub<StdDuration> for Date {
type Output = Self;
+ /// # Panics
+ ///
+ /// This may panic if an overflow occurs.
fn sub(self, duration: StdDuration) -> Self::Output {
- Self::from_julian_day(
- self.to_julian_day() - (duration.as_secs() / Second.per(Day) as u64) as i32,
- )
- .expect("overflow subtracting duration from date")
+ self.checked_sub_std(duration)
+ .expect("overflow subtracting duration from date")
}
}
impl_sub_assign!(Date: Duration, StdDuration);
impl Sub for Date {
type Output = Duration;
fn sub(self, other: Self) -> Self::Output {
- Duration::days((self.to_julian_day() - other.to_julian_day()) as _)
+ Duration::days((self.to_julian_day() - other.to_julian_day()).extend())
}
}
// endregion trait impls
diff --git a/third_party/rust/time/src/date_time.rs b/third_party/rust/time/src/date_time.rs
deleted file mode 100644
index f4730f502d..0000000000
--- a/third_party/rust/time/src/date_time.rs
+++ /dev/null
@@ -1,1180 +0,0 @@
-//! The [`DateTime`] struct and its associated `impl`s.
-
-// TODO(jhpratt) Document everything before making public.
-#![allow(clippy::missing_docs_in_private_items)]
-// This is intentional, as the struct will likely be exposed at some point.
-#![allow(unreachable_pub)]
-
-use core::cmp::Ordering;
-use core::fmt;
-use core::hash::{Hash, Hasher};
-use core::mem::size_of;
-use core::ops::{Add, AddAssign, Sub, SubAssign};
-use core::time::Duration as StdDuration;
-#[cfg(feature = "formatting")]
-use std::io;
-#[cfg(feature = "std")]
-use std::time::SystemTime;
-
-use crate::convert::*;
-use crate::date::{MAX_YEAR, MIN_YEAR};
-#[cfg(feature = "formatting")]
-use crate::formatting::Formattable;
-#[cfg(feature = "parsing")]
-use crate::parsing::{Parsable, Parsed};
-use crate::{error, util, Date, Duration, Month, Time, UtcOffset, Weekday};
-
-#[allow(missing_debug_implementations, missing_copy_implementations)]
-pub(crate) mod offset_kind {
- pub enum None {}
- pub enum Fixed {}
-}
-
-pub(crate) use sealed::MaybeOffset;
-use sealed::*;
-mod sealed {
- use super::*;
-
- /// A type that is guaranteed to be either `()` or [`UtcOffset`].
- ///
- /// **Do not** add any additional implementations of this trait.
- #[allow(unreachable_pub)] // intentional
- pub trait MaybeOffsetType {}
- impl MaybeOffsetType for () {}
- impl MaybeOffsetType for UtcOffset {}
-
- pub trait MaybeOffset: Sized {
- /// The offset type as it is stored in memory.
- #[cfg(feature = "quickcheck")]
- type MemoryOffsetType: Copy + MaybeOffsetType + quickcheck::Arbitrary;
- #[cfg(not(feature = "quickcheck"))]
- type MemoryOffsetType: Copy + MaybeOffsetType;
-
- /// The offset type as it should be thought about.
- ///
- /// For example, a `DateTime<Utc>` has a logical offset type of [`UtcOffset`], but does not
- /// actually store an offset in memory.
- type LogicalOffsetType: Copy + MaybeOffsetType;
-
- /// Required to be `Self`. Used for bound equality.
- type Self_;
-
- /// True if and only if `Self::LogicalOffsetType` is `UtcOffset`.
- const HAS_LOGICAL_OFFSET: bool =
- size_of::<Self::LogicalOffsetType>() == size_of::<UtcOffset>();
- /// True if and only if `Self::MemoryOffsetType` is `UtcOffset`.
- const HAS_MEMORY_OFFSET: bool =
- size_of::<Self::MemoryOffsetType>() == size_of::<UtcOffset>();
-
- /// `Some` if and only if the logical UTC offset is statically known.
- // TODO(jhpratt) When const trait impls are stable, this can be removed in favor of
- // `.as_offset_opt()`.
- const STATIC_OFFSET: Option<UtcOffset>;
-
- #[cfg(feature = "parsing")]
- fn try_from_parsed(parsed: Parsed) -> Result<Self::MemoryOffsetType, error::TryFromParsed>;
- }
-
- // Traits to indicate whether a `MaybeOffset` has a logical offset type of `UtcOffset` or not.
-
- pub trait HasLogicalOffset: MaybeOffset<LogicalOffsetType = UtcOffset> {}
- impl<T: MaybeOffset<LogicalOffsetType = UtcOffset>> HasLogicalOffset for T {}
-
- pub trait NoLogicalOffset: MaybeOffset<LogicalOffsetType = ()> {}
- impl<T: MaybeOffset<LogicalOffsetType = ()>> NoLogicalOffset for T {}
-
- // Traits to indicate whether a `MaybeOffset` has a memory offset type of `UtcOffset` or not.
-
- pub trait HasMemoryOffset: MaybeOffset<MemoryOffsetType = UtcOffset> {}
- impl<T: MaybeOffset<MemoryOffsetType = UtcOffset>> HasMemoryOffset for T {}
-
- pub trait NoMemoryOffset: MaybeOffset<MemoryOffsetType = ()> {}
- impl<T: MaybeOffset<MemoryOffsetType = ()>> NoMemoryOffset for T {}
-
- // Traits to indicate backing type being implemented.
-
- pub trait IsOffsetKindNone:
- MaybeOffset<Self_ = offset_kind::None, MemoryOffsetType = (), LogicalOffsetType = ()>
- {
- }
- impl IsOffsetKindNone for offset_kind::None {}
-
- pub trait IsOffsetKindFixed:
- MaybeOffset<
- Self_ = offset_kind::Fixed,
- MemoryOffsetType = UtcOffset,
- LogicalOffsetType = UtcOffset,
- >
- {
- }
- impl IsOffsetKindFixed for offset_kind::Fixed {}
-}
-
-impl MaybeOffset for offset_kind::None {
- type MemoryOffsetType = ();
- type LogicalOffsetType = ();
-
- type Self_ = Self;
-
- const STATIC_OFFSET: Option<UtcOffset> = None;
-
- #[cfg(feature = "parsing")]
- fn try_from_parsed(_: Parsed) -> Result<(), error::TryFromParsed> {
- Ok(())
- }
-}
-
-impl MaybeOffset for offset_kind::Fixed {
- type MemoryOffsetType = UtcOffset;
- type LogicalOffsetType = UtcOffset;
-
- type Self_ = Self;
-
- const STATIC_OFFSET: Option<UtcOffset> = None;
-
- #[cfg(feature = "parsing")]
- fn try_from_parsed(parsed: Parsed) -> Result<UtcOffset, error::TryFromParsed> {
- parsed.try_into()
- }
-}
-
-// region: const trait method hacks
-// TODO(jhpratt) When const trait impls are stable, these methods can be removed in favor of methods
-// in `MaybeOffset`, which would then be made `const`.
-const fn maybe_offset_as_offset_opt<O: MaybeOffset>(
- offset: O::MemoryOffsetType,
-) -> Option<UtcOffset> {
- if O::STATIC_OFFSET.is_some() {
- O::STATIC_OFFSET
- } else if O::HAS_MEMORY_OFFSET {
- #[repr(C)] // needed to guarantee they align at the start
- union Convert<O: MaybeOffset> {
- input: O::MemoryOffsetType,
- output: UtcOffset,
- }
-
- // Safety: `O::HAS_OFFSET` indicates that `O::Offset` is `UtcOffset`. This code effectively
- // performs a transmute from `O::Offset` to `UtcOffset`, which we know is the same type.
- Some(unsafe { Convert::<O> { input: offset }.output })
- } else {
- None
- }
-}
-
-const fn maybe_offset_as_offset<O: MaybeOffset + HasLogicalOffset>(
- offset: O::MemoryOffsetType,
-) -> UtcOffset {
- match maybe_offset_as_offset_opt::<O>(offset) {
- Some(offset) => offset,
- None => bug!("`MaybeOffset::as_offset` called on a type without an offset in memory"),
- }
-}
-
-pub(crate) const fn maybe_offset_from_offset<O: MaybeOffset>(
- offset: UtcOffset,
-) -> O::MemoryOffsetType {
- #[repr(C)] // needed to guarantee the types align at the start
- union Convert<O: MaybeOffset> {
- input: UtcOffset,
- output: O::MemoryOffsetType,
- }
-
- // Safety: It is statically known that there are only two possibilities due to the trait bound
- // of `O::MemoryOffsetType`, which ultimately relies on `MaybeOffsetType`. The two possibilities
- // are:
- // 1. UtcOffset -> UtcOffset
- // 2. UtcOffset -> ()
- // (1) is valid because it is an identity conversion, which is always valid. (2) is valid
- // because `()` is a 1-ZST, so converting to it is always valid.
- unsafe { Convert::<O> { input: offset }.output }
-}
-// endregion const trait methods hacks
-
-/// The Julian day of the Unix epoch.
-const UNIX_EPOCH_JULIAN_DAY: i32 = Date::__from_ordinal_date_unchecked(1970, 1).to_julian_day();
-
-pub struct DateTime<O: MaybeOffset> {
- pub(crate) date: Date,
- pub(crate) time: Time,
- pub(crate) offset: O::MemoryOffsetType,
-}
-
-// Manual impl to remove extraneous bounds.
-impl<O: MaybeOffset> Clone for DateTime<O> {
- fn clone(&self) -> Self {
- *self
- }
-}
-
-// Manual impl to remove extraneous bounds.
-impl<O: MaybeOffset> Copy for DateTime<O> {}
-
-// region: constructors
-impl DateTime<offset_kind::None> {
- pub const MIN: Self = Self {
- date: Date::MIN,
- time: Time::MIN,
- offset: (),
- };
-
- pub const MAX: Self = Self {
- date: Date::MAX,
- time: Time::MAX,
- offset: (),
- };
-}
-
-impl DateTime<offset_kind::Fixed> {
- pub const UNIX_EPOCH: Self = Self {
- date: Date::__from_ordinal_date_unchecked(1970, 1),
- time: Time::MIDNIGHT,
- offset: UtcOffset::UTC,
- };
-}
-
-impl<O: MaybeOffset> DateTime<O> {
- pub const fn new(date: Date, time: Time) -> Self
- where
- O: IsOffsetKindNone,
- {
- Self {
- date,
- time,
- offset: (),
- }
- }
-
- pub const fn from_unix_timestamp(timestamp: i64) -> Result<Self, error::ComponentRange>
- where
- O: HasLogicalOffset,
- {
- #[allow(clippy::missing_docs_in_private_items)]
- const MIN_TIMESTAMP: i64 = Date::MIN.midnight().assume_utc().unix_timestamp();
- #[allow(clippy::missing_docs_in_private_items)]
- const MAX_TIMESTAMP: i64 = Date::MAX
- .with_time(Time::__from_hms_nanos_unchecked(23, 59, 59, 999_999_999))
- .assume_utc()
- .unix_timestamp();
-
- ensure_value_in_range!(timestamp in MIN_TIMESTAMP => MAX_TIMESTAMP);
-
- // Use the unchecked method here, as the input validity has already been verified.
- let date = Date::from_julian_day_unchecked(
- UNIX_EPOCH_JULIAN_DAY + div_floor!(timestamp, Second.per(Day) as i64) as i32,
- );
-
- let seconds_within_day = timestamp.rem_euclid(Second.per(Day) as _);
- let time = Time::__from_hms_nanos_unchecked(
- (seconds_within_day / Second.per(Hour) as i64) as _,
- ((seconds_within_day % Second.per(Hour) as i64) / Minute.per(Hour) as i64) as _,
- (seconds_within_day % Second.per(Minute) as i64) as _,
- 0,
- );
-
- Ok(Self {
- date,
- time,
- offset: maybe_offset_from_offset::<O>(UtcOffset::UTC),
- })
- }
-
- pub const fn from_unix_timestamp_nanos(timestamp: i128) -> Result<Self, error::ComponentRange>
- where
- O: HasLogicalOffset,
- {
- let datetime = const_try!(Self::from_unix_timestamp(div_floor!(
- timestamp,
- Nanosecond.per(Second) as i128
- ) as i64));
-
- Ok(Self {
- date: datetime.date,
- time: Time::__from_hms_nanos_unchecked(
- datetime.hour(),
- datetime.minute(),
- datetime.second(),
- timestamp.rem_euclid(Nanosecond.per(Second) as _) as u32,
- ),
- offset: maybe_offset_from_offset::<O>(UtcOffset::UTC),
- })
- }
- // endregion constructors
-
- // region: now
- // The return type will likely be loosened once `ZonedDateTime` is implemented. This is not a
- // breaking change calls are currently limited to only `OffsetDateTime`.
- #[cfg(feature = "std")]
- pub fn now_utc() -> DateTime<offset_kind::Fixed>
- where
- O: IsOffsetKindFixed,
- {
- #[cfg(all(
- target_family = "wasm",
- not(any(target_os = "emscripten", target_os = "wasi")),
- feature = "wasm-bindgen"
- ))]
- {
- js_sys::Date::new_0().into()
- }
-
- #[cfg(not(all(
- target_family = "wasm",
- not(any(target_os = "emscripten", target_os = "wasi")),
- feature = "wasm-bindgen"
- )))]
- SystemTime::now().into()
- }
-
- // The return type will likely be loosened once `ZonedDateTime` is implemented. This is not a
- // breaking change calls are currently limited to only `OffsetDateTime`.
- #[cfg(feature = "local-offset")]
- pub fn now_local() -> Result<DateTime<offset_kind::Fixed>, error::IndeterminateOffset>
- where
- O: IsOffsetKindFixed,
- {
- let t = DateTime::<offset_kind::Fixed>::now_utc();
- Ok(t.to_offset(UtcOffset::local_offset_at(crate::OffsetDateTime(t))?))
- }
- // endregion now
-
- // region: getters
- // region: component getters
- pub const fn date(self) -> Date {
- self.date
- }
-
- pub const fn time(self) -> Time {
- self.time
- }
-
- pub const fn offset(self) -> UtcOffset
- where
- O: HasLogicalOffset,
- {
- maybe_offset_as_offset::<O>(self.offset)
- }
- // endregion component getters
-
- // region: date getters
- pub const fn year(self) -> i32 {
- self.date.year()
- }
-
- pub const fn month(self) -> Month {
- self.date.month()
- }
-
- pub const fn day(self) -> u8 {
- self.date.day()
- }
-
- pub const fn ordinal(self) -> u16 {
- self.date.ordinal()
- }
-
- pub const fn iso_week(self) -> u8 {
- self.date.iso_week()
- }
-
- pub const fn sunday_based_week(self) -> u8 {
- self.date.sunday_based_week()
- }
-
- pub const fn monday_based_week(self) -> u8 {
- self.date.monday_based_week()
- }
-
- pub const fn to_calendar_date(self) -> (i32, Month, u8) {
- self.date.to_calendar_date()
- }
-
- pub const fn to_ordinal_date(self) -> (i32, u16) {
- self.date.to_ordinal_date()
- }
-
- pub const fn to_iso_week_date(self) -> (i32, u8, Weekday) {
- self.date.to_iso_week_date()
- }
-
- pub const fn weekday(self) -> Weekday {
- self.date.weekday()
- }
-
- pub const fn to_julian_day(self) -> i32 {
- self.date.to_julian_day()
- }
- // endregion date getters
-
- // region: time getters
- pub const fn as_hms(self) -> (u8, u8, u8) {
- self.time.as_hms()
- }
-
- pub const fn as_hms_milli(self) -> (u8, u8, u8, u16) {
- self.time.as_hms_milli()
- }
-
- pub const fn as_hms_micro(self) -> (u8, u8, u8, u32) {
- self.time.as_hms_micro()
- }
-
- pub const fn as_hms_nano(self) -> (u8, u8, u8, u32) {
- self.time.as_hms_nano()
- }
-
- pub const fn hour(self) -> u8 {
- self.time.hour()
- }
-
- pub const fn minute(self) -> u8 {
- self.time.minute()
- }
-
- pub const fn second(self) -> u8 {
- self.time.second()
- }
-
- pub const fn millisecond(self) -> u16 {
- self.time.millisecond()
- }
-
- pub const fn microsecond(self) -> u32 {
- self.time.microsecond()
- }
-
- pub const fn nanosecond(self) -> u32 {
- self.time.nanosecond()
- }
- // endregion time getters
-
- // region: unix timestamp getters
- pub const fn unix_timestamp(self) -> i64
- where
- O: HasLogicalOffset,
- {
- let offset = maybe_offset_as_offset::<O>(self.offset).whole_seconds() as i64;
-
- let days =
- (self.to_julian_day() as i64 - UNIX_EPOCH_JULIAN_DAY as i64) * Second.per(Day) as i64;
- let hours = self.hour() as i64 * Second.per(Hour) as i64;
- let minutes = self.minute() as i64 * Second.per(Minute) as i64;
- let seconds = self.second() as i64;
- days + hours + minutes + seconds - offset
- }
-
- pub const fn unix_timestamp_nanos(self) -> i128
- where
- O: HasLogicalOffset,
- {
- self.unix_timestamp() as i128 * Nanosecond.per(Second) as i128 + self.nanosecond() as i128
- }
- // endregion unix timestamp getters
- // endregion: getters
-
- // region: attach offset
- pub const fn assume_offset(self, offset: UtcOffset) -> DateTime<offset_kind::Fixed>
- where
- O: NoLogicalOffset,
- {
- DateTime {
- date: self.date,
- time: self.time,
- offset,
- }
- }
-
- pub const fn assume_utc(self) -> DateTime<offset_kind::Fixed>
- where
- O: NoLogicalOffset,
- {
- self.assume_offset(UtcOffset::UTC)
- }
- // endregion attach offset
-
- // region: to offset
- pub const fn to_offset(self, offset: UtcOffset) -> DateTime<offset_kind::Fixed>
- where
- O: HasLogicalOffset,
- {
- expect_opt!(
- self.checked_to_offset(offset),
- "local datetime out of valid range"
- )
- }
-
- pub const fn checked_to_offset(self, offset: UtcOffset) -> Option<DateTime<offset_kind::Fixed>>
- where
- O: HasLogicalOffset,
- {
- let self_offset = maybe_offset_as_offset::<O>(self.offset);
-
- if self_offset.whole_hours() == offset.whole_hours()
- && self_offset.minutes_past_hour() == offset.minutes_past_hour()
- && self_offset.seconds_past_minute() == offset.seconds_past_minute()
- {
- return Some(DateTime {
- date: self.date,
- time: self.time,
- offset,
- });
- }
-
- let (year, ordinal, time) = self.to_offset_raw(offset);
-
- if year > MAX_YEAR || year < MIN_YEAR {
- return None;
- }
-
- Some(DateTime {
- date: Date::__from_ordinal_date_unchecked(year, ordinal),
- time,
- offset,
- })
- }
-
- /// Equivalent to `.to_offset(UtcOffset::UTC)`, but returning the year, ordinal, and time. This
- /// avoids constructing an invalid [`Date`] if the new value is out of range.
- pub(crate) const fn to_offset_raw(self, offset: UtcOffset) -> (i32, u16, Time) {
- let Some(from) = maybe_offset_as_offset_opt::<O>(self.offset) else {
- // No adjustment is needed because there is no offset.
- return (self.year(), self.ordinal(), self.time);
- };
- let to = offset;
-
- // Fast path for when no conversion is necessary.
- if from.whole_hours() == to.whole_hours()
- && from.minutes_past_hour() == to.minutes_past_hour()
- && from.seconds_past_minute() == to.seconds_past_minute()
- {
- return (self.year(), self.ordinal(), self.time());
- }
-
- let mut second = self.second() as i16 - from.seconds_past_minute() as i16
- + to.seconds_past_minute() as i16;
- let mut minute =
- self.minute() as i16 - from.minutes_past_hour() as i16 + to.minutes_past_hour() as i16;
- let mut hour = self.hour() as i8 - from.whole_hours() + to.whole_hours();
- let (mut year, ordinal) = self.to_ordinal_date();
- let mut ordinal = ordinal as i16;
-
- // Cascade the values twice. This is needed because the values are adjusted twice above.
- cascade!(second in 0..Second.per(Minute) as i16 => minute);
- cascade!(second in 0..Second.per(Minute) as i16 => minute);
- cascade!(minute in 0..Minute.per(Hour) as i16 => hour);
- cascade!(minute in 0..Minute.per(Hour) as i16 => hour);
- cascade!(hour in 0..Hour.per(Day) as i8 => ordinal);
- cascade!(hour in 0..Hour.per(Day) as i8 => ordinal);
- cascade!(ordinal => year);
-
- debug_assert!(ordinal > 0);
- debug_assert!(ordinal <= crate::util::days_in_year(year) as i16);
-
- (
- year,
- ordinal as _,
- Time::__from_hms_nanos_unchecked(
- hour as _,
- minute as _,
- second as _,
- self.nanosecond(),
- ),
- )
- }
- // endregion to offset
-
- // region: checked arithmetic
- pub const fn checked_add(self, duration: Duration) -> Option<Self> {
- let (date_adjustment, time) = self.time.adjusting_add(duration);
- let date = const_try_opt!(self.date.checked_add(duration));
-
- Some(Self {
- date: match date_adjustment {
- util::DateAdjustment::Previous => const_try_opt!(date.previous_day()),
- util::DateAdjustment::Next => const_try_opt!(date.next_day()),
- util::DateAdjustment::None => date,
- },
- time,
- offset: self.offset,
- })
- }
-
- pub const fn checked_sub(self, duration: Duration) -> Option<Self> {
- let (date_adjustment, time) = self.time.adjusting_sub(duration);
- let date = const_try_opt!(self.date.checked_sub(duration));
-
- Some(Self {
- date: match date_adjustment {
- util::DateAdjustment::Previous => const_try_opt!(date.previous_day()),
- util::DateAdjustment::Next => const_try_opt!(date.next_day()),
- util::DateAdjustment::None => date,
- },
- time,
- offset: self.offset,
- })
- }
- // endregion checked arithmetic
-
- // region: saturating arithmetic
- pub const fn saturating_add(self, duration: Duration) -> Self {
- if let Some(datetime) = self.checked_add(duration) {
- datetime
- } else if duration.is_negative() {
- Self {
- date: Date::MIN,
- time: Time::MIN,
- offset: self.offset,
- }
- } else {
- Self {
- date: Date::MAX,
- time: Time::MAX,
- offset: self.offset,
- }
- }
- }
-
- pub const fn saturating_sub(self, duration: Duration) -> Self {
- if let Some(datetime) = self.checked_sub(duration) {
- datetime
- } else if duration.is_negative() {
- Self {
- date: Date::MAX,
- time: Time::MAX,
- offset: self.offset,
- }
- } else {
- Self {
- date: Date::MIN,
- time: Time::MIN,
- offset: self.offset,
- }
- }
- }
- // endregion saturating arithmetic
-
- // region: replacement
- pub const fn replace_time(self, time: Time) -> Self {
- Self {
- date: self.date,
- time,
- offset: self.offset,
- }
- }
-
- pub const fn replace_date(self, date: Date) -> Self {
- Self {
- date,
- time: self.time,
- offset: self.offset,
- }
- }
-
- pub const fn replace_date_time(self, date_time: DateTime<offset_kind::None>) -> Self
- where
- O: HasLogicalOffset,
- {
- Self {
- date: date_time.date,
- time: date_time.time,
- offset: self.offset,
- }
- }
-
- pub const fn replace_year(self, year: i32) -> Result<Self, error::ComponentRange> {
- Ok(Self {
- date: const_try!(self.date.replace_year(year)),
- time: self.time,
- offset: self.offset,
- })
- }
-
- pub const fn replace_month(self, month: Month) -> Result<Self, error::ComponentRange> {
- Ok(Self {
- date: const_try!(self.date.replace_month(month)),
- time: self.time,
- offset: self.offset,
- })
- }
-
- pub const fn replace_day(self, day: u8) -> Result<Self, error::ComponentRange> {
- Ok(Self {
- date: const_try!(self.date.replace_day(day)),
- time: self.time,
- offset: self.offset,
- })
- }
-
- pub const fn replace_hour(self, hour: u8) -> Result<Self, error::ComponentRange> {
- Ok(Self {
- date: self.date,
- time: const_try!(self.time.replace_hour(hour)),
- offset: self.offset,
- })
- }
-
- pub const fn replace_minute(self, minute: u8) -> Result<Self, error::ComponentRange> {
- Ok(Self {
- date: self.date,
- time: const_try!(self.time.replace_minute(minute)),
- offset: self.offset,
- })
- }
-
- pub const fn replace_second(self, second: u8) -> Result<Self, error::ComponentRange> {
- Ok(Self {
- date: self.date,
- time: const_try!(self.time.replace_second(second)),
- offset: self.offset,
- })
- }
-
- pub const fn replace_millisecond(
- self,
- millisecond: u16,
- ) -> Result<Self, error::ComponentRange> {
- Ok(Self {
- date: self.date,
- time: const_try!(self.time.replace_millisecond(millisecond)),
- offset: self.offset,
- })
- }
-
- pub const fn replace_microsecond(
- self,
- microsecond: u32,
- ) -> Result<Self, error::ComponentRange> {
- Ok(Self {
- date: self.date,
- time: const_try!(self.time.replace_microsecond(microsecond)),
- offset: self.offset,
- })
- }
-
- pub const fn replace_nanosecond(self, nanosecond: u32) -> Result<Self, error::ComponentRange> {
- Ok(Self {
- date: self.date,
- time: const_try!(self.time.replace_nanosecond(nanosecond)),
- offset: self.offset,
- })
- }
-
- // Don't gate this on just having an offset, as `ZonedDateTime` cannot be set to an arbitrary
- // offset.
- pub const fn replace_offset(self, offset: UtcOffset) -> DateTime<offset_kind::Fixed>
- where
- O: IsOffsetKindFixed,
- {
- DateTime {
- date: self.date,
- time: self.time,
- offset,
- }
- }
-
- // endregion replacement
-
- // region: formatting & parsing
- #[cfg(feature = "formatting")]
- pub fn format_into(
- self,
- output: &mut impl io::Write,
- format: &(impl Formattable + ?Sized),
- ) -> Result<usize, error::Format> {
- format.format_into(
- output,
- Some(self.date),
- Some(self.time),
- maybe_offset_as_offset_opt::<O>(self.offset),
- )
- }
-
- #[cfg(feature = "formatting")]
- pub fn format(self, format: &(impl Formattable + ?Sized)) -> Result<String, error::Format> {
- format.format(
- Some(self.date),
- Some(self.time),
- maybe_offset_as_offset_opt::<O>(self.offset),
- )
- }
-
- #[cfg(feature = "parsing")]
- pub fn parse(
- input: &str,
- description: &(impl Parsable + ?Sized),
- ) -> Result<Self, error::Parse> {
- description.parse_date_time(input.as_bytes())
- }
-
- /// A helper method to check if the `OffsetDateTime` is a valid representation of a leap second.
- /// Leap seconds, when parsed, are represented as the preceding nanosecond. However, leap
- /// seconds can only occur as the last second of a month UTC.
- #[cfg(feature = "parsing")]
- pub(crate) const fn is_valid_leap_second_stand_in(self) -> bool {
- // Leap seconds aren't allowed if there is no offset.
- if !O::HAS_LOGICAL_OFFSET {
- return false;
- }
-
- // This comparison doesn't need to be adjusted for the stored offset, so check it first for
- // speed.
- if self.nanosecond() != 999_999_999 {
- return false;
- }
-
- let (year, ordinal, time) = self.to_offset_raw(UtcOffset::UTC);
- let Ok(date) = Date::from_ordinal_date(year, ordinal) else {
- return false;
- };
-
- time.hour() == 23
- && time.minute() == 59
- && time.second() == 59
- && date.day() == util::days_in_year_month(year, date.month())
- }
-
- // endregion formatting & parsing
-
- // region: deprecated time getters
-
- // All the way at the bottom as it's low priority. These methods only exist for when
- // `OffsetDateTime` is made an alias of `DateTime<Fixed>`. Consider hiding these methods from
- // documentation in the future.
-
- #[doc(hidden)]
- #[allow(dead_code)] // while functionally private
- #[deprecated(since = "0.3.18", note = "use `as_hms` instead")]
- pub const fn to_hms(self) -> (u8, u8, u8)
- where
- O: IsOffsetKindFixed,
- {
- self.time.as_hms()
- }
-
- #[doc(hidden)]
- #[allow(dead_code)] // while functionally private
- #[deprecated(since = "0.3.18", note = "use `as_hms_milli` instead")]
- pub const fn to_hms_milli(self) -> (u8, u8, u8, u16)
- where
- O: IsOffsetKindFixed,
- {
- self.time.as_hms_milli()
- }
-
- #[doc(hidden)]
- #[allow(dead_code)] // while functionally private
- #[deprecated(since = "0.3.18", note = "use `as_hms_micro` instead")]
- pub const fn to_hms_micro(self) -> (u8, u8, u8, u32)
- where
- O: IsOffsetKindFixed,
- {
- self.time.as_hms_micro()
- }
-
- #[doc(hidden)]
- #[allow(dead_code)] // while functionally private
- #[deprecated(since = "0.3.18", note = "use `as_hms_nano` instead")]
- pub const fn to_hms_nano(self) -> (u8, u8, u8, u32)
- where
- O: IsOffsetKindFixed,
- {
- self.time.as_hms_nano()
- }
- // endregion deprecated time getters
-}
-
-impl<O: MaybeOffset> fmt::Debug for DateTime<O> {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- <Self as fmt::Display>::fmt(self, f)
- }
-}
-
-impl<O: MaybeOffset> fmt::Display for DateTime<O> {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- write!(f, "{} {}", self.date, self.time)?;
- if let Some(offset) = maybe_offset_as_offset_opt::<O>(self.offset) {
- write!(f, " {offset}")?;
- }
- Ok(())
- }
-}
-
-// region: trait impls
-impl<O: MaybeOffset> PartialEq for DateTime<O> {
- fn eq(&self, rhs: &Self) -> bool {
- if O::HAS_LOGICAL_OFFSET {
- self.to_offset_raw(UtcOffset::UTC) == rhs.to_offset_raw(UtcOffset::UTC)
- } else {
- (self.date, self.time) == (rhs.date, rhs.time)
- }
- }
-}
-
-impl<O: MaybeOffset> Eq for DateTime<O> {}
-
-impl<O: MaybeOffset> PartialOrd for DateTime<O> {
- fn partial_cmp(&self, rhs: &Self) -> Option<Ordering> {
- Some(self.cmp(rhs))
- }
-}
-
-impl<O: MaybeOffset> Ord for DateTime<O> {
- fn cmp(&self, rhs: &Self) -> Ordering {
- if O::HAS_LOGICAL_OFFSET {
- self.to_offset_raw(UtcOffset::UTC)
- .cmp(&rhs.to_offset_raw(UtcOffset::UTC))
- } else {
- (self.date, self.time).cmp(&(rhs.date, rhs.time))
- }
- }
-}
-
-impl<O: MaybeOffset> Hash for DateTime<O> {
- fn hash<H: Hasher>(&self, hasher: &mut H) {
- if O::HAS_LOGICAL_OFFSET {
- self.to_offset_raw(UtcOffset::UTC).hash(hasher);
- } else {
- (self.date, self.time).hash(hasher);
- }
- }
-}
-
-impl<O: MaybeOffset> Add<Duration> for DateTime<O> {
- type Output = Self;
-
- fn add(self, duration: Duration) -> Self {
- self.checked_add(duration)
- .expect("resulting value is out of range")
- }
-}
-
-impl<O: MaybeOffset> Add<StdDuration> for DateTime<O> {
- type Output = Self;
-
- fn add(self, duration: StdDuration) -> Self::Output {
- let (is_next_day, time) = self.time.adjusting_add_std(duration);
-
- Self {
- date: if is_next_day {
- (self.date + duration)
- .next_day()
- .expect("resulting value is out of range")
- } else {
- self.date + duration
- },
- time,
- offset: self.offset,
- }
- }
-}
-
-impl<O: MaybeOffset> AddAssign<Duration> for DateTime<O> {
- fn add_assign(&mut self, rhs: Duration) {
- *self = *self + rhs;
- }
-}
-
-impl<O: MaybeOffset> AddAssign<StdDuration> for DateTime<O> {
- fn add_assign(&mut self, rhs: StdDuration) {
- *self = *self + rhs;
- }
-}
-
-impl<O: MaybeOffset> Sub<Duration> for DateTime<O> {
- type Output = Self;
-
- fn sub(self, duration: Duration) -> Self {
- self.checked_sub(duration)
- .expect("resulting value is out of range")
- }
-}
-
-impl<O: MaybeOffset> Sub<StdDuration> for DateTime<O> {
- type Output = Self;
-
- fn sub(self, duration: StdDuration) -> Self::Output {
- let (is_previous_day, time) = self.time.adjusting_sub_std(duration);
-
- Self {
- date: if is_previous_day {
- (self.date - duration)
- .previous_day()
- .expect("resulting value is out of range")
- } else {
- self.date - duration
- },
- time,
- offset: self.offset,
- }
- }
-}
-
-impl<O: MaybeOffset> SubAssign<Duration> for DateTime<O> {
- fn sub_assign(&mut self, rhs: Duration) {
- *self = *self - rhs;
- }
-}
-
-impl<O: MaybeOffset> SubAssign<StdDuration> for DateTime<O> {
- fn sub_assign(&mut self, rhs: StdDuration) {
- *self = *self - rhs;
- }
-}
-
-impl<O: MaybeOffset> Sub<Self> for DateTime<O> {
- type Output = Duration;
-
- fn sub(self, rhs: Self) -> Self::Output {
- let base = (self.date - rhs.date) + (self.time - rhs.time);
-
- match (
- maybe_offset_as_offset_opt::<O>(self.offset),
- maybe_offset_as_offset_opt::<O>(rhs.offset),
- ) {
- (Some(self_offset), Some(rhs_offset)) => {
- let adjustment = Duration::seconds(
- (self_offset.whole_seconds() - rhs_offset.whole_seconds()) as i64,
- );
- base - adjustment
- }
- (left, right) => {
- debug_assert!(
- left.is_none() && right.is_none(),
- "offset type should not be different for the same type"
- );
- base
- }
- }
- }
-}
-
-#[cfg(feature = "std")]
-impl Add<Duration> for SystemTime {
- type Output = Self;
-
- fn add(self, duration: Duration) -> Self::Output {
- if duration.is_zero() {
- self
- } else if duration.is_positive() {
- self + duration.unsigned_abs()
- } else {
- debug_assert!(duration.is_negative());
- self - duration.unsigned_abs()
- }
- }
-}
-
-impl_add_assign!(SystemTime: #[cfg(feature = "std")] Duration);
-
-#[cfg(feature = "std")]
-impl Sub<Duration> for SystemTime {
- type Output = Self;
-
- fn sub(self, duration: Duration) -> Self::Output {
- (DateTime::from(self) - duration).into()
- }
-}
-
-impl_sub_assign!(SystemTime: #[cfg(feature = "std")] Duration);
-
-#[cfg(feature = "std")]
-impl Sub<SystemTime> for DateTime<offset_kind::Fixed> {
- type Output = Duration;
-
- fn sub(self, rhs: SystemTime) -> Self::Output {
- self - Self::from(rhs)
- }
-}
-
-#[cfg(feature = "std")]
-impl Sub<DateTime<offset_kind::Fixed>> for SystemTime {
- type Output = Duration;
-
- fn sub(self, rhs: DateTime<offset_kind::Fixed>) -> Self::Output {
- DateTime::<offset_kind::Fixed>::from(self) - rhs
- }
-}
-
-#[cfg(feature = "std")]
-impl PartialEq<SystemTime> for DateTime<offset_kind::Fixed> {
- fn eq(&self, rhs: &SystemTime) -> bool {
- self == &Self::from(*rhs)
- }
-}
-
-#[cfg(feature = "std")]
-impl PartialEq<DateTime<offset_kind::Fixed>> for SystemTime {
- fn eq(&self, rhs: &DateTime<offset_kind::Fixed>) -> bool {
- &DateTime::<offset_kind::Fixed>::from(*self) == rhs
- }
-}
-
-#[cfg(feature = "std")]
-impl PartialOrd<SystemTime> for DateTime<offset_kind::Fixed> {
- fn partial_cmp(&self, other: &SystemTime) -> Option<Ordering> {
- self.partial_cmp(&Self::from(*other))
- }
-}
-
-#[cfg(feature = "std")]
-impl PartialOrd<DateTime<offset_kind::Fixed>> for SystemTime {
- fn partial_cmp(&self, other: &DateTime<offset_kind::Fixed>) -> Option<Ordering> {
- DateTime::<offset_kind::Fixed>::from(*self).partial_cmp(other)
- }
-}
-
-#[cfg(feature = "std")]
-impl From<SystemTime> for DateTime<offset_kind::Fixed> {
- fn from(system_time: SystemTime) -> Self {
- match system_time.duration_since(SystemTime::UNIX_EPOCH) {
- Ok(duration) => Self::UNIX_EPOCH + duration,
- Err(err) => Self::UNIX_EPOCH - err.duration(),
- }
- }
-}
-
-#[allow(clippy::fallible_impl_from)] // caused by `debug_assert!`
-#[cfg(feature = "std")]
-impl From<DateTime<offset_kind::Fixed>> for SystemTime {
- fn from(datetime: DateTime<offset_kind::Fixed>) -> Self {
- let duration = datetime - DateTime::<offset_kind::Fixed>::UNIX_EPOCH;
-
- if duration.is_zero() {
- Self::UNIX_EPOCH
- } else if duration.is_positive() {
- Self::UNIX_EPOCH + duration.unsigned_abs()
- } else {
- debug_assert!(duration.is_negative());
- Self::UNIX_EPOCH - duration.unsigned_abs()
- }
- }
-}
-
-#[allow(clippy::fallible_impl_from)]
-#[cfg(all(
- target_family = "wasm",
- not(any(target_os = "emscripten", target_os = "wasi")),
- feature = "wasm-bindgen"
-))]
-impl From<js_sys::Date> for DateTime<offset_kind::Fixed> {
- fn from(js_date: js_sys::Date) -> Self {
- // get_time() returns milliseconds
- let timestamp_nanos = (js_date.get_time() * Nanosecond.per(Millisecond) as f64) as i128;
- Self::from_unix_timestamp_nanos(timestamp_nanos)
- .expect("invalid timestamp: Timestamp cannot fit in range")
- }
-}
-
-#[cfg(all(
- target_family = "wasm",
- not(any(target_os = "emscripten", target_os = "wasi")),
- feature = "wasm-bindgen"
-))]
-impl From<DateTime<offset_kind::Fixed>> for js_sys::Date {
- fn from(datetime: DateTime<offset_kind::Fixed>) -> Self {
- // new Date() takes milliseconds
- let timestamp =
- (datetime.unix_timestamp_nanos() / Nanosecond.per(Millisecond) as i128) as f64;
- js_sys::Date::new(&timestamp.into())
- }
-}
-// endregion trait impls
diff --git a/third_party/rust/time/src/duration.rs b/third_party/rust/time/src/duration.rs
index 68a38eec78..0da8385b3c 100644
--- a/third_party/rust/time/src/duration.rs
+++ b/third_party/rust/time/src/duration.rs
@@ -1,67 +1,85 @@
//! The [`Duration`] struct and its associated `impl`s.
use core::cmp::Ordering;
use core::fmt;
use core::iter::Sum;
use core::ops::{Add, AddAssign, Div, Mul, Neg, Sub, SubAssign};
use core::time::Duration as StdDuration;
+use deranged::RangedI32;
+use num_conv::prelude::*;
+
use crate::convert::*;
use crate::error;
+use crate::internal_macros::{
+ const_try_opt, expect_opt, impl_add_assign, impl_div_assign, impl_mul_assign, impl_sub_assign,
+};
#[cfg(feature = "std")]
+#[allow(deprecated)]
use crate::Instant;
/// By explicitly inserting this enum where padding is expected, the compiler is able to better
/// perform niche value optimization.
#[repr(u32)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub(crate) enum Padding {
#[allow(clippy::missing_docs_in_private_items)]
Optimize,
}
-impl Default for Padding {
- fn default() -> Self {
- Self::Optimize
- }
-}
+/// The type of the `nanosecond` field of `Duration`.
+type Nanoseconds =
+ RangedI32<{ -(Nanosecond::per(Second) as i32 - 1) }, { Nanosecond::per(Second) as i32 - 1 }>;
/// A span of time with nanosecond precision.
///
/// Each `Duration` is composed of a whole number of seconds and a fractional part represented in
/// nanoseconds.
///
/// This implementation allows for negative durations, unlike [`core::time::Duration`].
-#[derive(Clone, Copy, Default, PartialEq, Eq, Hash, PartialOrd, Ord)]
+#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct Duration {
/// Number of whole seconds.
seconds: i64,
/// Number of nanoseconds within the second. The sign always matches the `seconds` field.
- nanoseconds: i32, // always -10^9 < nanoseconds < 10^9
+ // Sign must match that of `seconds` (though this is not a safety requirement).
+ nanoseconds: Nanoseconds,
#[allow(clippy::missing_docs_in_private_items)]
padding: Padding,
}
impl fmt::Debug for Duration {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Duration")
.field("seconds", &self.seconds)
.field("nanoseconds", &self.nanoseconds)
.finish()
}
}
-/// This is adapted from the `std` implementation, which uses mostly bit
+impl Default for Duration {
+ fn default() -> Self {
+ Self {
+ seconds: 0,
+ nanoseconds: Nanoseconds::new_static::<0>(),
+ padding: Padding::Optimize,
+ }
+ }
+}
+
+/// This is adapted from the [`std` implementation][std], which uses mostly bit
/// operations to ensure the highest precision:
-/// https://github.com/rust-lang/rust/blob/3a37c2f0523c87147b64f1b8099fc9df22e8c53e/library/core/src/time.rs#L1262-L1340
+///
/// Changes from `std` are marked and explained below.
+///
+/// [std]: https://github.com/rust-lang/rust/blob/3a37c2f0523c87147b64f1b8099fc9df22e8c53e/library/core/src/time.rs#L1262-L1340
#[rustfmt::skip] // Skip `rustfmt` because it reformats the arguments of the macro weirdly.
macro_rules! try_from_secs {
(
secs = $secs: expr,
mantissa_bits = $mant_bits: literal,
exponent_bits = $exp_bits: literal,
offset = $offset: literal,
bits_ty = $bits_ty:ty,
bits_ty_signed = $bits_ty_signed:ty,
double_ty = $double_ty:ty,
@@ -80,98 +98,99 @@ macro_rules! try_from_secs {
let mant = (bits & MANT_MASK) | (MANT_MASK + 1);
let exp = ((bits >> $mant_bits) & EXP_MASK) as i16 + MIN_EXP;
let (secs, nanos) = if exp < -31 {
// the input represents less than 1ns and can not be rounded to it
(0u64, 0u32)
} else if exp < 0 {
// the input is less than 1 second
let t = <$double_ty>::from(mant) << ($offset + exp);
let nanos_offset = $mant_bits + $offset;
- let nanos_tmp = u128::from(Nanosecond.per(Second)) * u128::from(t);
+ let nanos_tmp = u128::from(Nanosecond::per(Second)) * u128::from(t);
let nanos = (nanos_tmp >> nanos_offset) as u32;
let rem_mask = (1 << nanos_offset) - 1;
let rem_msb_mask = 1 << (nanos_offset - 1);
let rem = nanos_tmp & rem_mask;
let is_tie = rem == rem_msb_mask;
let is_even = (nanos & 1) == 0;
let rem_msb = nanos_tmp & rem_msb_mask == 0;
let add_ns = !(rem_msb || (is_even && is_tie));
// f32 does not have enough precision to trigger the second branch
// since it can not represent numbers between 0.999_999_940_395 and 1.0.
let nanos = nanos + add_ns as u32;
- if ($mant_bits == 23) || (nanos != Nanosecond.per(Second)) {
+ if ($mant_bits == 23) || (nanos != Nanosecond::per(Second)) {
(0, nanos)
} else {
(1, 0)
}
} else if exp < $mant_bits {
let secs = u64::from(mant >> ($mant_bits - exp));
let t = <$double_ty>::from((mant << exp) & MANT_MASK);
let nanos_offset = $mant_bits;
- let nanos_tmp = <$double_ty>::from(Nanosecond.per(Second)) * t;
+ let nanos_tmp = <$double_ty>::from(Nanosecond::per(Second)) * t;
let nanos = (nanos_tmp >> nanos_offset) as u32;
let rem_mask = (1 << nanos_offset) - 1;
let rem_msb_mask = 1 << (nanos_offset - 1);
let rem = nanos_tmp & rem_mask;
let is_tie = rem == rem_msb_mask;
let is_even = (nanos & 1) == 0;
let rem_msb = nanos_tmp & rem_msb_mask == 0;
let add_ns = !(rem_msb || (is_even && is_tie));
// f32 does not have enough precision to trigger the second branch.
// For example, it can not represent numbers between 1.999_999_880...
// and 2.0. Bigger values result in even smaller precision of the
// fractional part.
let nanos = nanos + add_ns as u32;
- if ($mant_bits == 23) || (nanos != Nanosecond.per(Second)) {
+ if ($mant_bits == 23) || (nanos != Nanosecond::per(Second)) {
(secs, nanos)
} else {
(secs + 1, 0)
}
} else if exp < 63 {
// Change from std: The exponent here is 63 instead of 64,
// because i64::MAX + 1 is 2^63.
// the input has no fractional part
let secs = u64::from(mant) << (exp - $mant_bits);
(secs, 0)
} else if bits == (i64::MIN as $float_ty).to_bits() {
// Change from std: Signed integers are asymmetrical in that
// iN::MIN is -iN::MAX - 1. So for example i8 covers the
// following numbers -128..=127. The check above (exp < 63)
// doesn't cover i64::MIN as that is -2^63, so we have this
// additional case to handle the asymmetry of iN::MIN.
- break 'value Self::new_unchecked(i64::MIN, 0);
+ break 'value Self::new_ranged_unchecked(i64::MIN, Nanoseconds::new_static::<0>());
} else if $secs.is_nan() {
// Change from std: std doesn't differentiate between the error
// cases.
$is_nan
} else {
$is_overflow
};
// Change from std: All the code is mostly unmodified in that it
// simply calculates an unsigned integer. Here we extract the sign
// bit and assign it to the number. We basically manually do two's
// complement here, we could also use an if and just negate the
// numbers based on the sign, but it turns out to be quite a bit
// slower.
let mask = (bits as $bits_ty_signed) >> ($mant_bits + $exp_bits);
#[allow(trivial_numeric_casts)]
let secs_signed = ((secs as i64) ^ (mask as i64)) - (mask as i64);
#[allow(trivial_numeric_casts)]
let nanos_signed = ((nanos as i32) ^ (mask as i32)) - (mask as i32);
- Self::new_unchecked(secs_signed, nanos_signed)
+ // Safety: `nanos_signed` is in range.
+ unsafe { Self::new_unchecked(secs_signed, nanos_signed) }
}
}};
}
impl Duration {
// region: constants
/// Equivalent to `0.seconds()`.
///
/// ```rust
/// # use time::{Duration, ext::NumericalDuration};
@@ -237,208 +256,267 @@ impl Duration {
/// Equivalent to `1.weeks()`.
///
/// ```rust
/// # use time::{Duration, ext::NumericalDuration};
/// assert_eq!(Duration::WEEK, 1.weeks());
/// ```
pub const WEEK: Self = Self::weeks(1);
/// The minimum possible duration. Adding any negative duration to this will cause an overflow.
- pub const MIN: Self = Self::new_unchecked(i64::MIN, -((Nanosecond.per(Second) - 1) as i32));
+ pub const MIN: Self = Self::new_ranged(i64::MIN, Nanoseconds::MIN);
/// The maximum possible duration. Adding any positive duration to this will cause an overflow.
- pub const MAX: Self = Self::new_unchecked(i64::MAX, (Nanosecond.per(Second) - 1) as _);
+ pub const MAX: Self = Self::new_ranged(i64::MAX, Nanoseconds::MAX);
// endregion constants
// region: is_{sign}
/// Check if a duration is exactly zero.
///
/// ```rust
/// # use time::ext::NumericalDuration;
/// assert!(0.seconds().is_zero());
/// assert!(!1.nanoseconds().is_zero());
/// ```
pub const fn is_zero(self) -> bool {
- self.seconds == 0 && self.nanoseconds == 0
+ self.seconds == 0 && self.nanoseconds.get() == 0
}
/// Check if a duration is negative.
///
/// ```rust
/// # use time::ext::NumericalDuration;
/// assert!((-1).seconds().is_negative());
/// assert!(!0.seconds().is_negative());
/// assert!(!1.seconds().is_negative());
/// ```
pub const fn is_negative(self) -> bool {
- self.seconds < 0 || self.nanoseconds < 0
+ self.seconds < 0 || self.nanoseconds.get() < 0
}
/// Check if a duration is positive.
///
/// ```rust
/// # use time::ext::NumericalDuration;
/// assert!(1.seconds().is_positive());
/// assert!(!0.seconds().is_positive());
/// assert!(!(-1).seconds().is_positive());
/// ```
pub const fn is_positive(self) -> bool {
- self.seconds > 0 || self.nanoseconds > 0
+ self.seconds > 0 || self.nanoseconds.get() > 0
}
// endregion is_{sign}
// region: abs
/// Get the absolute value of the duration.
///
/// This method saturates the returned value if it would otherwise overflow.
///
/// ```rust
/// # use time::ext::NumericalDuration;
/// assert_eq!(1.seconds().abs(), 1.seconds());
/// assert_eq!(0.seconds().abs(), 0.seconds());
/// assert_eq!((-1).seconds().abs(), 1.seconds());
/// ```
pub const fn abs(self) -> Self {
match self.seconds.checked_abs() {
- Some(seconds) => Self::new_unchecked(seconds, self.nanoseconds.abs()),
+ Some(seconds) => Self::new_ranged_unchecked(seconds, self.nanoseconds.abs()),
None => Self::MAX,
}
}
/// Convert the existing `Duration` to a `std::time::Duration` and its sign. This returns a
/// [`std::time::Duration`] and does not saturate the returned value (unlike [`Duration::abs`]).
///
/// ```rust
/// # use time::ext::{NumericalDuration, NumericalStdDuration};
/// assert_eq!(1.seconds().unsigned_abs(), 1.std_seconds());
/// assert_eq!(0.seconds().unsigned_abs(), 0.std_seconds());
/// assert_eq!((-1).seconds().unsigned_abs(), 1.std_seconds());
/// ```
pub const fn unsigned_abs(self) -> StdDuration {
- StdDuration::new(self.seconds.unsigned_abs(), self.nanoseconds.unsigned_abs())
+ StdDuration::new(
+ self.seconds.unsigned_abs(),
+ self.nanoseconds.get().unsigned_abs(),
+ )
}
// endregion abs
// region: constructors
/// Create a new `Duration` without checking the validity of the components.
- pub(crate) const fn new_unchecked(seconds: i64, nanoseconds: i32) -> Self {
+ ///
+ /// # Safety
+ ///
+ /// - `nanoseconds` must be in the range `-999_999_999..=999_999_999`.
+ ///
+ /// While the sign of `nanoseconds` is required to be the same as the sign of `seconds`, this is
+ /// not a safety invariant.
+ pub(crate) const unsafe fn new_unchecked(seconds: i64, nanoseconds: i32) -> Self {
+ Self::new_ranged_unchecked(
+ seconds,
+ // Safety: The caller must uphold the safety invariants.
+ unsafe { Nanoseconds::new_unchecked(nanoseconds) },
+ )
+ }
+
+ /// Create a new `Duration` without checking the validity of the components.
+ pub(crate) const fn new_ranged_unchecked(seconds: i64, nanoseconds: Nanoseconds) -> Self {
if seconds < 0 {
- debug_assert!(nanoseconds <= 0);
- debug_assert!(nanoseconds > -(Nanosecond.per(Second) as i32));
+ debug_assert!(nanoseconds.get() <= 0);
} else if seconds > 0 {
- debug_assert!(nanoseconds >= 0);
- debug_assert!(nanoseconds < Nanosecond.per(Second) as _);
- } else {
- debug_assert!(nanoseconds.unsigned_abs() < Nanosecond.per(Second));
+ debug_assert!(nanoseconds.get() >= 0);
}
Self {
seconds,
nanoseconds,
padding: Padding::Optimize,
}
}
/// Create a new `Duration` with the provided seconds and nanoseconds. If nanoseconds is at
/// least ±10<sup>9</sup>, it will wrap to the number of seconds.
///
/// ```rust
/// # use time::{Duration, ext::NumericalDuration};
/// assert_eq!(Duration::new(1, 0), 1.seconds());
/// assert_eq!(Duration::new(-1, 0), (-1).seconds());
/// assert_eq!(Duration::new(1, 2_000_000_000), 3.seconds());
/// ```
+ ///
+ /// # Panics
+ ///
+ /// This may panic if an overflow occurs.
pub const fn new(mut seconds: i64, mut nanoseconds: i32) -> Self {
seconds = expect_opt!(
- seconds.checked_add(nanoseconds as i64 / Nanosecond.per(Second) as i64),
+ seconds.checked_add(nanoseconds as i64 / Nanosecond::per(Second) as i64),
"overflow constructing `time::Duration`"
);
- nanoseconds %= Nanosecond.per(Second) as i32;
+ nanoseconds %= Nanosecond::per(Second) as i32;
if seconds > 0 && nanoseconds < 0 {
// `seconds` cannot overflow here because it is positive.
seconds -= 1;
- nanoseconds += Nanosecond.per(Second) as i32;
+ nanoseconds += Nanosecond::per(Second) as i32;
} else if seconds < 0 && nanoseconds > 0 {
// `seconds` cannot overflow here because it is negative.
seconds += 1;
- nanoseconds -= Nanosecond.per(Second) as i32;
+ nanoseconds -= Nanosecond::per(Second) as i32;
+ }
+
+ // Safety: `nanoseconds` is in range due to the modulus above.
+ unsafe { Self::new_unchecked(seconds, nanoseconds) }
+ }
+
+ /// Create a new `Duration` with the provided seconds and nanoseconds.
+ pub(crate) const fn new_ranged(mut seconds: i64, mut nanoseconds: Nanoseconds) -> Self {
+ if seconds > 0 && nanoseconds.get() < 0 {
+ // `seconds` cannot overflow here because it is positive.
+ seconds -= 1;
+ // Safety: `nanoseconds` is negative with a maximum of 999,999,999, so adding a billion
+ // to it is guaranteed to result in an in-range value.
+ nanoseconds = unsafe {
+ Nanoseconds::new_unchecked(nanoseconds.get() + Nanosecond::per(Second) as i32)
+ };
+ } else if seconds < 0 && nanoseconds.get() > 0 {
+ // `seconds` cannot overflow here because it is negative.
+ seconds += 1;
+ // Safety: `nanoseconds` is positive with a minimum of -999,999,999, so subtracting a
+ // billion from it is guaranteed to result in an in-range value.
+ nanoseconds = unsafe {
+ Nanoseconds::new_unchecked(nanoseconds.get() - Nanosecond::per(Second) as i32)
+ };
}
- Self::new_unchecked(seconds, nanoseconds)
+ Self::new_ranged_unchecked(seconds, nanoseconds)
}
/// Create a new `Duration` with the given number of weeks. Equivalent to
/// `Duration::seconds(weeks * 604_800)`.
///
/// ```rust
/// # use time::{Duration, ext::NumericalDuration};
/// assert_eq!(Duration::weeks(1), 604_800.seconds());
/// ```
+ ///
+ /// # Panics
+ ///
+ /// This may panic if an overflow occurs.
pub const fn weeks(weeks: i64) -> Self {
Self::seconds(expect_opt!(
- weeks.checked_mul(Second.per(Week) as _),
+ weeks.checked_mul(Second::per(Week) as _),
"overflow constructing `time::Duration`"
))
}
/// Create a new `Duration` with the given number of days. Equivalent to
/// `Duration::seconds(days * 86_400)`.
///
/// ```rust
/// # use time::{Duration, ext::NumericalDuration};
/// assert_eq!(Duration::days(1), 86_400.seconds());
/// ```
+ ///
+ /// # Panics
+ ///
+ /// This may panic if an overflow occurs.
pub const fn days(days: i64) -> Self {
Self::seconds(expect_opt!(
- days.checked_mul(Second.per(Day) as _),
+ days.checked_mul(Second::per(Day) as _),
"overflow constructing `time::Duration`"
))
}
/// Create a new `Duration` with the given number of hours. Equivalent to
/// `Duration::seconds(hours * 3_600)`.
///
/// ```rust
/// # use time::{Duration, ext::NumericalDuration};
/// assert_eq!(Duration::hours(1), 3_600.seconds());
/// ```
+ ///
+ /// # Panics
+ ///
+ /// This may panic if an overflow occurs.
pub const fn hours(hours: i64) -> Self {
Self::seconds(expect_opt!(
- hours.checked_mul(Second.per(Hour) as _),
+ hours.checked_mul(Second::per(Hour) as _),
"overflow constructing `time::Duration`"
))
}
/// Create a new `Duration` with the given number of minutes. Equivalent to
/// `Duration::seconds(minutes * 60)`.
///
/// ```rust
/// # use time::{Duration, ext::NumericalDuration};
/// assert_eq!(Duration::minutes(1), 60.seconds());
/// ```
+ ///
+ /// # Panics
+ ///
+ /// This may panic if an overflow occurs.
pub const fn minutes(minutes: i64) -> Self {
Self::seconds(expect_opt!(
- minutes.checked_mul(Second.per(Minute) as _),
+ minutes.checked_mul(Second::per(Minute) as _),
"overflow constructing `time::Duration`"
))
}
/// Create a new `Duration` with the given number of seconds.
///
/// ```rust
/// # use time::{Duration, ext::NumericalDuration};
/// assert_eq!(Duration::seconds(1), 1_000.milliseconds());
/// ```
pub const fn seconds(seconds: i64) -> Self {
- Self::new_unchecked(seconds, 0)
+ Self::new_ranged_unchecked(seconds, Nanoseconds::new_static::<0>())
}
/// Creates a new `Duration` from the specified number of seconds represented as `f64`.
///
/// ```rust
/// # use time::{Duration, ext::NumericalDuration};
/// assert_eq!(Duration::seconds_f64(0.5), 0.5.seconds());
/// assert_eq!(Duration::seconds_f64(-0.5), -0.5.seconds());
/// ```
pub fn seconds_f64(seconds: f64) -> Self {
@@ -607,123 +685,133 @@ impl Duration {
}
/// Create a new `Duration` with the given number of milliseconds.
///
/// ```rust
/// # use time::{Duration, ext::NumericalDuration};
/// assert_eq!(Duration::milliseconds(1), 1_000.microseconds());
/// assert_eq!(Duration::milliseconds(-1), (-1_000).microseconds());
/// ```
pub const fn milliseconds(milliseconds: i64) -> Self {
- Self::new_unchecked(
- milliseconds / Millisecond.per(Second) as i64,
- (milliseconds % Millisecond.per(Second) as i64 * Nanosecond.per(Millisecond) as i64)
- as _,
- )
+ // Safety: `nanoseconds` is guaranteed to be in range because of the modulus.
+ unsafe {
+ Self::new_unchecked(
+ milliseconds / Millisecond::per(Second) as i64,
+ (milliseconds % Millisecond::per(Second) as i64
+ * Nanosecond::per(Millisecond) as i64) as _,
+ )
+ }
}
/// Create a new `Duration` with the given number of microseconds.
///
/// ```rust
/// # use time::{Duration, ext::NumericalDuration};
/// assert_eq!(Duration::microseconds(1), 1_000.nanoseconds());
/// assert_eq!(Duration::microseconds(-1), (-1_000).nanoseconds());
/// ```
pub const fn microseconds(microseconds: i64) -> Self {
- Self::new_unchecked(
- microseconds / Microsecond.per(Second) as i64,
- (microseconds % Microsecond.per(Second) as i64 * Nanosecond.per(Microsecond) as i64)
- as _,
- )
+ // Safety: `nanoseconds` is guaranteed to be in range because of the modulus.
+ unsafe {
+ Self::new_unchecked(
+ microseconds / Microsecond::per(Second) as i64,
+ (microseconds % Microsecond::per(Second) as i64
+ * Nanosecond::per(Microsecond) as i64) as _,
+ )
+ }
}
/// Create a new `Duration` with the given number of nanoseconds.
///
/// ```rust
/// # use time::{Duration, ext::NumericalDuration};
/// assert_eq!(Duration::nanoseconds(1), 1.microseconds() / 1_000);
/// assert_eq!(Duration::nanoseconds(-1), (-1).microseconds() / 1_000);
/// ```
pub const fn nanoseconds(nanoseconds: i64) -> Self {
- Self::new_unchecked(
- nanoseconds / Nanosecond.per(Second) as i64,
- (nanoseconds % Nanosecond.per(Second) as i64) as _,
- )
+ // Safety: `nanoseconds` is guaranteed to be in range because of the modulus.
+ unsafe {
+ Self::new_unchecked(
+ nanoseconds / Nanosecond::per(Second) as i64,
+ (nanoseconds % Nanosecond::per(Second) as i64) as _,
+ )
+ }
}
/// Create a new `Duration` with the given number of nanoseconds.
///
/// As the input range cannot be fully mapped to the output, this should only be used where it's
/// known to result in a valid value.
pub(crate) const fn nanoseconds_i128(nanoseconds: i128) -> Self {
- let seconds = nanoseconds / Nanosecond.per(Second) as i128;
- let nanoseconds = nanoseconds % Nanosecond.per(Second) as i128;
+ let seconds = nanoseconds / Nanosecond::per(Second) as i128;
+ let nanoseconds = nanoseconds % Nanosecond::per(Second) as i128;
if seconds > i64::MAX as i128 || seconds < i64::MIN as i128 {
crate::expect_failed("overflow constructing `time::Duration`");
}
- Self::new_unchecked(seconds as _, nanoseconds as _)
+ // Safety: `nanoseconds` is guaranteed to be in range because of the modulus above.
+ unsafe { Self::new_unchecked(seconds as _, nanoseconds as _) }
}
// endregion constructors
// region: getters
/// Get the number of whole weeks in the duration.
///
/// ```rust
/// # use time::ext::NumericalDuration;
/// assert_eq!(1.weeks().whole_weeks(), 1);
/// assert_eq!((-1).weeks().whole_weeks(), -1);
/// assert_eq!(6.days().whole_weeks(), 0);
/// assert_eq!((-6).days().whole_weeks(), 0);
/// ```
pub const fn whole_weeks(self) -> i64 {
- self.whole_seconds() / Second.per(Week) as i64
+ self.whole_seconds() / Second::per(Week) as i64
}
/// Get the number of whole days in the duration.
///
/// ```rust
/// # use time::ext::NumericalDuration;
/// assert_eq!(1.days().whole_days(), 1);
/// assert_eq!((-1).days().whole_days(), -1);
/// assert_eq!(23.hours().whole_days(), 0);
/// assert_eq!((-23).hours().whole_days(), 0);
/// ```
pub const fn whole_days(self) -> i64 {
- self.whole_seconds() / Second.per(Day) as i64
+ self.whole_seconds() / Second::per(Day) as i64
}
/// Get the number of whole hours in the duration.
///
/// ```rust
/// # use time::ext::NumericalDuration;
/// assert_eq!(1.hours().whole_hours(), 1);
/// assert_eq!((-1).hours().whole_hours(), -1);
/// assert_eq!(59.minutes().whole_hours(), 0);
/// assert_eq!((-59).minutes().whole_hours(), 0);
/// ```
pub const fn whole_hours(self) -> i64 {
- self.whole_seconds() / Second.per(Hour) as i64
+ self.whole_seconds() / Second::per(Hour) as i64
}
/// Get the number of whole minutes in the duration.
///
/// ```rust
/// # use time::ext::NumericalDuration;
/// assert_eq!(1.minutes().whole_minutes(), 1);
/// assert_eq!((-1).minutes().whole_minutes(), -1);
/// assert_eq!(59.seconds().whole_minutes(), 0);
/// assert_eq!((-59).seconds().whole_minutes(), 0);
/// ```
pub const fn whole_minutes(self) -> i64 {
- self.whole_seconds() / Second.per(Minute) as i64
+ self.whole_seconds() / Second::per(Minute) as i64
}
/// Get the number of whole seconds in the duration.
///
/// ```rust
/// # use time::ext::NumericalDuration;
/// assert_eq!(1.seconds().whole_seconds(), 1);
/// assert_eq!((-1).seconds().whole_seconds(), -1);
/// assert_eq!(1.minutes().whole_seconds(), 60);
/// assert_eq!((-1).minutes().whole_seconds(), -60);
@@ -733,203 +821,234 @@ impl Duration {
}
/// Get the number of fractional seconds in the duration.
///
/// ```rust
/// # use time::ext::NumericalDuration;
/// assert_eq!(1.5.seconds().as_seconds_f64(), 1.5);
/// assert_eq!((-1.5).seconds().as_seconds_f64(), -1.5);
/// ```
pub fn as_seconds_f64(self) -> f64 {
- self.seconds as f64 + self.nanoseconds as f64 / Nanosecond.per(Second) as f64
+ self.seconds as f64 + self.nanoseconds.get() as f64 / Nanosecond::per(Second) as f64
}
/// Get the number of fractional seconds in the duration.
///
/// ```rust
/// # use time::ext::NumericalDuration;
/// assert_eq!(1.5.seconds().as_seconds_f32(), 1.5);
/// assert_eq!((-1.5).seconds().as_seconds_f32(), -1.5);
/// ```
pub fn as_seconds_f32(self) -> f32 {
- self.seconds as f32 + self.nanoseconds as f32 / Nanosecond.per(Second) as f32
+ self.seconds as f32 + self.nanoseconds.get() as f32 / Nanosecond::per(Second) as f32
}
/// Get the number of whole milliseconds in the duration.
///
/// ```rust
/// # use time::ext::NumericalDuration;
/// assert_eq!(1.seconds().whole_milliseconds(), 1_000);
/// assert_eq!((-1).seconds().whole_milliseconds(), -1_000);
/// assert_eq!(1.milliseconds().whole_milliseconds(), 1);
/// assert_eq!((-1).milliseconds().whole_milliseconds(), -1);
/// ```
pub const fn whole_milliseconds(self) -> i128 {
- self.seconds as i128 * Millisecond.per(Second) as i128
- + self.nanoseconds as i128 / Nanosecond.per(Millisecond) as i128
+ self.seconds as i128 * Millisecond::per(Second) as i128
+ + self.nanoseconds.get() as i128 / Nanosecond::per(Millisecond) as i128
}
/// Get the number of milliseconds past the number of whole seconds.
///
- /// Always in the range `-1_000..1_000`.
+ /// Always in the range `-999..=999`.
///
/// ```rust
/// # use time::ext::NumericalDuration;
/// assert_eq!(1.4.seconds().subsec_milliseconds(), 400);
/// assert_eq!((-1.4).seconds().subsec_milliseconds(), -400);
/// ```
// Allow the lint, as the value is guaranteed to be less than 1000.
pub const fn subsec_milliseconds(self) -> i16 {
- (self.nanoseconds / Nanosecond.per(Millisecond) as i32) as _
+ (self.nanoseconds.get() / Nanosecond::per(Millisecond) as i32) as _
}
/// Get the number of whole microseconds in the duration.
///
/// ```rust
/// # use time::ext::NumericalDuration;
/// assert_eq!(1.milliseconds().whole_microseconds(), 1_000);
/// assert_eq!((-1).milliseconds().whole_microseconds(), -1_000);
/// assert_eq!(1.microseconds().whole_microseconds(), 1);
/// assert_eq!((-1).microseconds().whole_microseconds(), -1);
/// ```
pub const fn whole_microseconds(self) -> i128 {
- self.seconds as i128 * Microsecond.per(Second) as i128
- + self.nanoseconds as i128 / Nanosecond.per(Microsecond) as i128
+ self.seconds as i128 * Microsecond::per(Second) as i128
+ + self.nanoseconds.get() as i128 / Nanosecond::per(Microsecond) as i128
}
/// Get the number of microseconds past the number of whole seconds.
///
- /// Always in the range `-1_000_000..1_000_000`.
+ /// Always in the range `-999_999..=999_999`.
///
/// ```rust
/// # use time::ext::NumericalDuration;
/// assert_eq!(1.0004.seconds().subsec_microseconds(), 400);
/// assert_eq!((-1.0004).seconds().subsec_microseconds(), -400);
/// ```
pub const fn subsec_microseconds(self) -> i32 {
- self.nanoseconds / Nanosecond.per(Microsecond) as i32
+ self.nanoseconds.get() / Nanosecond::per(Microsecond) as i32
}
/// Get the number of nanoseconds in the duration.
///
/// ```rust
/// # use time::ext::NumericalDuration;
/// assert_eq!(1.microseconds().whole_nanoseconds(), 1_000);
/// assert_eq!((-1).microseconds().whole_nanoseconds(), -1_000);
/// assert_eq!(1.nanoseconds().whole_nanoseconds(), 1);
/// assert_eq!((-1).nanoseconds().whole_nanoseconds(), -1);
/// ```
pub const fn whole_nanoseconds(self) -> i128 {
- self.seconds as i128 * Nanosecond.per(Second) as i128 + self.nanoseconds as i128
+ self.seconds as i128 * Nanosecond::per(Second) as i128 + self.nanoseconds.get() as i128
}
/// Get the number of nanoseconds past the number of whole seconds.
///
- /// The returned value will always be in the range `-1_000_000_000..1_000_000_000`.
+ /// The returned value will always be in the range `-999_999_999..=999_999_999`.
///
/// ```rust
/// # use time::ext::NumericalDuration;
/// assert_eq!(1.000_000_400.seconds().subsec_nanoseconds(), 400);
/// assert_eq!((-1.000_000_400).seconds().subsec_nanoseconds(), -400);
/// ```
pub const fn subsec_nanoseconds(self) -> i32 {
+ self.nanoseconds.get()
+ }
+
+ /// Get the number of nanoseconds past the number of whole seconds.
+ #[cfg(feature = "quickcheck")]
+ pub(crate) const fn subsec_nanoseconds_ranged(self) -> Nanoseconds {
self.nanoseconds
}
// endregion getters
// region: checked arithmetic
/// Computes `self + rhs`, returning `None` if an overflow occurred.
///
/// ```rust
/// # use time::{Duration, ext::NumericalDuration};
/// assert_eq!(5.seconds().checked_add(5.seconds()), Some(10.seconds()));
/// assert_eq!(Duration::MAX.checked_add(1.nanoseconds()), None);
/// assert_eq!((-5).seconds().checked_add(5.seconds()), Some(0.seconds()));
/// ```
pub const fn checked_add(self, rhs: Self) -> Option<Self> {
let mut seconds = const_try_opt!(self.seconds.checked_add(rhs.seconds));
- let mut nanoseconds = self.nanoseconds + rhs.nanoseconds;
+ let mut nanoseconds = self.nanoseconds.get() + rhs.nanoseconds.get();
- if nanoseconds >= Nanosecond.per(Second) as _ || seconds < 0 && nanoseconds > 0 {
- nanoseconds -= Nanosecond.per(Second) as i32;
+ if nanoseconds >= Nanosecond::per(Second) as _ || seconds < 0 && nanoseconds > 0 {
+ nanoseconds -= Nanosecond::per(Second) as i32;
seconds = const_try_opt!(seconds.checked_add(1));
- } else if nanoseconds <= -(Nanosecond.per(Second) as i32) || seconds > 0 && nanoseconds < 0
+ } else if nanoseconds <= -(Nanosecond::per(Second) as i32) || seconds > 0 && nanoseconds < 0
{
- nanoseconds += Nanosecond.per(Second) as i32;
+ nanoseconds += Nanosecond::per(Second) as i32;
seconds = const_try_opt!(seconds.checked_sub(1));
}
- Some(Self::new_unchecked(seconds, nanoseconds))
+ // Safety: `nanoseconds` is guaranteed to be in range because of the overflow handling.
+ unsafe { Some(Self::new_unchecked(seconds, nanoseconds)) }
}
/// Computes `self - rhs`, returning `None` if an overflow occurred.
///
/// ```rust
/// # use time::{Duration, ext::NumericalDuration};
/// assert_eq!(5.seconds().checked_sub(5.seconds()), Some(Duration::ZERO));
/// assert_eq!(Duration::MIN.checked_sub(1.nanoseconds()), None);
/// assert_eq!(5.seconds().checked_sub(10.seconds()), Some((-5).seconds()));
/// ```
pub const fn checked_sub(self, rhs: Self) -> Option<Self> {
let mut seconds = const_try_opt!(self.seconds.checked_sub(rhs.seconds));
- let mut nanoseconds = self.nanoseconds - rhs.nanoseconds;
+ let mut nanoseconds = self.nanoseconds.get() - rhs.nanoseconds.get();
- if nanoseconds >= Nanosecond.per(Second) as _ || seconds < 0 && nanoseconds > 0 {
- nanoseconds -= Nanosecond.per(Second) as i32;
+ if nanoseconds >= Nanosecond::per(Second) as _ || seconds < 0 && nanoseconds > 0 {
+ nanoseconds -= Nanosecond::per(Second) as i32;
seconds = const_try_opt!(seconds.checked_add(1));
- } else if nanoseconds <= -(Nanosecond.per(Second) as i32) || seconds > 0 && nanoseconds < 0
+ } else if nanoseconds <= -(Nanosecond::per(Second) as i32) || seconds > 0 && nanoseconds < 0
{
- nanoseconds += Nanosecond.per(Second) as i32;
+ nanoseconds += Nanosecond::per(Second) as i32;
seconds = const_try_opt!(seconds.checked_sub(1));
}
- Some(Self::new_unchecked(seconds, nanoseconds))
+ // Safety: `nanoseconds` is guaranteed to be in range because of the overflow handling.
+ unsafe { Some(Self::new_unchecked(seconds, nanoseconds)) }
}
/// Computes `self * rhs`, returning `None` if an overflow occurred.
///
/// ```rust
/// # use time::{Duration, ext::NumericalDuration};
/// assert_eq!(5.seconds().checked_mul(2), Some(10.seconds()));
/// assert_eq!(5.seconds().checked_mul(-2), Some((-10).seconds()));
/// assert_eq!(5.seconds().checked_mul(0), Some(0.seconds()));
/// assert_eq!(Duration::MAX.checked_mul(2), None);
/// assert_eq!(Duration::MIN.checked_mul(2), None);
/// ```
pub const fn checked_mul(self, rhs: i32) -> Option<Self> {
// Multiply nanoseconds as i64, because it cannot overflow that way.
- let total_nanos = self.nanoseconds as i64 * rhs as i64;
- let extra_secs = total_nanos / Nanosecond.per(Second) as i64;
- let nanoseconds = (total_nanos % Nanosecond.per(Second) as i64) as _;
+ let total_nanos = self.nanoseconds.get() as i64 * rhs as i64;
+ let extra_secs = total_nanos / Nanosecond::per(Second) as i64;
+ let nanoseconds = (total_nanos % Nanosecond::per(Second) as i64) as _;
let seconds = const_try_opt!(
const_try_opt!(self.seconds.checked_mul(rhs as _)).checked_add(extra_secs)
);
- Some(Self::new_unchecked(seconds, nanoseconds))
+ // Safety: `nanoseconds` is guaranteed to be in range because of the modulus above.
+ unsafe { Some(Self::new_unchecked(seconds, nanoseconds)) }
}
/// Computes `self / rhs`, returning `None` if `rhs == 0` or if the result would overflow.
///
/// ```rust
/// # use time::ext::NumericalDuration;
/// assert_eq!(10.seconds().checked_div(2), Some(5.seconds()));
/// assert_eq!(10.seconds().checked_div(-2), Some((-5).seconds()));
/// assert_eq!(1.seconds().checked_div(0), None);
/// ```
pub const fn checked_div(self, rhs: i32) -> Option<Self> {
- let seconds = const_try_opt!(self.seconds.checked_div(rhs as i64));
- let carry = self.seconds - seconds * (rhs as i64);
- let extra_nanos =
- const_try_opt!((carry * Nanosecond.per(Second) as i64).checked_div(rhs as i64));
- let nanoseconds = const_try_opt!(self.nanoseconds.checked_div(rhs)) + (extra_nanos as i32);
+ let (secs, extra_secs) = (
+ const_try_opt!(self.seconds.checked_div(rhs as i64)),
+ self.seconds % (rhs as i64),
+ );
+ let (mut nanos, extra_nanos) = (self.nanoseconds.get() / rhs, self.nanoseconds.get() % rhs);
+ nanos += ((extra_secs * (Nanosecond::per(Second) as i64) + extra_nanos as i64)
+ / (rhs as i64)) as i32;
+
+ // Safety: `nanoseconds` is in range.
+ unsafe { Some(Self::new_unchecked(secs, nanos)) }
+ }
- Some(Self::new_unchecked(seconds, nanoseconds))
+ /// Computes `-self`, returning `None` if the result would overflow.
+ ///
+ /// ```rust
+ /// # use time::ext::NumericalDuration;
+ /// # use time::Duration;
+ /// assert_eq!(5.seconds().checked_neg(), Some((-5).seconds()));
+ /// assert_eq!(Duration::MIN.checked_neg(), None);
+ /// ```
+ pub const fn checked_neg(self) -> Option<Self> {
+ if self.seconds == i64::MIN {
+ None
+ } else {
+ Some(Self::new_ranged_unchecked(
+ -self.seconds,
+ self.nanoseconds.neg(),
+ ))
+ }
}
// endregion checked arithmetic
// region: saturating arithmetic
/// Computes `self + rhs`, saturating if an overflow occurred.
///
/// ```rust
/// # use time::{Duration, ext::NumericalDuration};
/// assert_eq!(5.seconds().saturating_add(5.seconds()), 10.seconds());
/// assert_eq!(Duration::MAX.saturating_add(1.nanoseconds()), Duration::MAX);
@@ -940,38 +1059,39 @@ impl Duration {
/// assert_eq!((-5).seconds().saturating_add(5.seconds()), Duration::ZERO);
/// ```
pub const fn saturating_add(self, rhs: Self) -> Self {
let (mut seconds, overflow) = self.seconds.overflowing_add(rhs.seconds);
if overflow {
if self.seconds > 0 {
return Self::MAX;
}
return Self::MIN;
}
- let mut nanoseconds = self.nanoseconds + rhs.nanoseconds;
+ let mut nanoseconds = self.nanoseconds.get() + rhs.nanoseconds.get();
- if nanoseconds >= Nanosecond.per(Second) as _ || seconds < 0 && nanoseconds > 0 {
- nanoseconds -= Nanosecond.per(Second) as i32;
+ if nanoseconds >= Nanosecond::per(Second) as _ || seconds < 0 && nanoseconds > 0 {
+ nanoseconds -= Nanosecond::per(Second) as i32;
seconds = match seconds.checked_add(1) {
Some(seconds) => seconds,
None => return Self::MAX,
};
- } else if nanoseconds <= -(Nanosecond.per(Second) as i32) || seconds > 0 && nanoseconds < 0
+ } else if nanoseconds <= -(Nanosecond::per(Second) as i32) || seconds > 0 && nanoseconds < 0
{
- nanoseconds += Nanosecond.per(Second) as i32;
+ nanoseconds += Nanosecond::per(Second) as i32;
seconds = match seconds.checked_sub(1) {
Some(seconds) => seconds,
None => return Self::MIN,
};
}
- Self::new_unchecked(seconds, nanoseconds)
+ // Safety: `nanoseconds` is guaranteed to be in range because of the overflow handling.
+ unsafe { Self::new_unchecked(seconds, nanoseconds) }
}
/// Computes `self - rhs`, saturating if an overflow occurred.
///
/// ```rust
/// # use time::{Duration, ext::NumericalDuration};
/// assert_eq!(5.seconds().saturating_sub(5.seconds()), Duration::ZERO);
/// assert_eq!(Duration::MIN.saturating_sub(1.nanoseconds()), Duration::MIN);
/// assert_eq!(
/// Duration::MAX.saturating_sub((-1).nanoseconds()),
@@ -980,79 +1100,86 @@ impl Duration {
/// assert_eq!(5.seconds().saturating_sub(10.seconds()), (-5).seconds());
/// ```
pub const fn saturating_sub(self, rhs: Self) -> Self {
let (mut seconds, overflow) = self.seconds.overflowing_sub(rhs.seconds);
if overflow {
if self.seconds > 0 {
return Self::MAX;
}
return Self::MIN;
}
- let mut nanoseconds = self.nanoseconds - rhs.nanoseconds;
+ let mut nanoseconds = self.nanoseconds.get() - rhs.nanoseconds.get();
- if nanoseconds >= Nanosecond.per(Second) as _ || seconds < 0 && nanoseconds > 0 {
- nanoseconds -= Nanosecond.per(Second) as i32;
+ if nanoseconds >= Nanosecond::per(Second) as _ || seconds < 0 && nanoseconds > 0 {
+ nanoseconds -= Nanosecond::per(Second) as i32;
seconds = match seconds.checked_add(1) {
Some(seconds) => seconds,
None => return Self::MAX,
};
- } else if nanoseconds <= -(Nanosecond.per(Second) as i32) || seconds > 0 && nanoseconds < 0
+ } else if nanoseconds <= -(Nanosecond::per(Second) as i32) || seconds > 0 && nanoseconds < 0
{
- nanoseconds += Nanosecond.per(Second) as i32;
+ nanoseconds += Nanosecond::per(Second) as i32;
seconds = match seconds.checked_sub(1) {
Some(seconds) => seconds,
None => return Self::MIN,
};
}
- Self::new_unchecked(seconds, nanoseconds)
+ // Safety: `nanoseconds` is guaranteed to be in range because of the overflow handling.
+ unsafe { Self::new_unchecked(seconds, nanoseconds) }
}
/// Computes `self * rhs`, saturating if an overflow occurred.
///
/// ```rust
/// # use time::{Duration, ext::NumericalDuration};
/// assert_eq!(5.seconds().saturating_mul(2), 10.seconds());
/// assert_eq!(5.seconds().saturating_mul(-2), (-10).seconds());
/// assert_eq!(5.seconds().saturating_mul(0), Duration::ZERO);
/// assert_eq!(Duration::MAX.saturating_mul(2), Duration::MAX);
/// assert_eq!(Duration::MIN.saturating_mul(2), Duration::MIN);
/// assert_eq!(Duration::MAX.saturating_mul(-2), Duration::MIN);
/// assert_eq!(Duration::MIN.saturating_mul(-2), Duration::MAX);
/// ```
pub const fn saturating_mul(self, rhs: i32) -> Self {
// Multiply nanoseconds as i64, because it cannot overflow that way.
- let total_nanos = self.nanoseconds as i64 * rhs as i64;
- let extra_secs = total_nanos / Nanosecond.per(Second) as i64;
- let nanoseconds = (total_nanos % Nanosecond.per(Second) as i64) as _;
+ let total_nanos = self.nanoseconds.get() as i64 * rhs as i64;
+ let extra_secs = total_nanos / Nanosecond::per(Second) as i64;
+ let nanoseconds = (total_nanos % Nanosecond::per(Second) as i64) as _;
let (seconds, overflow1) = self.seconds.overflowing_mul(rhs as _);
if overflow1 {
if self.seconds > 0 && rhs > 0 || self.seconds < 0 && rhs < 0 {
return Self::MAX;
}
return Self::MIN;
}
let (seconds, overflow2) = seconds.overflowing_add(extra_secs);
if overflow2 {
if self.seconds > 0 && rhs > 0 {
return Self::MAX;
}
return Self::MIN;
}
- Self::new_unchecked(seconds, nanoseconds)
+ // Safety: `nanoseconds` is guaranteed to be in range because of to the modulus above.
+ unsafe { Self::new_unchecked(seconds, nanoseconds) }
}
// endregion saturating arithmetic
/// Runs a closure, returning the duration of time it took to run. The return value of the
/// closure is provided in the second part of the tuple.
#[cfg(feature = "std")]
+ #[deprecated(
+ since = "0.3.32",
+ note = "extremely limited use case, not intended for benchmarking"
+ )]
+ #[allow(deprecated)]
pub fn time_fn<T>(f: impl FnOnce() -> T) -> (Self, T) {
let start = Instant::now();
let return_value = f();
let end = Instant::now();
(end - start, return_value)
}
}
// region: trait impls
@@ -1090,213 +1217,240 @@ impl fmt::Display for Duration {
let value = $value;
if value >= 1.0 {
return value.fmt(f).and_then(|_| f.write_str($name));
}
};
}
// Even if this produces a de-normal float, because we're rounding we don't really care.
let seconds = self.unsigned_abs().as_secs_f64();
- item!("d", seconds / Second.per(Day) as f64);
- item!("h", seconds / Second.per(Hour) as f64);
- item!("m", seconds / Second.per(Minute) as f64);
+ item!("d", seconds / Second::per(Day) as f64);
+ item!("h", seconds / Second::per(Hour) as f64);
+ item!("m", seconds / Second::per(Minute) as f64);
item!("s", seconds);
- item!("ms", seconds * Millisecond.per(Second) as f64);
- item!("µs", seconds * Microsecond.per(Second) as f64);
- item!("ns", seconds * Nanosecond.per(Second) as f64);
+ item!("ms", seconds * Millisecond::per(Second) as f64);
+ item!("µs", seconds * Microsecond::per(Second) as f64);
+ item!("ns", seconds * Nanosecond::per(Second) as f64);
} else {
// Precise, but verbose representation.
if self.is_zero() {
return f.write_str("0s");
}
/// Format a single item.
macro_rules! item {
($name:literal, $value:expr) => {
match $value {
0 => Ok(()),
value => value.fmt(f).and_then(|_| f.write_str($name)),
}
};
}
let seconds = self.seconds.unsigned_abs();
- let nanoseconds = self.nanoseconds.unsigned_abs();
+ let nanoseconds = self.nanoseconds.get().unsigned_abs();
- item!("d", seconds / Second.per(Day) as u64)?;
+ item!("d", seconds / Second::per(Day).extend::<u64>())?;
item!(
"h",
- seconds / Second.per(Hour) as u64 % Hour.per(Day) as u64
+ seconds / Second::per(Hour).extend::<u64>() % Hour::per(Day).extend::<u64>()
)?;
item!(
"m",
- seconds / Second.per(Minute) as u64 % Minute.per(Hour) as u64
+ seconds / Second::per(Minute).extend::<u64>() % Minute::per(Hour).extend::<u64>()
)?;
- item!("s", seconds % Second.per(Minute) as u64)?;
- item!("ms", nanoseconds / Nanosecond.per(Millisecond))?;
+ item!("s", seconds % Second::per(Minute).extend::<u64>())?;
+ item!("ms", nanoseconds / Nanosecond::per(Millisecond))?;
item!(
"µs",
- nanoseconds / Nanosecond.per(Microsecond) as u32
- % Microsecond.per(Millisecond) as u32
+ nanoseconds / Nanosecond::per(Microsecond).extend::<u32>()
+ % Microsecond::per(Millisecond).extend::<u32>()
+ )?;
+ item!(
+ "ns",
+ nanoseconds % Nanosecond::per(Microsecond).extend::<u32>()
)?;
- item!("ns", nanoseconds % Nanosecond.per(Microsecond) as u32)?;
}
Ok(())
}
}
impl TryFrom<StdDuration> for Duration {
type Error = error::ConversionRange;
fn try_from(original: StdDuration) -> Result<Self, error::ConversionRange> {
Ok(Self::new(
original
.as_secs()
.try_into()
.map_err(|_| error::ConversionRange)?,
- original.subsec_nanos() as _,
+ original.subsec_nanos().cast_signed(),
))
}
}
impl TryFrom<Duration> for StdDuration {
type Error = error::ConversionRange;
fn try_from(duration: Duration) -> Result<Self, error::ConversionRange> {
Ok(Self::new(
duration
.seconds
.try_into()
.map_err(|_| error::ConversionRange)?,
duration
.nanoseconds
+ .get()
.try_into()
.map_err(|_| error::ConversionRange)?,
))
}
}
impl Add for Duration {
type Output = Self;
+ /// # Panics
+ ///
+ /// This may panic if an overflow occurs.
fn add(self, rhs: Self) -> Self::Output {
self.checked_add(rhs)
.expect("overflow when adding durations")
}
}
impl Add<StdDuration> for Duration {
type Output = Self;
+ /// # Panics
+ ///
+ /// This may panic if an overflow occurs.
fn add(self, std_duration: StdDuration) -> Self::Output {
self + Self::try_from(std_duration)
.expect("overflow converting `std::time::Duration` to `time::Duration`")
}
}
impl Add<Duration> for StdDuration {
type Output = Duration;
fn add(self, rhs: Duration) -> Self::Output {
rhs + self
}
}
impl_add_assign!(Duration: Self, StdDuration);
impl AddAssign<Duration> for StdDuration {
+ /// # Panics
+ ///
+ /// This may panic if the resulting addition cannot be represented.
fn add_assign(&mut self, rhs: Duration) {
*self = (*self + rhs).try_into().expect(
"Cannot represent a resulting duration in std. Try `let x = x + rhs;`, which will \
change the type.",
);
}
}
impl Neg for Duration {
type Output = Self;
fn neg(self) -> Self::Output {
- Self::new_unchecked(-self.seconds, -self.nanoseconds)
+ self.checked_neg().expect("overflow when negating duration")
}
}
impl Sub for Duration {
type Output = Self;
+ /// # Panics
+ ///
+ /// This may panic if an overflow occurs.
fn sub(self, rhs: Self) -> Self::Output {
self.checked_sub(rhs)
.expect("overflow when subtracting durations")
}
}
impl Sub<StdDuration> for Duration {
type Output = Self;
+ /// # Panics
+ ///
+ /// This may panic if an overflow occurs.
fn sub(self, rhs: StdDuration) -> Self::Output {
self - Self::try_from(rhs)
.expect("overflow converting `std::time::Duration` to `time::Duration`")
}
}
impl Sub<Duration> for StdDuration {
type Output = Duration;
+ /// # Panics
+ ///
+ /// This may panic if an overflow occurs.
fn sub(self, rhs: Duration) -> Self::Output {
Duration::try_from(self)
.expect("overflow converting `std::time::Duration` to `time::Duration`")
- rhs
}
}
impl_sub_assign!(Duration: Self, StdDuration);
impl SubAssign<Duration> for StdDuration {
+ /// # Panics
+ ///
+ /// This may panic if the resulting subtraction can not be represented.
fn sub_assign(&mut self, rhs: Duration) {
*self = (*self - rhs).try_into().expect(
"Cannot represent a resulting duration in std. Try `let x = x - rhs;`, which will \
change the type.",
);
}
}
/// Implement `Mul` (reflexively) and `Div` for `Duration` for various types.
macro_rules! duration_mul_div_int {
($($type:ty),+) => {$(
impl Mul<$type> for Duration {
type Output = Self;
fn mul(self, rhs: $type) -> Self::Output {
Self::nanoseconds_i128(
self.whole_nanoseconds()
- .checked_mul(rhs as _)
+ .checked_mul(rhs.cast_signed().extend::<i128>())
.expect("overflow when multiplying duration")
)
}
}
impl Mul<Duration> for $type {
type Output = Duration;
fn mul(self, rhs: Duration) -> Self::Output {
rhs * self
}
}
impl Div<$type> for Duration {
type Output = Self;
fn div(self, rhs: $type) -> Self::Output {
- Self::nanoseconds_i128(self.whole_nanoseconds() / rhs as i128)
+ Self::nanoseconds_i128(
+ self.whole_nanoseconds() / rhs.cast_signed().extend::<i128>()
+ )
}
}
)+};
}
duration_mul_div_int![i8, i16, i32, u8, u16, u32];
impl Mul<f32> for Duration {
type Output = Self;
fn mul(self, rhs: f32) -> Self::Output {
@@ -1379,28 +1533,32 @@ impl PartialEq<StdDuration> for Duration {
}
impl PartialEq<Duration> for StdDuration {
fn eq(&self, rhs: &Duration) -> bool {
rhs == self
}
}
impl PartialOrd<StdDuration> for Duration {
fn partial_cmp(&self, rhs: &StdDuration) -> Option<Ordering> {
- if rhs.as_secs() > i64::MAX as _ {
+ if rhs.as_secs() > i64::MAX.cast_unsigned() {
return Some(Ordering::Less);
}
Some(
self.seconds
- .cmp(&(rhs.as_secs() as _))
- .then_with(|| self.nanoseconds.cmp(&(rhs.subsec_nanos() as _))),
+ .cmp(&rhs.as_secs().cast_signed())
+ .then_with(|| {
+ self.nanoseconds
+ .get()
+ .cmp(&rhs.subsec_nanos().cast_signed())
+ }),
)
}
}
impl PartialOrd<Duration> for StdDuration {
fn partial_cmp(&self, rhs: &Duration) -> Option<Ordering> {
rhs.partial_cmp(self).map(Ordering::reverse)
}
}
diff --git a/third_party/rust/time/src/error/mod.rs b/third_party/rust/time/src/error/mod.rs
index 346b89f748..d2730c462d 100644
--- a/third_party/rust/time/src/error/mod.rs
+++ b/third_party/rust/time/src/error/mod.rs
@@ -29,61 +29,78 @@ pub use indeterminate_offset::IndeterminateOffset;
#[cfg(all(any(feature = "formatting", feature = "parsing"), feature = "alloc"))]
pub use invalid_format_description::InvalidFormatDescription;
pub use invalid_variant::InvalidVariant;
#[cfg(feature = "parsing")]
pub use parse::Parse;
#[cfg(feature = "parsing")]
pub use parse_from_description::ParseFromDescription;
#[cfg(feature = "parsing")]
pub use try_from_parsed::TryFromParsed;
+#[cfg(feature = "parsing")]
+use crate::internal_macros::bug;
+
/// A unified error type for anything returned by a method in the time crate.
///
/// This can be used when you either don't know or don't care about the exact error returned.
/// `Result<_, time::Error>` (or its alias `time::Result<_>`) will work in these situations.
#[allow(missing_copy_implementations, variant_size_differences)]
-#[allow(clippy::missing_docs_in_private_items)] // variants only
#[non_exhaustive]
#[derive(Debug)]
pub enum Error {
+ #[allow(missing_docs)]
ConversionRange(ConversionRange),
+ #[allow(missing_docs)]
ComponentRange(ComponentRange),
#[cfg(feature = "local-offset")]
+ #[allow(missing_docs)]
IndeterminateOffset(IndeterminateOffset),
#[cfg(feature = "formatting")]
+ #[allow(missing_docs)]
Format(Format),
#[cfg(feature = "parsing")]
+ #[allow(missing_docs)]
ParseFromDescription(ParseFromDescription),
#[cfg(feature = "parsing")]
+ #[allow(missing_docs)]
#[non_exhaustive]
+ #[deprecated(
+ since = "0.3.28",
+ note = "no longer output. moved to the `ParseFromDescription` variant"
+ )]
UnexpectedTrailingCharacters,
#[cfg(feature = "parsing")]
+ #[allow(missing_docs)]
TryFromParsed(TryFromParsed),
#[cfg(all(any(feature = "formatting", feature = "parsing"), feature = "alloc"))]
+ #[allow(missing_docs)]
InvalidFormatDescription(InvalidFormatDescription),
+ #[allow(missing_docs)]
DifferentVariant(DifferentVariant),
+ #[allow(missing_docs)]
InvalidVariant(InvalidVariant),
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::ConversionRange(e) => e.fmt(f),
Self::ComponentRange(e) => e.fmt(f),
#[cfg(feature = "local-offset")]
Self::IndeterminateOffset(e) => e.fmt(f),
#[cfg(feature = "formatting")]
Self::Format(e) => e.fmt(f),
#[cfg(feature = "parsing")]
Self::ParseFromDescription(e) => e.fmt(f),
#[cfg(feature = "parsing")]
- Self::UnexpectedTrailingCharacters => f.write_str("unexpected trailing characters"),
+ #[allow(deprecated)]
+ Self::UnexpectedTrailingCharacters => bug!("variant should not be used"),
#[cfg(feature = "parsing")]
Self::TryFromParsed(e) => e.fmt(f),
#[cfg(all(any(feature = "formatting", feature = "parsing"), feature = "alloc"))]
Self::InvalidFormatDescription(e) => e.fmt(f),
Self::DifferentVariant(e) => e.fmt(f),
Self::InvalidVariant(e) => e.fmt(f),
}
}
}
@@ -93,20 +110,21 @@ impl std::error::Error for Error {
match self {
Self::ConversionRange(err) => Some(err),
Self::ComponentRange(err) => Some(err),
#[cfg(feature = "local-offset")]
Self::IndeterminateOffset(err) => Some(err),
#[cfg(feature = "formatting")]
Self::Format(err) => Some(err),
#[cfg(feature = "parsing")]
Self::ParseFromDescription(err) => Some(err),
#[cfg(feature = "parsing")]
- Self::UnexpectedTrailingCharacters => None,
+ #[allow(deprecated)]
+ Self::UnexpectedTrailingCharacters => bug!("variant should not be used"),
#[cfg(feature = "parsing")]
Self::TryFromParsed(err) => Some(err),
#[cfg(all(any(feature = "formatting", feature = "parsing"), feature = "alloc"))]
Self::InvalidFormatDescription(err) => Some(err),
Self::DifferentVariant(err) => Some(err),
Self::InvalidVariant(err) => Some(err),
}
}
}
diff --git a/third_party/rust/time/src/error/parse.rs b/third_party/rust/time/src/error/parse.rs
index b90ac74e73..09935b1c9b 100644
--- a/third_party/rust/time/src/error/parse.rs
+++ b/third_party/rust/time/src/error/parse.rs
@@ -1,47 +1,54 @@
//! Error that occurred at some stage of parsing
use core::fmt;
use crate::error::{self, ParseFromDescription, TryFromParsed};
+use crate::internal_macros::bug;
/// An error that occurred at some stage of parsing.
#[allow(variant_size_differences)]
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Parse {
- #[allow(clippy::missing_docs_in_private_items)]
+ #[allow(missing_docs)]
TryFromParsed(TryFromParsed),
- #[allow(clippy::missing_docs_in_private_items)]
+ #[allow(missing_docs)]
ParseFromDescription(ParseFromDescription),
/// The input should have ended, but there were characters remaining.
#[non_exhaustive]
+ #[deprecated(
+ since = "0.3.28",
+ note = "no longer output. moved to the `ParseFromDescription` variant"
+ )]
UnexpectedTrailingCharacters,
}
impl fmt::Display for Parse {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::TryFromParsed(err) => err.fmt(f),
Self::ParseFromDescription(err) => err.fmt(f),
- Self::UnexpectedTrailingCharacters => f.write_str("unexpected trailing characters"),
+ #[allow(deprecated)]
+ Self::UnexpectedTrailingCharacters => bug!("variant should not be used"),
}
}
}
#[cfg(feature = "std")]
impl std::error::Error for Parse {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Self::TryFromParsed(err) => Some(err),
Self::ParseFromDescription(err) => Some(err),
- Self::UnexpectedTrailingCharacters => None,
+ #[allow(deprecated)]
+ Self::UnexpectedTrailingCharacters => bug!("variant should not be used"),
}
}
}
impl From<TryFromParsed> for Parse {
fn from(err: TryFromParsed) -> Self {
Self::TryFromParsed(err)
}
}
@@ -71,27 +78,29 @@ impl TryFrom<Parse> for ParseFromDescription {
_ => Err(error::DifferentVariant),
}
}
}
impl From<Parse> for crate::Error {
fn from(err: Parse) -> Self {
match err {
Parse::TryFromParsed(err) => Self::TryFromParsed(err),
Parse::ParseFromDescription(err) => Self::ParseFromDescription(err),
- Parse::UnexpectedTrailingCharacters => Self::UnexpectedTrailingCharacters,
+ #[allow(deprecated)]
+ Parse::UnexpectedTrailingCharacters => bug!("variant should not be used"),
}
}
}
impl TryFrom<crate::Error> for Parse {
type Error = error::DifferentVariant;
fn try_from(err: crate::Error) -> Result<Self, Self::Error> {
match err {
crate::Error::ParseFromDescription(err) => Ok(Self::ParseFromDescription(err)),
- crate::Error::UnexpectedTrailingCharacters => Ok(Self::UnexpectedTrailingCharacters),
+ #[allow(deprecated)]
+ crate::Error::UnexpectedTrailingCharacters => bug!("variant should not be used"),
crate::Error::TryFromParsed(err) => Ok(Self::TryFromParsed(err)),
_ => Err(error::DifferentVariant),
}
}
}
diff --git a/third_party/rust/time/src/error/parse_from_description.rs b/third_party/rust/time/src/error/parse_from_description.rs
index 772f10d173..40124dce37 100644
--- a/third_party/rust/time/src/error/parse_from_description.rs
+++ b/third_party/rust/time/src/error/parse_from_description.rs
@@ -6,29 +6,35 @@ use crate::error;
/// An error that occurred while parsing the input into a [`Parsed`](crate::parsing::Parsed) struct.
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ParseFromDescription {
/// A string literal was not what was expected.
#[non_exhaustive]
InvalidLiteral,
/// A dynamic component was not valid.
InvalidComponent(&'static str),
+ /// The input was expected to have ended, but there are characters that remain.
+ #[non_exhaustive]
+ UnexpectedTrailingCharacters,
}
impl fmt::Display for ParseFromDescription {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::InvalidLiteral => f.write_str("a character literal was not valid"),
Self::InvalidComponent(name) => {
write!(f, "the '{name}' component could not be parsed")
}
+ Self::UnexpectedTrailingCharacters => {
+ f.write_str("unexpected trailing characters; the end of input was expected")
+ }
}
}
}
#[cfg(feature = "std")]
impl std::error::Error for ParseFromDescription {}
impl From<ParseFromDescription> for crate::Error {
fn from(original: ParseFromDescription) -> Self {
Self::ParseFromDescription(original)
diff --git a/third_party/rust/time/src/ext.rs b/third_party/rust/time/src/ext.rs
deleted file mode 100644
index 8b7759d8e9..0000000000
--- a/third_party/rust/time/src/ext.rs
+++ /dev/null
@@ -1,280 +0,0 @@
-//! Extension traits.
-
-use core::time::Duration as StdDuration;
-
-use crate::convert::*;
-use crate::Duration;
-
-/// Sealed trait to prevent downstream implementations.
-mod sealed {
- /// A trait that cannot be implemented by downstream users.
- pub trait Sealed {}
- impl Sealed for i64 {}
- impl Sealed for u64 {}
- impl Sealed for f64 {}
-}
-
-// region: NumericalDuration
-/// Create [`Duration`]s from numeric literals.
-///
-/// # Examples
-///
-/// Basic construction of [`Duration`]s.
-///
-/// ```rust
-/// # use time::{Duration, ext::NumericalDuration};
-/// assert_eq!(5.nanoseconds(), Duration::nanoseconds(5));
-/// assert_eq!(5.microseconds(), Duration::microseconds(5));
-/// assert_eq!(5.milliseconds(), Duration::milliseconds(5));
-/// assert_eq!(5.seconds(), Duration::seconds(5));
-/// assert_eq!(5.minutes(), Duration::minutes(5));
-/// assert_eq!(5.hours(), Duration::hours(5));
-/// assert_eq!(5.days(), Duration::days(5));
-/// assert_eq!(5.weeks(), Duration::weeks(5));
-/// ```
-///
-/// Signed integers work as well!
-///
-/// ```rust
-/// # use time::{Duration, ext::NumericalDuration};
-/// assert_eq!((-5).nanoseconds(), Duration::nanoseconds(-5));
-/// assert_eq!((-5).microseconds(), Duration::microseconds(-5));
-/// assert_eq!((-5).milliseconds(), Duration::milliseconds(-5));
-/// assert_eq!((-5).seconds(), Duration::seconds(-5));
-/// assert_eq!((-5).minutes(), Duration::minutes(-5));
-/// assert_eq!((-5).hours(), Duration::hours(-5));
-/// assert_eq!((-5).days(), Duration::days(-5));
-/// assert_eq!((-5).weeks(), Duration::weeks(-5));
-/// ```
-///
-/// Just like any other [`Duration`], they can be added, subtracted, etc.
-///
-/// ```rust
-/// # use time::ext::NumericalDuration;
-/// assert_eq!(2.seconds() + 500.milliseconds(), 2_500.milliseconds());
-/// assert_eq!(2.seconds() - 500.milliseconds(), 1_500.milliseconds());
-/// ```
-///
-/// When called on floating point values, any remainder of the floating point value will be
-/// truncated. Keep in mind that floating point numbers are inherently imprecise and have limited
-/// capacity.
-pub trait NumericalDuration: sealed::Sealed {
- /// Create a [`Duration`] from the number of nanoseconds.
- fn nanoseconds(self) -> Duration;
- /// Create a [`Duration`] from the number of microseconds.
- fn microseconds(self) -> Duration;
- /// Create a [`Duration`] from the number of milliseconds.
- fn milliseconds(self) -> Duration;
- /// Create a [`Duration`] from the number of seconds.
- fn seconds(self) -> Duration;
- /// Create a [`Duration`] from the number of minutes.
- fn minutes(self) -> Duration;
- /// Create a [`Duration`] from the number of hours.
- fn hours(self) -> Duration;
- /// Create a [`Duration`] from the number of days.
- fn days(self) -> Duration;
- /// Create a [`Duration`] from the number of weeks.
- fn weeks(self) -> Duration;
-}
-
-impl NumericalDuration for i64 {
- fn nanoseconds(self) -> Duration {
- Duration::nanoseconds(self)
- }
-
- fn microseconds(self) -> Duration {
- Duration::microseconds(self)
- }
-
- fn milliseconds(self) -> Duration {
- Duration::milliseconds(self)
- }
-
- fn seconds(self) -> Duration {
- Duration::seconds(self)
- }
-
- fn minutes(self) -> Duration {
- Duration::minutes(self)
- }
-
- fn hours(self) -> Duration {
- Duration::hours(self)
- }
-
- fn days(self) -> Duration {
- Duration::days(self)
- }
-
- fn weeks(self) -> Duration {
- Duration::weeks(self)
- }
-}
-
-impl NumericalDuration for f64 {
- fn nanoseconds(self) -> Duration {
- Duration::nanoseconds(self as _)
- }
-
- fn microseconds(self) -> Duration {
- Duration::nanoseconds((self * Nanosecond.per(Microsecond) as Self) as _)
- }
-
- fn milliseconds(self) -> Duration {
- Duration::nanoseconds((self * Nanosecond.per(Millisecond) as Self) as _)
- }
-
- fn seconds(self) -> Duration {
- Duration::nanoseconds((self * Nanosecond.per(Second) as Self) as _)
- }
-
- fn minutes(self) -> Duration {
- Duration::nanoseconds((self * Nanosecond.per(Minute) as Self) as _)
- }
-
- fn hours(self) -> Duration {
- Duration::nanoseconds((self * Nanosecond.per(Hour) as Self) as _)
- }
-
- fn days(self) -> Duration {
- Duration::nanoseconds((self * Nanosecond.per(Day) as Self) as _)
- }
-
- fn weeks(self) -> Duration {
- Duration::nanoseconds((self * Nanosecond.per(Week) as Self) as _)
- }
-}
-// endregion NumericalDuration
-
-// region: NumericalStdDuration
-/// Create [`std::time::Duration`]s from numeric literals.
-///
-/// # Examples
-///
-/// Basic construction of [`std::time::Duration`]s.
-///
-/// ```rust
-/// # use time::ext::NumericalStdDuration;
-/// # use core::time::Duration;
-/// assert_eq!(5.std_nanoseconds(), Duration::from_nanos(5));
-/// assert_eq!(5.std_microseconds(), Duration::from_micros(5));
-/// assert_eq!(5.std_milliseconds(), Duration::from_millis(5));
-/// assert_eq!(5.std_seconds(), Duration::from_secs(5));
-/// assert_eq!(5.std_minutes(), Duration::from_secs(5 * 60));
-/// assert_eq!(5.std_hours(), Duration::from_secs(5 * 3_600));
-/// assert_eq!(5.std_days(), Duration::from_secs(5 * 86_400));
-/// assert_eq!(5.std_weeks(), Duration::from_secs(5 * 604_800));
-/// ```
-///
-/// Just like any other [`std::time::Duration`], they can be added, subtracted, etc.
-///
-/// ```rust
-/// # use time::ext::NumericalStdDuration;
-/// assert_eq!(
-/// 2.std_seconds() + 500.std_milliseconds(),
-/// 2_500.std_milliseconds()
-/// );
-/// assert_eq!(
-/// 2.std_seconds() - 500.std_milliseconds(),
-/// 1_500.std_milliseconds()
-/// );
-/// ```
-///
-/// When called on floating point values, any remainder of the floating point value will be
-/// truncated. Keep in mind that floating point numbers are inherently imprecise and have limited
-/// capacity.
-pub trait NumericalStdDuration: sealed::Sealed {
- /// Create a [`std::time::Duration`] from the number of nanoseconds.
- fn std_nanoseconds(self) -> StdDuration;
- /// Create a [`std::time::Duration`] from the number of microseconds.
- fn std_microseconds(self) -> StdDuration;
- /// Create a [`std::time::Duration`] from the number of milliseconds.
- fn std_milliseconds(self) -> StdDuration;
- /// Create a [`std::time::Duration`] from the number of seconds.
- fn std_seconds(self) -> StdDuration;
- /// Create a [`std::time::Duration`] from the number of minutes.
- fn std_minutes(self) -> StdDuration;
- /// Create a [`std::time::Duration`] from the number of hours.
- fn std_hours(self) -> StdDuration;
- /// Create a [`std::time::Duration`] from the number of days.
- fn std_days(self) -> StdDuration;
- /// Create a [`std::time::Duration`] from the number of weeks.
- fn std_weeks(self) -> StdDuration;
-}
-
-impl NumericalStdDuration for u64 {
- fn std_nanoseconds(self) -> StdDuration {
- StdDuration::from_nanos(self)
- }
-
- fn std_microseconds(self) -> StdDuration {
- StdDuration::from_micros(self)
- }
-
- fn std_milliseconds(self) -> StdDuration {
- StdDuration::from_millis(self)
- }
-
- fn std_seconds(self) -> StdDuration {
- StdDuration::from_secs(self)
- }
-
- fn std_minutes(self) -> StdDuration {
- StdDuration::from_secs(self * Second.per(Minute) as Self)
- }
-
- fn std_hours(self) -> StdDuration {
- StdDuration::from_secs(self * Second.per(Hour) as Self)
- }
-
- fn std_days(self) -> StdDuration {
- StdDuration::from_secs(self * Second.per(Day) as Self)
- }
-
- fn std_weeks(self) -> StdDuration {
- StdDuration::from_secs(self * Second.per(Week) as Self)
- }
-}
-
-impl NumericalStdDuration for f64 {
- fn std_nanoseconds(self) -> StdDuration {
- assert!(self >= 0.);
- StdDuration::from_nanos(self as _)
- }
-
- fn std_microseconds(self) -> StdDuration {
- assert!(self >= 0.);
- StdDuration::from_nanos((self * Nanosecond.per(Microsecond) as Self) as _)
- }
-
- fn std_milliseconds(self) -> StdDuration {
- assert!(self >= 0.);
- StdDuration::from_nanos((self * Nanosecond.per(Millisecond) as Self) as _)
- }
-
- fn std_seconds(self) -> StdDuration {
- assert!(self >= 0.);
- StdDuration::from_nanos((self * Nanosecond.per(Second) as Self) as _)
- }
-
- fn std_minutes(self) -> StdDuration {
- assert!(self >= 0.);
- StdDuration::from_nanos((self * Nanosecond.per(Minute) as Self) as _)
- }
-
- fn std_hours(self) -> StdDuration {
- assert!(self >= 0.);
- StdDuration::from_nanos((self * Nanosecond.per(Hour) as Self) as _)
- }
-
- fn std_days(self) -> StdDuration {
- assert!(self >= 0.);
- StdDuration::from_nanos((self * Nanosecond.per(Day) as Self) as _)
- }
-
- fn std_weeks(self) -> StdDuration {
- assert!(self >= 0.);
- StdDuration::from_nanos((self * Nanosecond.per(Week) as Self) as _)
- }
-}
-// endregion NumericalStdDuration
diff --git a/third_party/rust/time/src/ext/digit_count.rs b/third_party/rust/time/src/ext/digit_count.rs
new file mode 100644
index 0000000000..fb42ce9e04
--- /dev/null
+++ b/third_party/rust/time/src/ext/digit_count.rs
@@ -0,0 +1,26 @@
+use num_conv::prelude::*;
+
+/// A trait that indicates the formatted width of the value can be determined.
+///
+/// Note that this should not be implemented for any signed integers. This forces the caller to
+/// write the sign if desired.
+pub(crate) trait DigitCount {
+ /// The number of digits in the stringified value.
+ fn num_digits(self) -> u8;
+}
+
+/// A macro to generate implementations of `DigitCount` for unsigned integers.
+macro_rules! impl_digit_count {
+ ($($t:ty),* $(,)?) => {
+ $(impl DigitCount for $t {
+ fn num_digits(self) -> u8 {
+ match self.checked_ilog10() {
+ Some(n) => n.truncate::<u8>() + 1,
+ None => 1,
+ }
+ }
+ })*
+ };
+}
+
+impl_digit_count!(u8, u16, u32);
diff --git a/third_party/rust/time/src/ext/instant.rs b/third_party/rust/time/src/ext/instant.rs
new file mode 100644
index 0000000000..dbc6a948f0
--- /dev/null
+++ b/third_party/rust/time/src/ext/instant.rs
@@ -0,0 +1,100 @@
+use std::time::Instant as StdInstant;
+
+use crate::Duration;
+
+/// Sealed trait to prevent downstream implementations.
+mod sealed {
+ /// A trait that cannot be implemented by downstream users.
+ pub trait Sealed: Sized {}
+ impl Sealed for std::time::Instant {}
+}
+
+/// An extension trait for [`std::time::Instant`] that adds methods for
+/// [`time::Duration`](Duration)s.
+pub trait InstantExt: sealed::Sealed {
+ /// # Panics
+ ///
+ /// This function may panic if the resulting point in time cannot be represented by the
+ /// underlying data structure. See [`InstantExt::checked_add_signed`] for a non-panicking
+ /// version.
+ fn add_signed(self, duration: Duration) -> Self {
+ self.checked_add_signed(duration)
+ .expect("overflow when adding duration to instant")
+ }
+
+ /// # Panics
+ ///
+ /// This function may panic if the resulting point in time cannot be represented by the
+ /// underlying data structure. See [`InstantExt::checked_sub_signed`] for a non-panicking
+ /// version.
+ fn sub_signed(self, duration: Duration) -> Self {
+ self.checked_sub_signed(duration)
+ .expect("overflow when subtracting duration from instant")
+ }
+
+ /// Returns `Some(t)` where `t` is the time `self.checked_add_signed(duration)` if `t` can be
+ /// represented as `Instant` (which means it's inside the bounds of the underlying data
+ /// structure), `None` otherwise.
+ fn checked_add_signed(&self, duration: Duration) -> Option<Self>;
+
+ /// Returns `Some(t)` where `t` is the time `self.checked_sub_signed(duration)` if `t` can be
+ /// represented as `Instant` (which means it's inside the bounds of the underlying data
+ /// structure), `None` otherwise.
+ fn checked_sub_signed(&self, duration: Duration) -> Option<Self>;
+
+ /// Returns the amount of time elapsed from another instant to this one. This will be negative
+ /// if `earlier` is later than `self`.
+ ///
+ /// # Example
+ ///
+ /// ```rust
+ /// # use std::thread::sleep;
+ /// # use std::time::{Duration, Instant};
+ /// # use time::ext::InstantExt;
+ /// let now = Instant::now();
+ /// sleep(Duration::new(1, 0));
+ /// let new_now = Instant::now();
+ /// println!("{:?}", new_now.signed_duration_since(now)); // positive
+ /// println!("{:?}", now.signed_duration_since(new_now)); // negative
+ /// ```
+ fn signed_duration_since(&self, earlier: Self) -> Duration;
+}
+
+impl InstantExt for StdInstant {
+ fn checked_add_signed(&self, duration: Duration) -> Option<Self> {
+ if duration.is_positive() {
+ self.checked_add(duration.unsigned_abs())
+ } else if duration.is_negative() {
+ #[allow(clippy::unchecked_duration_subtraction)]
+ self.checked_sub(duration.unsigned_abs())
+ } else {
+ debug_assert!(duration.is_zero());
+ Some(*self)
+ }
+ }
+
+ fn checked_sub_signed(&self, duration: Duration) -> Option<Self> {
+ if duration.is_positive() {
+ #[allow(clippy::unchecked_duration_subtraction)]
+ self.checked_sub(duration.unsigned_abs())
+ } else if duration.is_negative() {
+ self.checked_add(duration.unsigned_abs())
+ } else {
+ debug_assert!(duration.is_zero());
+ Some(*self)
+ }
+ }
+
+ fn signed_duration_since(&self, earlier: Self) -> Duration {
+ if *self > earlier {
+ self.saturating_duration_since(earlier)
+ .try_into()
+ .unwrap_or(Duration::MAX)
+ } else {
+ earlier
+ .saturating_duration_since(*self)
+ .try_into()
+ .map_or(Duration::MIN, |d: Duration| -d)
+ }
+ }
+}
diff --git a/third_party/rust/time/src/ext/mod.rs b/third_party/rust/time/src/ext/mod.rs
new file mode 100644
index 0000000000..7cc2d0d920
--- /dev/null
+++ b/third_party/rust/time/src/ext/mod.rs
@@ -0,0 +1,13 @@
+//! Extension traits.
+
+mod digit_count;
+#[cfg(feature = "std")]
+mod instant;
+mod numerical_duration;
+mod numerical_std_duration;
+
+pub(crate) use self::digit_count::DigitCount;
+#[cfg(feature = "std")]
+pub use self::instant::InstantExt;
+pub use self::numerical_duration::NumericalDuration;
+pub use self::numerical_std_duration::NumericalStdDuration;
diff --git a/third_party/rust/time/src/ext/numerical_duration.rs b/third_party/rust/time/src/ext/numerical_duration.rs
new file mode 100644
index 0000000000..fb9209d235
--- /dev/null
+++ b/third_party/rust/time/src/ext/numerical_duration.rs
@@ -0,0 +1,140 @@
+use crate::convert::*;
+use crate::Duration;
+
+/// Sealed trait to prevent downstream implementations.
+mod sealed {
+ /// A trait that cannot be implemented by downstream users.
+ pub trait Sealed {}
+ impl Sealed for i64 {}
+ impl Sealed for f64 {}
+}
+
+/// Create [`Duration`]s from numeric literals.
+///
+/// # Examples
+///
+/// Basic construction of [`Duration`]s.
+///
+/// ```rust
+/// # use time::{Duration, ext::NumericalDuration};
+/// assert_eq!(5.nanoseconds(), Duration::nanoseconds(5));
+/// assert_eq!(5.microseconds(), Duration::microseconds(5));
+/// assert_eq!(5.milliseconds(), Duration::milliseconds(5));
+/// assert_eq!(5.seconds(), Duration::seconds(5));
+/// assert_eq!(5.minutes(), Duration::minutes(5));
+/// assert_eq!(5.hours(), Duration::hours(5));
+/// assert_eq!(5.days(), Duration::days(5));
+/// assert_eq!(5.weeks(), Duration::weeks(5));
+/// ```
+///
+/// Signed integers work as well!
+///
+/// ```rust
+/// # use time::{Duration, ext::NumericalDuration};
+/// assert_eq!((-5).nanoseconds(), Duration::nanoseconds(-5));
+/// assert_eq!((-5).microseconds(), Duration::microseconds(-5));
+/// assert_eq!((-5).milliseconds(), Duration::milliseconds(-5));
+/// assert_eq!((-5).seconds(), Duration::seconds(-5));
+/// assert_eq!((-5).minutes(), Duration::minutes(-5));
+/// assert_eq!((-5).hours(), Duration::hours(-5));
+/// assert_eq!((-5).days(), Duration::days(-5));
+/// assert_eq!((-5).weeks(), Duration::weeks(-5));
+/// ```
+///
+/// Just like any other [`Duration`], they can be added, subtracted, etc.
+///
+/// ```rust
+/// # use time::ext::NumericalDuration;
+/// assert_eq!(2.seconds() + 500.milliseconds(), 2_500.milliseconds());
+/// assert_eq!(2.seconds() - 500.milliseconds(), 1_500.milliseconds());
+/// ```
+///
+/// When called on floating point values, any remainder of the floating point value will be
+/// truncated. Keep in mind that floating point numbers are inherently imprecise and have
+/// limited capacity.
+pub trait NumericalDuration: sealed::Sealed {
+ /// Create a [`Duration`] from the number of nanoseconds.
+ fn nanoseconds(self) -> Duration;
+ /// Create a [`Duration`] from the number of microseconds.
+ fn microseconds(self) -> Duration;
+ /// Create a [`Duration`] from the number of milliseconds.
+ fn milliseconds(self) -> Duration;
+ /// Create a [`Duration`] from the number of seconds.
+ fn seconds(self) -> Duration;
+ /// Create a [`Duration`] from the number of minutes.
+ fn minutes(self) -> Duration;
+ /// Create a [`Duration`] from the number of hours.
+ fn hours(self) -> Duration;
+ /// Create a [`Duration`] from the number of days.
+ fn days(self) -> Duration;
+ /// Create a [`Duration`] from the number of weeks.
+ fn weeks(self) -> Duration;
+}
+
+impl NumericalDuration for i64 {
+ fn nanoseconds(self) -> Duration {
+ Duration::nanoseconds(self)
+ }
+
+ fn microseconds(self) -> Duration {
+ Duration::microseconds(self)
+ }
+
+ fn milliseconds(self) -> Duration {
+ Duration::milliseconds(self)
+ }
+
+ fn seconds(self) -> Duration {
+ Duration::seconds(self)
+ }
+
+ fn minutes(self) -> Duration {
+ Duration::minutes(self)
+ }
+
+ fn hours(self) -> Duration {
+ Duration::hours(self)
+ }
+
+ fn days(self) -> Duration {
+ Duration::days(self)
+ }
+
+ fn weeks(self) -> Duration {
+ Duration::weeks(self)
+ }
+}
+
+impl NumericalDuration for f64 {
+ fn nanoseconds(self) -> Duration {
+ Duration::nanoseconds(self as _)
+ }
+
+ fn microseconds(self) -> Duration {
+ Duration::nanoseconds((self * Nanosecond::per(Microsecond) as Self) as _)
+ }
+
+ fn milliseconds(self) -> Duration {
+ Duration::nanoseconds((self * Nanosecond::per(Millisecond) as Self) as _)
+ }
+
+ fn seconds(self) -> Duration {
+ Duration::nanoseconds((self * Nanosecond::per(Second) as Self) as _)
+ }
+
+ fn minutes(self) -> Duration {
+ Duration::nanoseconds((self * Nanosecond::per(Minute) as Self) as _)
+ }
+
+ fn hours(self) -> Duration {
+ Duration::nanoseconds((self * Nanosecond::per(Hour) as Self) as _)
+ }
+
+ fn days(self) -> Duration {
+ Duration::nanoseconds((self * Nanosecond::per(Day) as Self) as _)
+ }
+
+ fn weeks(self) -> Duration {
+ Duration::nanoseconds((self * Nanosecond::per(Week) as Self) as _)
+ }
+}
diff --git a/third_party/rust/time/src/ext/numerical_std_duration.rs b/third_party/rust/time/src/ext/numerical_std_duration.rs
new file mode 100644
index 0000000000..062b33f76f
--- /dev/null
+++ b/third_party/rust/time/src/ext/numerical_std_duration.rs
@@ -0,0 +1,192 @@
+use core::time::Duration as StdDuration;
+
+use num_conv::prelude::*;
+
+use crate::convert::*;
+
+/// Sealed trait to prevent downstream implementations.
+mod sealed {
+ /// A trait that cannot be implemented by downstream users.
+ pub trait Sealed {}
+ impl Sealed for u64 {}
+ impl Sealed for f64 {}
+}
+
+/// Create [`std::time::Duration`]s from numeric literals.
+///
+/// # Examples
+///
+/// Basic construction of [`std::time::Duration`]s.
+///
+/// ```rust
+/// # use time::ext::NumericalStdDuration;
+/// # use core::time::Duration;
+/// assert_eq!(5.std_nanoseconds(), Duration::from_nanos(5));
+/// assert_eq!(5.std_microseconds(), Duration::from_micros(5));
+/// assert_eq!(5.std_milliseconds(), Duration::from_millis(5));
+/// assert_eq!(5.std_seconds(), Duration::from_secs(5));
+/// assert_eq!(5.std_minutes(), Duration::from_secs(5 * 60));
+/// assert_eq!(5.std_hours(), Duration::from_secs(5 * 3_600));
+/// assert_eq!(5.std_days(), Duration::from_secs(5 * 86_400));
+/// assert_eq!(5.std_weeks(), Duration::from_secs(5 * 604_800));
+/// ```
+///
+/// Just like any other [`std::time::Duration`], they can be added, subtracted, etc.
+///
+/// ```rust
+/// # use time::ext::NumericalStdDuration;
+/// assert_eq!(
+/// 2.std_seconds() + 500.std_milliseconds(),
+/// 2_500.std_milliseconds()
+/// );
+/// assert_eq!(
+/// 2.std_seconds() - 500.std_milliseconds(),
+/// 1_500.std_milliseconds()
+/// );
+/// ```
+///
+/// When called on floating point values, any remainder of the floating point value will be
+/// truncated. Keep in mind that floating point numbers are inherently imprecise and have
+/// limited capacity.
+pub trait NumericalStdDuration: sealed::Sealed {
+ /// Create a [`std::time::Duration`] from the number of nanoseconds.
+ fn std_nanoseconds(self) -> StdDuration;
+ /// Create a [`std::time::Duration`] from the number of microseconds.
+ fn std_microseconds(self) -> StdDuration;
+ /// Create a [`std::time::Duration`] from the number of milliseconds.
+ fn std_milliseconds(self) -> StdDuration;
+ /// Create a [`std::time::Duration`] from the number of seconds.
+ fn std_seconds(self) -> StdDuration;
+ /// Create a [`std::time::Duration`] from the number of minutes.
+ fn std_minutes(self) -> StdDuration;
+ /// Create a [`std::time::Duration`] from the number of hours.
+ fn std_hours(self) -> StdDuration;
+ /// Create a [`std::time::Duration`] from the number of days.
+ fn std_days(self) -> StdDuration;
+ /// Create a [`std::time::Duration`] from the number of weeks.
+ fn std_weeks(self) -> StdDuration;
+}
+
+impl NumericalStdDuration for u64 {
+ fn std_nanoseconds(self) -> StdDuration {
+ StdDuration::from_nanos(self)
+ }
+
+ fn std_microseconds(self) -> StdDuration {
+ StdDuration::from_micros(self)
+ }
+
+ fn std_milliseconds(self) -> StdDuration {
+ StdDuration::from_millis(self)
+ }
+
+ fn std_seconds(self) -> StdDuration {
+ StdDuration::from_secs(self)
+ }
+
+ /// # Panics
+ ///
+ /// This may panic if an overflow occurs.
+ fn std_minutes(self) -> StdDuration {
+ StdDuration::from_secs(
+ self.checked_mul(Second::per(Minute).extend())
+ .expect("overflow constructing `time::Duration`"),
+ )
+ }
+
+ /// # Panics
+ ///
+ /// This may panic if an overflow occurs.
+ fn std_hours(self) -> StdDuration {
+ StdDuration::from_secs(
+ self.checked_mul(Second::per(Hour).extend())
+ .expect("overflow constructing `time::Duration`"),
+ )
+ }
+
+ /// # Panics
+ ///
+ /// This may panic if an overflow occurs.
+ fn std_days(self) -> StdDuration {
+ StdDuration::from_secs(
+ self.checked_mul(Second::per(Day).extend())
+ .expect("overflow constructing `time::Duration`"),
+ )
+ }
+
+ /// # Panics
+ ///
+ /// This may panic if an overflow occurs.
+ fn std_weeks(self) -> StdDuration {
+ StdDuration::from_secs(
+ self.checked_mul(Second::per(Week).extend())
+ .expect("overflow constructing `time::Duration`"),
+ )
+ }
+}
+
+impl NumericalStdDuration for f64 {
+ /// # Panics
+ ///
+ /// This will panic if self is negative.
+ fn std_nanoseconds(self) -> StdDuration {
+ assert!(self >= 0.);
+ StdDuration::from_nanos(self as _)
+ }
+
+ /// # Panics
+ ///
+ /// This will panic if self is negative.
+ fn std_microseconds(self) -> StdDuration {
+ assert!(self >= 0.);
+ StdDuration::from_nanos((self * Nanosecond::per(Microsecond) as Self) as _)
+ }
+
+ /// # Panics
+ ///
+ /// This will panic if self is negative.
+ fn std_milliseconds(self) -> StdDuration {
+ assert!(self >= 0.);
+ StdDuration::from_nanos((self * Nanosecond::per(Millisecond) as Self) as _)
+ }
+
+ /// # Panics
+ ///
+ /// This will panic if self is negative.
+ fn std_seconds(self) -> StdDuration {
+ assert!(self >= 0.);
+ StdDuration::from_nanos((self * Nanosecond::per(Second) as Self) as _)
+ }
+
+ /// # Panics
+ ///
+ /// This will panic if self is negative.
+ fn std_minutes(self) -> StdDuration {
+ assert!(self >= 0.);
+ StdDuration::from_nanos((self * Nanosecond::per(Minute) as Self) as _)
+ }
+
+ /// # Panics
+ ///
+ /// This will panic if self is negative.
+ fn std_hours(self) -> StdDuration {
+ assert!(self >= 0.);
+ StdDuration::from_nanos((self * Nanosecond::per(Hour) as Self) as _)
+ }
+
+ /// # Panics
+ ///
+ /// This will panic if self is negative.
+ fn std_days(self) -> StdDuration {
+ assert!(self >= 0.);
+ StdDuration::from_nanos((self * Nanosecond::per(Day) as Self) as _)
+ }
+
+ /// # Panics
+ ///
+ /// This will panic if self is negative.
+ fn std_weeks(self) -> StdDuration {
+ assert!(self >= 0.);
+ StdDuration::from_nanos((self * Nanosecond::per(Week) as Self) as _)
+ }
+}
diff --git a/third_party/rust/time/src/format_description/borrowed_format_item.rs b/third_party/rust/time/src/format_description/borrowed_format_item.rs
index 425b902878..4f3e6386c7 100644
--- a/third_party/rust/time/src/format_description/borrowed_format_item.rs
+++ b/third_party/rust/time/src/format_description/borrowed_format_item.rs
@@ -1,29 +1,42 @@
//! A format item with borrowed data.
#[cfg(feature = "alloc")]
use alloc::string::String;
#[cfg(feature = "alloc")]
use core::fmt;
+/// A complete description of how to format and parse a type.
+///
+/// This alias exists for backwards-compatibility. It is recommended to use `BorrowedFormatItem`
+/// for clarity, as it is more explicit that the data is borrowed rather than owned.
+#[cfg(doc)]
+#[deprecated(
+ since = "0.3.35",
+ note = "use `BorrowedFormatItem` instead for clarity"
+)]
+pub type FormatItem<'a> = BorrowedFormatItem<'a>;
+
+#[cfg(not(doc))]
+pub use self::BorrowedFormatItem as FormatItem;
use crate::error;
use crate::format_description::Component;
/// A complete description of how to format and parse a type.
#[non_exhaustive]
#[cfg_attr(not(feature = "alloc"), derive(Debug))]
#[derive(Clone, PartialEq, Eq)]
pub enum BorrowedFormatItem<'a> {
/// Bytes that are formatted as-is.
///
- /// **Note**: If you call the `format` method that returns a `String`, these bytes will be
- /// passed through `String::from_utf8_lossy`.
+ /// **Note**: These bytes **should** be UTF-8, but are not required to be. The value is passed
+ /// through `String::from_utf8_lossy` when necessary.
Literal(&'a [u8]),
/// A minimal representation of a single non-literal item.
Component(Component),
/// A series of literals or components that collectively form a partial or complete
/// description.
Compound(&'a [Self]),
/// A `FormatItem` that may or may not be present when parsing. If parsing fails, there
/// will be no effect on the resulting `struct`.
///
/// This variant has no effect on formatting, as the value is guaranteed to be present.
diff --git a/third_party/rust/time/src/format_description/component.rs b/third_party/rust/time/src/format_description/component.rs
index 6725590815..9119c0905c 100644
--- a/third_party/rust/time/src/format_description/component.rs
+++ b/third_party/rust/time/src/format_description/component.rs
@@ -31,11 +31,14 @@ pub enum Component {
/// Hour of the UTC offset.
OffsetHour(modifier::OffsetHour),
/// Minute within the hour of the UTC offset.
OffsetMinute(modifier::OffsetMinute),
/// Second within the minute of the UTC offset.
OffsetSecond(modifier::OffsetSecond),
/// A number of bytes to ignore when parsing. This has no effect on formatting.
Ignore(modifier::Ignore),
/// A Unix timestamp.
UnixTimestamp(modifier::UnixTimestamp),
+ /// The end of input. Parsing this component will fail if there is any input remaining. This
+ /// component neither affects formatting nor consumes any input when parsing.
+ End(modifier::End),
}
diff --git a/third_party/rust/time/src/format_description/mod.rs b/third_party/rust/time/src/format_description/mod.rs
index befae8b569..67882afe78 100644
--- a/third_party/rust/time/src/format_description/mod.rs
+++ b/third_party/rust/time/src/format_description/mod.rs
@@ -8,21 +8,23 @@
//! e.g. [`well_known::Rfc3339`].
mod borrowed_format_item;
mod component;
pub mod modifier;
#[cfg(feature = "alloc")]
mod owned_format_item;
#[cfg(feature = "alloc")]
mod parse;
-pub use borrowed_format_item::BorrowedFormatItem as FormatItem;
+pub use borrowed_format_item::BorrowedFormatItem;
+#[allow(deprecated)]
+pub use borrowed_format_item::FormatItem;
#[cfg(feature = "alloc")]
pub use owned_format_item::OwnedFormatItem;
pub use self::component::Component;
#[cfg(feature = "alloc")]
pub use self::parse::{parse, parse_borrowed, parse_owned};
/// Well-known formats, typically standards.
pub mod well_known {
pub mod iso8601;
diff --git a/third_party/rust/time/src/format_description/modifier.rs b/third_party/rust/time/src/format_description/modifier.rs
index cdac1ae974..3a57bb6f66 100644
--- a/third_party/rust/time/src/format_description/modifier.rs
+++ b/third_party/rust/time/src/format_description/modifier.rs
@@ -272,20 +272,27 @@ pub enum UnixTimestampPrecision {
/// A Unix timestamp.
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct UnixTimestamp {
/// The precision of the timestamp.
pub precision: UnixTimestampPrecision,
/// Whether the `+` sign must be present for a non-negative timestamp.
pub sign_is_mandatory: bool,
}
+/// The end of input.
+///
+/// There is currently not customization for this modifier.
+#[non_exhaustive]
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub struct End;
+
/// Generate the provided code if and only if `pub` is present.
macro_rules! if_pub {
(pub $(#[$attr:meta])*; $($x:tt)*) => {
$(#[$attr])*
///
/// This function exists since [`Default::default()`] cannot be used in a `const` context.
/// It may be removed once that becomes possible. As the [`Default`] trait is in the
/// prelude, removing this function in the future will not cause any resolution failures for
/// the overwhelming majority of users; only users who use `#![no_implicit_prelude]` will be
/// affected. As such it will not be considered a breaking change.
@@ -378,32 +385,34 @@ impl_const_default! {
case_sensitive: true,
};
/// Creates a modifier that indicates the value is [padded with zeroes](Padding::Zero).
@pub Second => Self { padding: Padding::Zero };
/// Creates a modifier that indicates the stringified value contains [one or more
/// digits](Self::OneOrMore).
SubsecondDigits => Self::OneOrMore;
/// Creates a modifier that indicates the stringified value contains [one or more
/// digits](SubsecondDigits::OneOrMore).
@pub Subsecond => Self { digits: SubsecondDigits::OneOrMore };
- /// Creates a modifier that indicates the value uses the `+` sign for all positive values
- /// and is [padded with zeroes](Padding::Zero).
+ /// Creates a modifier that indicates the value only uses a sign for negative values and is
+ /// [padded with zeroes](Padding::Zero).
@pub OffsetHour => Self {
- sign_is_mandatory: true,
+ sign_is_mandatory: false,
padding: Padding::Zero,
};
/// Creates a modifier that indicates the value is [padded with zeroes](Padding::Zero).
@pub OffsetMinute => Self { padding: Padding::Zero };
/// Creates a modifier that indicates the value is [padded with zeroes](Padding::Zero).
@pub OffsetSecond => Self { padding: Padding::Zero };
/// Creates a modifier that indicates the value is [padded with zeroes](Self::Zero).
Padding => Self::Zero;
/// Creates a modifier that indicates the value represents the [number of seconds](Self::Second)
/// since the Unix epoch.
UnixTimestampPrecision => Self::Second;
/// Creates a modifier that indicates the value represents the [number of
/// seconds](UnixTimestampPrecision::Second) since the Unix epoch. The sign is not mandatory.
@pub UnixTimestamp => Self {
precision: UnixTimestampPrecision::Second,
sign_is_mandatory: false,
};
+ /// Creates a modifier used to represent the end of input.
+ @pub End => End;
}
diff --git a/third_party/rust/time/src/format_description/owned_format_item.rs b/third_party/rust/time/src/format_description/owned_format_item.rs
index 6f7f7c2337..31ea9f90ba 100644
--- a/third_party/rust/time/src/format_description/owned_format_item.rs
+++ b/third_party/rust/time/src/format_description/owned_format_item.rs
@@ -1,28 +1,28 @@
//! A format item with owned data.
use alloc::boxed::Box;
use alloc::string::String;
use alloc::vec::Vec;
use core::fmt;
use crate::error;
-use crate::format_description::{Component, FormatItem};
+use crate::format_description::{BorrowedFormatItem, Component};
/// A complete description of how to format and parse a type.
#[non_exhaustive]
#[derive(Clone, PartialEq, Eq)]
pub enum OwnedFormatItem {
/// Bytes that are formatted as-is.
///
- /// **Note**: If you call the `format` method that returns a `String`, these bytes will be
- /// passed through `String::from_utf8_lossy`.
+ /// **Note**: These bytes **should** be UTF-8, but are not required to be. The value is passed
+ /// through `String::from_utf8_lossy` when necessary.
Literal(Box<[u8]>),
/// A minimal representation of a single non-literal item.
Component(Component),
/// A series of literals or components that collectively form a partial or complete
/// description.
Compound(Box<[Self]>),
/// A `FormatItem` that may or may not be present when parsing. If parsing fails, there
/// will be no effect on the resulting `struct`.
///
/// This variant has no effect on formatting, as the value is guaranteed to be present.
@@ -39,59 +39,61 @@ impl fmt::Debug for OwnedFormatItem {
Self::Literal(literal) => f.write_str(&String::from_utf8_lossy(literal)),
Self::Component(component) => component.fmt(f),
Self::Compound(compound) => compound.fmt(f),
Self::Optional(item) => f.debug_tuple("Optional").field(item).finish(),
Self::First(items) => f.debug_tuple("First").field(items).finish(),
}
}
}
// region: conversions from FormatItem
-impl From<FormatItem<'_>> for OwnedFormatItem {
- fn from(item: FormatItem<'_>) -> Self {
+impl From<BorrowedFormatItem<'_>> for OwnedFormatItem {
+ fn from(item: BorrowedFormatItem<'_>) -> Self {
(&item).into()
}
}
-impl From<&FormatItem<'_>> for OwnedFormatItem {
- fn from(item: &FormatItem<'_>) -> Self {
+impl From<&BorrowedFormatItem<'_>> for OwnedFormatItem {
+ fn from(item: &BorrowedFormatItem<'_>) -> Self {
match item {
- FormatItem::Literal(literal) => Self::Literal(literal.to_vec().into_boxed_slice()),
- FormatItem::Component(component) => Self::Component(*component),
- FormatItem::Compound(compound) => Self::Compound(
+ BorrowedFormatItem::Literal(literal) => {
+ Self::Literal(literal.to_vec().into_boxed_slice())
+ }
+ BorrowedFormatItem::Component(component) => Self::Component(*component),
+ BorrowedFormatItem::Compound(compound) => Self::Compound(
compound
.iter()
.cloned()
.map(Into::into)
.collect::<Vec<_>>()
.into_boxed_slice(),
),
- FormatItem::Optional(item) => Self::Optional(Box::new((*item).into())),
- FormatItem::First(items) => Self::First(
+ BorrowedFormatItem::Optional(item) => Self::Optional(Box::new((*item).into())),
+ BorrowedFormatItem::First(items) => Self::First(
items
.iter()
.cloned()
.map(Into::into)
.collect::<Vec<_>>()
.into_boxed_slice(),
),
}
}
}
-impl From<Vec<FormatItem<'_>>> for OwnedFormatItem {
- fn from(items: Vec<FormatItem<'_>>) -> Self {
+impl From<Vec<BorrowedFormatItem<'_>>> for OwnedFormatItem {
+ fn from(items: Vec<BorrowedFormatItem<'_>>) -> Self {
items.as_slice().into()
}
}
-impl<'a, T: AsRef<[FormatItem<'a>]> + ?Sized> From<&T> for OwnedFormatItem {
+impl<'a, T: AsRef<[BorrowedFormatItem<'a>]> + ?Sized> From<&T> for OwnedFormatItem {
fn from(items: &T) -> Self {
Self::Compound(
items
.as_ref()
.iter()
.cloned()
.map(Into::into)
.collect::<Vec<_>>()
.into_boxed_slice(),
)
diff --git a/third_party/rust/time/src/format_description/parse/ast.rs b/third_party/rust/time/src/format_description/parse/ast.rs
index 4957fe0968..c7fc5e05a0 100644
--- a/third_party/rust/time/src/format_description/parse/ast.rs
+++ b/third_party/rust/time/src/format_description/parse/ast.rs
@@ -1,18 +1,19 @@
//! AST for parsing format descriptions.
use alloc::boxed::Box;
use alloc::string::String;
use alloc::vec::Vec;
use core::iter;
use super::{lexer, unused, Error, Location, Spanned, SpannedValue, Unused};
+use crate::internal_macros::bug;
/// One part of a complete format description.
pub(super) enum Item<'a> {
/// A literal string, formatted and parsed as-is.
///
/// This should never be present inside a nested format description.
Literal(Spanned<&'a [u8]>),
/// A sequence of brackets. The first acts as the escape character.
///
/// This should never be present if the lexer has `BACKSLASH_ESCAPE` set to `true`.
diff --git a/third_party/rust/time/src/format_description/parse/format_item.rs b/third_party/rust/time/src/format_description/parse/format_item.rs
index c0e64d92a2..2e1ee6a74e 100644
--- a/third_party/rust/time/src/format_description/parse/format_item.rs
+++ b/third_party/rust/time/src/format_description/parse/format_item.rs
@@ -1,18 +1,19 @@
//! Typed, validated representation of a parsed format description.
use alloc::boxed::Box;
use alloc::string::String;
use core::num::NonZeroU16;
use core::str::{self, FromStr};
use super::{ast, unused, Error, Span, Spanned};
+use crate::internal_macros::bug;
/// Parse an AST iterator into a sequence of format items.
pub(super) fn parse<'a>(
ast_items: impl Iterator<Item = Result<ast::Item<'a>, Error>>,
) -> impl Iterator<Item = Result<Item<'a>, Error>> {
ast_items.map(|ast_item| ast_item.and_then(Item::from_ast))
}
/// A description of how to format and parse one part of a type.
pub(super) enum Item<'a> {
@@ -94,21 +95,21 @@ impl Item<'_> {
.collect::<Result<_, _>>()?;
Item::First {
value: items,
span: opening_bracket.to(closing_bracket),
}
}
})
}
}
-impl<'a> TryFrom<Item<'a>> for crate::format_description::FormatItem<'a> {
+impl<'a> TryFrom<Item<'a>> for crate::format_description::BorrowedFormatItem<'a> {
type Error = Error;
fn try_from(item: Item<'a>) -> Result<Self, Self::Error> {
match item {
Item::Literal(literal) => Ok(Self::Literal(literal)),
Item::Component(component) => Ok(Self::Component(component.into())),
Item::Optional { value: _, span } => Err(Error {
_inner: unused(span.error(
"optional items are not supported in runtime-parsed format descriptions",
)),
@@ -141,28 +142,23 @@ impl From<Item<'_>> for crate::format_description::OwnedFormatItem {
Item::First { value, span: _ } => {
Self::First(value.into_vec().into_iter().map(Into::into).collect())
}
}
}
}
impl<'a> From<Box<[Item<'a>]>> for crate::format_description::OwnedFormatItem {
fn from(items: Box<[Item<'a>]>) -> Self {
let items = items.into_vec();
- if items.len() == 1 {
- if let Ok([item]) = <[_; 1]>::try_from(items) {
- item.into()
- } else {
- bug!("the length was just checked to be 1")
- }
- } else {
- Self::Compound(items.into_iter().map(Self::from).collect())
+ match <[_; 1]>::try_from(items) {
+ Ok([item]) => item.into(),
+ Err(vec) => Self::Compound(vec.into_iter().map(Into::into).collect()),
}
}
}
/// Declare the `Component` struct.
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)*)? };
@@ -183,20 +179,22 @@ macro_rules! component_definition {
$($field: Option<$field_type>),*
})*
$(impl $variant {
/// Parse the component from the AST, given its modifiers.
fn with_modifiers(
modifiers: &[ast::Modifier<'_>],
_component_span: Span,
) -> Result<Self, Error>
{
+ // rustc will complain if the modifier is empty.
+ #[allow(unused_mut)]
let mut this = Self {
$($field: None),*
};
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)?
@@ -273,20 +271,21 @@ macro_rules! component_definition {
}
}
}
// Keep in alphabetical order.
component_definition! {
pub(super) enum Component {
Day = "day" {
padding = "padding": Option<Padding> => padding,
},
+ End = "end" {},
Hour = "hour" {
padding = "padding": Option<Padding> => padding,
base = "repr": Option<HourBase> => is_12_hour_clock,
},
Ignore = "ignore" {
#[required]
count = "count": Option<#[from_str] NonZeroU16> => count,
},
Minute = "minute" {
padding = "padding": Option<Padding> => padding,
diff --git a/third_party/rust/time/src/format_description/parse/lexer.rs b/third_party/rust/time/src/format_description/parse/lexer.rs
index 1604fd4971..2979fff9ee 100644
--- a/third_party/rust/time/src/format_description/parse/lexer.rs
+++ b/third_party/rust/time/src/format_description/parse/lexer.rs
@@ -1,20 +1,20 @@
//! Lexer for parsing format descriptions.
use core::iter;
use super::{unused, Error, Location, Spanned, SpannedValue};
/// An iterator over the lexed tokens.
pub(super) struct Lexed<I: Iterator> {
/// The internal iterator.
- iter: core::iter::Peekable<I>,
+ iter: iter::Peekable<I>,
}
impl<I: Iterator> Iterator for Lexed<I> {
type Item = I::Item;
fn next(&mut self) -> Option<Self::Item> {
self.iter.next()
}
}
diff --git a/third_party/rust/time/src/format_description/parse/mod.rs b/third_party/rust/time/src/format_description/parse/mod.rs
index 2ab58f172a..602ecf71f6 100644
--- a/third_party/rust/time/src/format_description/parse/mod.rs
+++ b/third_party/rust/time/src/format_description/parse/mod.rs
@@ -1,15 +1,17 @@
//! Parser for format descriptions.
use alloc::boxed::Box;
use alloc::vec::Vec;
+use crate::{error, format_description};
+
/// A helper macro to make version restrictions simpler to read and write.
macro_rules! version {
($range:expr) => {
$range.contains(&VERSION)
};
}
/// A helper macro to statically validate the version (when used as a const parameter).
macro_rules! validate_version {
($version:ident) => {
@@ -33,34 +35,32 @@ impl<const N: usize> Version<N> {
/// Parse a sequence of items from the format description.
///
/// The syntax for the format description can be found in [the
/// book](https://time-rs.github.io/book/api/format-description.html).
///
/// This function exists for backward compatibility reasons. It is equivalent to calling
/// `parse_borrowed::<1>(s)`. In the future, this function will be deprecated in favor of
/// `parse_borrowed`.
pub fn parse(
s: &str,
-) -> Result<Vec<crate::format_description::FormatItem<'_>>, crate::error::InvalidFormatDescription>
-{
+) -> Result<Vec<format_description::BorrowedFormatItem<'_>>, error::InvalidFormatDescription> {
parse_borrowed::<1>(s)
}
/// Parse a sequence of items from the format description.
///
/// The syntax for the format description can be found in [the
/// book](https://time-rs.github.io/book/api/format-description.html). The version of the format
/// description is provided as the const parameter. **It is recommended to use version 2.**
pub fn parse_borrowed<const VERSION: usize>(
s: &str,
-) -> Result<Vec<crate::format_description::FormatItem<'_>>, crate::error::InvalidFormatDescription>
-{
+) -> Result<Vec<format_description::BorrowedFormatItem<'_>>, error::InvalidFormatDescription> {
validate_version!(VERSION);
let mut lexed = lexer::lex::<VERSION>(s.as_bytes());
let ast = ast::parse::<_, VERSION>(&mut lexed);
let format_items = format_item::parse(ast);
Ok(format_items
.map(|res| res.and_then(TryInto::try_into))
.collect::<Result<_, _>>()?)
}
/// Parse a sequence of items from the format description.
@@ -68,28 +68,26 @@ pub fn parse_borrowed<const VERSION: usize>(
/// The syntax for the format description can be found in [the
/// book](https://time-rs.github.io/book/api/format-description.html). The version of the format
/// description is provided as the const parameter.
///
/// Unlike [`parse`], this function returns [`OwnedFormatItem`], which owns its contents. This means
/// that there is no lifetime that needs to be handled. **It is recommended to use version 2.**
///
/// [`OwnedFormatItem`]: crate::format_description::OwnedFormatItem
pub fn parse_owned<const VERSION: usize>(
s: &str,
-) -> Result<crate::format_description::OwnedFormatItem, crate::error::InvalidFormatDescription> {
+) -> Result<format_description::OwnedFormatItem, error::InvalidFormatDescription> {
validate_version!(VERSION);
let mut lexed = lexer::lex::<VERSION>(s.as_bytes());
let ast = ast::parse::<_, VERSION>(&mut lexed);
let format_items = format_item::parse(ast);
- let items = format_items
- .map(|res| res.map(Into::into))
- .collect::<Result<Box<_>, _>>()?;
+ let items = format_items.collect::<Result<Box<_>, _>>()?;
Ok(items.into())
}
/// A location within a string.
#[derive(Clone, Copy)]
struct Location {
/// The zero-indexed byte of the string.
byte: u32,
}
@@ -215,31 +213,30 @@ struct ErrorInner {
_message: &'static str,
/// Where the error originated.
_span: Span,
}
/// A complete error description.
struct Error {
/// The internal error.
_inner: Unused<ErrorInner>,
/// The error needed for interoperability with the rest of `time`.
- public: crate::error::InvalidFormatDescription,
+ public: error::InvalidFormatDescription,
}
-impl From<Error> for crate::error::InvalidFormatDescription {
+impl From<Error> for error::InvalidFormatDescription {
fn from(error: Error) -> Self {
error.public
}
}
/// A value that may be used in the future, but currently is not.
///
/// This struct exists so that data can semantically be passed around without _actually_ passing it
/// around. This way the data still exists if it is needed in the future.
// `PhantomData` is not used directly because we don't want to introduce any trait implementations.
struct Unused<T>(core::marker::PhantomData<T>);
/// Indicate that a value is currently unused.
-#[allow(clippy::missing_const_for_fn)] // false positive
fn unused<T>(_: T) -> Unused<T> {
Unused(core::marker::PhantomData)
}
diff --git a/third_party/rust/time/src/format_description/well_known/iso8601.rs b/third_party/rust/time/src/format_description/well_known/iso8601.rs
index f19181a926..6b8ff2ab63 100644
--- a/third_party/rust/time/src/format_description/well_known/iso8601.rs
+++ b/third_party/rust/time/src/format_description/well_known/iso8601.rs
@@ -1,33 +1,19 @@
//! The format described in ISO 8601.
mod adt_hack;
use core::num::NonZeroU8;
-pub use self::adt_hack::{DoNotRelyOnWhatThisIs, EncodedConfig};
-
-/// A configuration for [`Iso8601`] that only parses values.
-const PARSING_ONLY: EncodedConfig = Config {
- formatted_components: FormattedComponents::None,
- use_separators: false,
- year_is_six_digits: false,
- date_kind: DateKind::Calendar,
- time_precision: TimePrecision::Hour {
- decimal_digits: None,
- },
- offset_precision: OffsetPrecision::Hour,
-}
-.encode();
-
-/// The default configuration for [`Iso8601`].
-const DEFAULT_CONFIG: EncodedConfig = Config::DEFAULT.encode();
+#[doc(hidden, no_inline)]
+pub use self::adt_hack::DoNotRelyOnWhatThisIs;
+pub use self::adt_hack::EncodedConfig;
/// The format described in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html).
///
/// This implementation is of ISO 8601-1:2019. It may not be compatible with other versions.
///
/// The const parameter `CONFIG` **must** be a value that was returned by [`Config::encode`].
/// Passing any other value is **unspecified behavior**.
///
/// Example: 1997-11-21T09:55:06.000000000-06:00
///
@@ -36,52 +22,77 @@ const DEFAULT_CONFIG: EncodedConfig = Config::DEFAULT.encode();
#[cfg_attr(not(feature = "formatting"), doc = "```rust,ignore")]
/// # use time::format_description::well_known::Iso8601;
/// # use time_macros::datetime;
/// assert_eq!(
/// datetime!(1997-11-12 9:55:06 -6:00).format(&Iso8601::DEFAULT)?,
/// "1997-11-12T09:55:06.000000000-06:00"
/// );
/// # Ok::<_, time::Error>(())
/// ```
#[derive(Clone, Copy, PartialEq, Eq)]
-pub struct Iso8601<const CONFIG: EncodedConfig = DEFAULT_CONFIG>;
+pub struct Iso8601<const CONFIG: EncodedConfig = { Config::DEFAULT.encode() }>;
impl<const CONFIG: EncodedConfig> core::fmt::Debug for Iso8601<CONFIG> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("Iso8601")
.field("config", &Config::decode(CONFIG))
.finish()
}
}
-impl Iso8601<DEFAULT_CONFIG> {
+/// Define associated constants for `Iso8601`.
+macro_rules! define_assoc_consts {
+ ($($(#[$doc:meta])* $vis:vis const $const_name:ident = $format:expr;)*) => {$(
+ const $const_name: EncodedConfig = $format.encode();
+ impl Iso8601<$const_name> {
+ $(#[$doc])*
+ $vis const $const_name: Self = Self;
+ }
+ )*};
+}
+
+define_assoc_consts! {
/// An [`Iso8601`] with the default configuration.
///
/// The following is the default behavior:
///
/// - The configuration can be used for both formatting and parsing.
/// - The date, time, and UTC offset are all formatted.
/// - Separators (such as `-` and `:`) are included.
/// - The year contains four digits, such that the year must be between 0 and 9999.
/// - The date uses the calendar format.
/// - The time has precision to the second and nine decimal digits.
/// - The UTC offset has precision to the minute.
///
- /// If you need different behavior, use [`Config::DEFAULT`] and [`Config`]'s methods to create
- /// a custom configuration.
- pub const DEFAULT: Self = Self;
-}
-
-impl Iso8601<PARSING_ONLY> {
+ /// If you need different behavior, use another associated constant. For full customization, use
+ /// [`Config::DEFAULT`] and [`Config`]'s methods to create a custom configuration.
+ pub const DEFAULT = Config::DEFAULT;
/// An [`Iso8601`] that can only be used for parsing. Using this to format a value is
/// unspecified behavior.
- pub const PARSING: Self = Self;
+ pub const PARSING = Config::PARSING;
+ /// An [`Iso8601`] that handles only the date, but is otherwise the same as [`Config::DEFAULT`].
+ pub const DATE = Config::DEFAULT.set_formatted_components(FormattedComponents::Date);
+ /// An [`Iso8601`] that handles only the time, but is otherwise the same as [`Config::DEFAULT`].
+ pub const TIME = Config::DEFAULT.set_formatted_components(FormattedComponents::Time);
+ /// An [`Iso8601`] that handles only the UTC offset, but is otherwise the same as
+ /// [`Config::DEFAULT`].
+ pub const OFFSET = Config::DEFAULT.set_formatted_components(FormattedComponents::Offset);
+ /// An [`Iso8601`] that handles the date and time, but is otherwise the same as
+ /// [`Config::DEFAULT`].
+ pub const DATE_TIME = Config::DEFAULT.set_formatted_components(FormattedComponents::DateTime);
+ /// An [`Iso8601`] that handles the date, time, and UTC offset. This is the same as
+ /// [`Config::DEFAULT`].
+ pub const DATE_TIME_OFFSET = Config::DEFAULT;
+ /// An [`Iso8601`] that handles the time and UTC offset, but is otherwise the same as
+ /// [`Config::DEFAULT`].
+ pub const TIME_OFFSET = Config::DEFAULT
+ .set_formatted_components(FormattedComponents::TimeOffset);
}
/// Which components to format.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum FormattedComponents {
/// The configuration can only be used for parsing. Using this to format a value is
/// unspecified behavior.
None,
/// Format only the date.
Date,
@@ -107,33 +118,33 @@ pub enum DateKind {
/// Use the week-ordinal format.
Ordinal,
}
/// The precision and number of decimal digits present for the time.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TimePrecision {
/// Format the hour only. Minutes, seconds, and nanoseconds will be represented with the
/// specified number of decimal digits, if any.
Hour {
- #[allow(clippy::missing_docs_in_private_items)]
+ #[allow(missing_docs)]
decimal_digits: Option<NonZeroU8>,
},
/// Format the hour and minute. Seconds and nanoseconds will be represented with the specified
/// number of decimal digits, if any.
Minute {
- #[allow(clippy::missing_docs_in_private_items)]
+ #[allow(missing_docs)]
decimal_digits: Option<NonZeroU8>,
},
/// Format the hour, minute, and second. Nanoseconds will be represented with the specified
/// number of decimal digits, if any.
Second {
- #[allow(clippy::missing_docs_in_private_items)]
+ #[allow(missing_docs)]
decimal_digits: Option<NonZeroU8>,
},
}
/// The precision for the UTC offset.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum OffsetPrecision {
/// Format only the offset hour. Requires the offset minute to be zero.
Hour,
/// Format both the offset hour and minute.
@@ -179,20 +190,33 @@ impl Config {
formatted_components: FormattedComponents::DateTimeOffset,
use_separators: true,
year_is_six_digits: false,
date_kind: DateKind::Calendar,
time_precision: TimePrecision::Second {
decimal_digits: NonZeroU8::new(9),
},
offset_precision: OffsetPrecision::Minute,
};
+ /// A configuration that can only be used for parsing. Using this to format a value is
+ /// unspecified behavior.
+ const PARSING: Self = Self {
+ formatted_components: FormattedComponents::None,
+ use_separators: false,
+ year_is_six_digits: false,
+ date_kind: DateKind::Calendar,
+ time_precision: TimePrecision::Hour {
+ decimal_digits: None,
+ },
+ offset_precision: OffsetPrecision::Hour,
+ };
+
/// Set whether the format the date, time, and/or UTC offset.
pub const fn set_formatted_components(self, formatted_components: FormattedComponents) -> Self {
Self {
formatted_components,
..self
}
}
/// Set whether the format contains separators (such as `-` or `:`).
pub const fn set_use_separators(self, use_separators: bool) -> Self {
diff --git a/third_party/rust/time/src/format_description/well_known/iso8601/adt_hack.rs b/third_party/rust/time/src/format_description/well_known/iso8601/adt_hack.rs
index 1fdecaf269..12b1c725fd 100644
--- a/third_party/rust/time/src/format_description/well_known/iso8601/adt_hack.rs
+++ b/third_party/rust/time/src/format_description/well_known/iso8601/adt_hack.rs
@@ -42,25 +42,24 @@ impl<const CONFIG: EncodedConfig> Iso8601<CONFIG> {
pub(crate) const USE_SEPARATORS: bool = Self::CONFIG.use_separators;
/// Which format to use for the date.
pub(crate) const DATE_KIND: DateKind = Self::CONFIG.date_kind;
/// The precision and number of decimal digits to use for the time.
pub(crate) const TIME_PRECISION: TimePrecision = Self::CONFIG.time_precision;
/// The precision for the UTC offset.
pub(crate) const OFFSET_PRECISION: OffsetPrecision = Self::CONFIG.offset_precision;
}
impl Config {
- /// Encode the configuration, permitting it to be used as a const parameter of
- /// [`Iso8601`](super::Iso8601).
+ /// Encode the configuration, permitting it to be used as a const parameter of [`Iso8601`].
///
- /// The value returned by this method must only be used as a const parameter to
- /// [`Iso8601`](super::Iso8601). Any other usage is unspecified behavior.
+ /// The value returned by this method must only be used as a const parameter to [`Iso8601`]. Any
+ /// other usage is unspecified behavior.
pub const fn encode(&self) -> EncodedConfig {
let mut bytes = [0; EncodedConfig::BITS as usize / 8];
bytes[0] = match self.formatted_components {
FC::None => 0,
FC::Date => 1,
FC::Time => 2,
FC::Offset => 3,
FC::DateTime => 4,
FC::DateTimeOffset => 5,
diff --git a/third_party/rust/time/src/formatting/formattable.rs b/third_party/rust/time/src/formatting/formattable.rs
index 2b4b350883..7ee17a5cc1 100644
--- a/third_party/rust/time/src/formatting/formattable.rs
+++ b/third_party/rust/time/src/formatting/formattable.rs
@@ -1,33 +1,37 @@
//! A trait that can be used to format an item from its components.
+use alloc::string::String;
+use alloc::vec::Vec;
use core::ops::Deref;
use std::io;
+use num_conv::prelude::*;
+
use crate::format_description::well_known::iso8601::EncodedConfig;
use crate::format_description::well_known::{Iso8601, Rfc2822, Rfc3339};
-use crate::format_description::{FormatItem, OwnedFormatItem};
+use crate::format_description::{BorrowedFormatItem, OwnedFormatItem};
use crate::formatting::{
format_component, format_number_pad_zero, iso8601, write, MONTH_NAMES, WEEKDAY_NAMES,
};
use crate::{error, Date, Time, UtcOffset};
/// A type that describes a format.
///
/// Implementors of [`Formattable`] are [format descriptions](crate::format_description).
///
/// [`Date::format`] and [`Time::format`] each use a format description to generate
/// a String from their data. See the respective methods for usage examples.
#[cfg_attr(__time_03_docs, doc(notable_trait))]
pub trait Formattable: sealed::Sealed {}
-impl Formattable for FormatItem<'_> {}
-impl Formattable for [FormatItem<'_>] {}
+impl Formattable for BorrowedFormatItem<'_> {}
+impl Formattable for [BorrowedFormatItem<'_>] {}
impl Formattable for OwnedFormatItem {}
impl Formattable for [OwnedFormatItem] {}
impl Formattable for Rfc3339 {}
impl Formattable for Rfc2822 {}
impl<const CONFIG: EncodedConfig> Formattable for Iso8601<CONFIG> {}
impl<T: Deref> Formattable for T where T::Target: Formattable {}
/// Seal the trait to prevent downstream users from implementing it.
mod sealed {
#[allow(clippy::wildcard_imports)]
@@ -52,42 +56,42 @@ mod sealed {
offset: Option<UtcOffset>,
) -> Result<String, error::Format> {
let mut buf = Vec::new();
self.format_into(&mut buf, date, time, offset)?;
Ok(String::from_utf8_lossy(&buf).into_owned())
}
}
}
// region: custom formats
-impl<'a> sealed::Sealed for FormatItem<'a> {
+impl sealed::Sealed for BorrowedFormatItem<'_> {
fn format_into(
&self,
output: &mut impl io::Write,
date: Option<Date>,
time: Option<Time>,
offset: Option<UtcOffset>,
) -> Result<usize, error::Format> {
Ok(match *self {
Self::Literal(literal) => write(output, literal)?,
Self::Component(component) => format_component(output, component, date, time, offset)?,
Self::Compound(items) => items.format_into(output, date, time, offset)?,
Self::Optional(item) => item.format_into(output, date, time, offset)?,
Self::First(items) => match items {
[] => 0,
[item, ..] => item.format_into(output, date, time, offset)?,
},
})
}
}
-impl<'a> sealed::Sealed for [FormatItem<'a>] {
+impl sealed::Sealed for [BorrowedFormatItem<'_>] {
fn format_into(
&self,
output: &mut impl io::Write,
date: Option<Date>,
time: Option<Time>,
offset: Option<UtcOffset>,
) -> Result<usize, error::Format> {
let mut bytes = 0;
for item in self.iter() {
bytes += item.format_into(output, date, time, offset)?;
@@ -168,28 +172,31 @@ impl sealed::Sealed for Rfc2822 {
if year < 1900 {
return Err(error::Format::InvalidComponent("year"));
}
if offset.seconds_past_minute() != 0 {
return Err(error::Format::InvalidComponent("offset_second"));
}
bytes += write(
output,
- &WEEKDAY_NAMES[date.weekday().number_days_from_monday() as usize][..3],
+ &WEEKDAY_NAMES[date.weekday().number_days_from_monday().extend::<usize>()][..3],
)?;
bytes += write(output, b", ")?;
bytes += format_number_pad_zero::<2>(output, day)?;
bytes += write(output, b" ")?;
- bytes += write(output, &MONTH_NAMES[month as usize - 1][..3])?;
+ bytes += write(
+ output,
+ &MONTH_NAMES[u8::from(month).extend::<usize>() - 1][..3],
+ )?;
bytes += write(output, b" ")?;
- bytes += format_number_pad_zero::<4>(output, year as u32)?;
+ bytes += format_number_pad_zero::<4>(output, year.cast_unsigned())?;
bytes += write(output, b" ")?;
bytes += format_number_pad_zero::<2>(output, time.hour())?;
bytes += write(output, b":")?;
bytes += format_number_pad_zero::<2>(output, time.minute())?;
bytes += write(output, b":")?;
bytes += format_number_pad_zero::<2>(output, time.second())?;
bytes += write(output, b" ")?;
bytes += write(output, if offset.is_negative() { b"-" } else { b"+" })?;
bytes += format_number_pad_zero::<2>(output, offset.whole_hours().unsigned_abs())?;
bytes += format_number_pad_zero::<2>(output, offset.minutes_past_hour().unsigned_abs())?;
@@ -210,27 +217,30 @@ impl sealed::Sealed for Rfc3339 {
let time = time.ok_or(error::Format::InsufficientTypeInformation)?;
let offset = offset.ok_or(error::Format::InsufficientTypeInformation)?;
let mut bytes = 0;
let year = date.year();
if !(0..10_000).contains(&year) {
return Err(error::Format::InvalidComponent("year"));
}
+ if offset.whole_hours().unsigned_abs() > 23 {
+ return Err(error::Format::InvalidComponent("offset_hour"));
+ }
if offset.seconds_past_minute() != 0 {
return Err(error::Format::InvalidComponent("offset_second"));
}
- bytes += format_number_pad_zero::<4>(output, year as u32)?;
+ bytes += format_number_pad_zero::<4>(output, year.cast_unsigned())?;
bytes += write(output, b"-")?;
- bytes += format_number_pad_zero::<2>(output, date.month() as u8)?;
+ bytes += format_number_pad_zero::<2>(output, u8::from(date.month()))?;
bytes += write(output, b"-")?;
bytes += format_number_pad_zero::<2>(output, date.day())?;
bytes += write(output, b"T")?;
bytes += format_number_pad_zero::<2>(output, time.hour())?;
bytes += write(output, b":")?;
bytes += format_number_pad_zero::<2>(output, time.minute())?;
bytes += write(output, b":")?;
bytes += format_number_pad_zero::<2>(output, time.second())?;
#[allow(clippy::if_not_else)]
diff --git a/third_party/rust/time/src/formatting/iso8601.rs b/third_party/rust/time/src/formatting/iso8601.rs
index 29d443ef46..ee49d7d896 100644
--- a/third_party/rust/time/src/formatting/iso8601.rs
+++ b/third_party/rust/time/src/formatting/iso8601.rs
@@ -1,14 +1,16 @@
//! Helpers for implementing formatting for ISO 8601.
use std::io;
+use num_conv::prelude::*;
+
use crate::convert::*;
use crate::format_description::well_known::iso8601::{
DateKind, EncodedConfig, OffsetPrecision, TimePrecision,
};
use crate::format_description::well_known::Iso8601;
use crate::formatting::{format_float, format_number_pad_zero, write, write_if, write_if_else};
use crate::{error, Date, Time, UtcOffset};
/// Format the date portion of ISO 8601.
pub(super) fn format_date<const CONFIG: EncodedConfig>(
@@ -19,51 +21,51 @@ pub(super) fn format_date<const CONFIG: EncodedConfig>(
match Iso8601::<CONFIG>::DATE_KIND {
DateKind::Calendar => {
let (year, month, day) = date.to_calendar_date();
if Iso8601::<CONFIG>::YEAR_IS_SIX_DIGITS {
bytes += write_if_else(output, year < 0, b"-", b"+")?;
bytes += format_number_pad_zero::<6>(output, year.unsigned_abs())?;
} else if !(0..=9999).contains(&year) {
return Err(error::Format::InvalidComponent("year"));
} else {
- bytes += format_number_pad_zero::<4>(output, year as u32)?;
+ bytes += format_number_pad_zero::<4>(output, year.cast_unsigned())?;
}
bytes += write_if(output, Iso8601::<CONFIG>::USE_SEPARATORS, b"-")?;
- bytes += format_number_pad_zero::<2>(output, month as u8)?;
+ bytes += format_number_pad_zero::<2>(output, u8::from(month))?;
bytes += write_if(output, Iso8601::<CONFIG>::USE_SEPARATORS, b"-")?;
bytes += format_number_pad_zero::<2>(output, day)?;
}
DateKind::Week => {
let (year, week, day) = date.to_iso_week_date();
if Iso8601::<CONFIG>::YEAR_IS_SIX_DIGITS {
bytes += write_if_else(output, year < 0, b"-", b"+")?;
bytes += format_number_pad_zero::<6>(output, year.unsigned_abs())?;
} else if !(0..=9999).contains(&year) {
return Err(error::Format::InvalidComponent("year"));
} else {
- bytes += format_number_pad_zero::<4>(output, year as u32)?;
+ bytes += format_number_pad_zero::<4>(output, year.cast_unsigned())?;
}
bytes += write_if_else(output, Iso8601::<CONFIG>::USE_SEPARATORS, b"-W", b"W")?;
bytes += format_number_pad_zero::<2>(output, week)?;
bytes += write_if(output, Iso8601::<CONFIG>::USE_SEPARATORS, b"-")?;
bytes += format_number_pad_zero::<1>(output, day.number_from_monday())?;
}
DateKind::Ordinal => {
let (year, day) = date.to_ordinal_date();
if Iso8601::<CONFIG>::YEAR_IS_SIX_DIGITS {
bytes += write_if_else(output, year < 0, b"-", b"+")?;
bytes += format_number_pad_zero::<6>(output, year.unsigned_abs())?;
} else if !(0..=9999).contains(&year) {
return Err(error::Format::InvalidComponent("year"));
} else {
- bytes += format_number_pad_zero::<4>(output, year as u32)?;
+ bytes += format_number_pad_zero::<4>(output, year.cast_unsigned())?;
}
bytes += write_if(output, Iso8601::<CONFIG>::USE_SEPARATORS, b"-")?;
bytes += format_number_pad_zero::<3>(output, day)?;
}
}
Ok(bytes)
}
/// Format the time portion of ISO 8601.
@@ -78,39 +80,39 @@ pub(super) fn format_time<const CONFIG: EncodedConfig>(
output,
Iso8601::<CONFIG>::USE_SEPARATORS || Iso8601::<CONFIG>::FORMAT_DATE,
b"T",
)?;
let (hours, minutes, seconds, nanoseconds) = time.as_hms_nano();
match Iso8601::<CONFIG>::TIME_PRECISION {
TimePrecision::Hour { decimal_digits } => {
let hours = (hours as f64)
- + (minutes as f64) / Minute.per(Hour) as f64
- + (seconds as f64) / Second.per(Hour) as f64
- + (nanoseconds as f64) / Nanosecond.per(Hour) as f64;
+ + (minutes as f64) / Minute::per(Hour) as f64
+ + (seconds as f64) / Second::per(Hour) as f64
+ + (nanoseconds as f64) / Nanosecond::per(Hour) as f64;
format_float(output, hours, 2, decimal_digits)?;
}
TimePrecision::Minute { decimal_digits } => {
bytes += format_number_pad_zero::<2>(output, hours)?;
bytes += write_if(output, Iso8601::<CONFIG>::USE_SEPARATORS, b":")?;
let minutes = (minutes as f64)
- + (seconds as f64) / Second.per(Minute) as f64
- + (nanoseconds as f64) / Nanosecond.per(Minute) as f64;
+ + (seconds as f64) / Second::per(Minute) as f64
+ + (nanoseconds as f64) / Nanosecond::per(Minute) as f64;
bytes += format_float(output, minutes, 2, decimal_digits)?;
}
TimePrecision::Second { decimal_digits } => {
bytes += format_number_pad_zero::<2>(output, hours)?;
bytes += write_if(output, Iso8601::<CONFIG>::USE_SEPARATORS, b":")?;
bytes += format_number_pad_zero::<2>(output, minutes)?;
bytes += write_if(output, Iso8601::<CONFIG>::USE_SEPARATORS, b":")?;
- let seconds = (seconds as f64) + (nanoseconds as f64) / Nanosecond.per(Second) as f64;
+ let seconds = (seconds as f64) + (nanoseconds as f64) / Nanosecond::per(Second) as f64;
bytes += format_float(output, seconds, 2, decimal_digits)?;
}
}
Ok(bytes)
}
/// Format the UTC offset portion of ISO 8601.
pub(super) fn format_offset<const CONFIG: EncodedConfig>(
output: &mut impl io::Write,
diff --git a/third_party/rust/time/src/formatting/mod.rs b/third_party/rust/time/src/formatting/mod.rs
index e5017063ab..b57c152221 100644
--- a/third_party/rust/time/src/formatting/mod.rs
+++ b/third_party/rust/time/src/formatting/mod.rs
@@ -1,20 +1,22 @@
//! Formatting for various types.
pub(crate) mod formattable;
mod iso8601;
-
use core::num::NonZeroU8;
use std::io;
+use num_conv::prelude::*;
+
pub use self::formattable::Formattable;
use crate::convert::*;
+use crate::ext::DigitCount;
use crate::format_description::{modifier, Component};
use crate::{error, Date, OffsetDateTime, Time, UtcOffset};
#[allow(clippy::missing_docs_in_private_items)]
const MONTH_NAMES: [&[u8]; 12] = [
b"January",
b"February",
b"March",
b"April",
b"May",
@@ -31,100 +33,20 @@ const MONTH_NAMES: [&[u8]; 12] = [
const WEEKDAY_NAMES: [&[u8]; 7] = [
b"Monday",
b"Tuesday",
b"Wednesday",
b"Thursday",
b"Friday",
b"Saturday",
b"Sunday",
];
-// region: extension trait
-/// A trait that indicates the formatted width of the value can be determined.
-///
-/// Note that this should not be implemented for any signed integers. This forces the caller to
-/// write the sign if desired.
-pub(crate) trait DigitCount {
- /// The number of digits in the stringified value.
- fn num_digits(self) -> u8;
-}
-impl DigitCount for u8 {
- fn num_digits(self) -> u8 {
- // Using a lookup table as with u32 is *not* faster in standalone benchmarks.
- if self < 10 {
- 1
- } else if self < 100 {
- 2
- } else {
- 3
- }
- }
-}
-impl DigitCount for u16 {
- fn num_digits(self) -> u8 {
- // Using a lookup table as with u32 is *not* faster in standalone benchmarks.
- if self < 10 {
- 1
- } else if self < 100 {
- 2
- } else if self < 1_000 {
- 3
- } else if self < 10_000 {
- 4
- } else {
- 5
- }
- }
-}
-
-impl DigitCount for u32 {
- fn num_digits(self) -> u8 {
- /// Lookup table
- const TABLE: &[u64] = &[
- 0x0001_0000_0000,
- 0x0001_0000_0000,
- 0x0001_0000_0000,
- 0x0001_FFFF_FFF6,
- 0x0002_0000_0000,
- 0x0002_0000_0000,
- 0x0002_FFFF_FF9C,
- 0x0003_0000_0000,
- 0x0003_0000_0000,
- 0x0003_FFFF_FC18,
- 0x0004_0000_0000,
- 0x0004_0000_0000,
- 0x0004_0000_0000,
- 0x0004_FFFF_D8F0,
- 0x0005_0000_0000,
- 0x0005_0000_0000,
- 0x0005_FFFE_7960,
- 0x0006_0000_0000,
- 0x0006_0000_0000,
- 0x0006_FFF0_BDC0,
- 0x0007_0000_0000,
- 0x0007_0000_0000,
- 0x0007_0000_0000,
- 0x0007_FF67_6980,
- 0x0008_0000_0000,
- 0x0008_0000_0000,
- 0x0008_FA0A_1F00,
- 0x0009_0000_0000,
- 0x0009_0000_0000,
- 0x0009_C465_3600,
- 0x000A_0000_0000,
- 0x000A_0000_0000,
- ];
- ((self as u64 + TABLE[31_u32.saturating_sub(self.leading_zeros()) as usize]) >> 32) as _
- }
-}
-// endregion extension trait
-
/// Write all bytes to the output, returning the number of bytes written.
pub(crate) fn write(output: &mut impl io::Write, bytes: &[u8]) -> io::Result<usize> {
output.write_all(bytes)?;
Ok(bytes.len())
}
/// If `pred` is true, write all bytes to the output, returning the number of bytes written.
pub(crate) fn write_if(output: &mut impl io::Write, pred: bool, bytes: &[u8]) -> io::Result<usize> {
if pred { write(output, bytes) } else { Ok(0) }
}
@@ -144,28 +66,28 @@ pub(crate) fn write_if_else(
/// This method accepts the number of digits before and after the decimal. The value will be padded
/// with zeroes to the left if necessary.
pub(crate) fn format_float(
output: &mut impl io::Write,
value: f64,
digits_before_decimal: u8,
digits_after_decimal: Option<NonZeroU8>,
) -> io::Result<usize> {
match digits_after_decimal {
Some(digits_after_decimal) => {
- let digits_after_decimal = digits_after_decimal.get() as usize;
- let width = digits_before_decimal as usize + 1 + digits_after_decimal;
+ let digits_after_decimal = digits_after_decimal.get().extend();
+ let width = digits_before_decimal.extend::<usize>() + 1 + digits_after_decimal;
write!(output, "{value:0>width$.digits_after_decimal$}")?;
Ok(width)
}
None => {
let value = value.trunc() as u64;
- let width = digits_before_decimal as usize;
+ let width = digits_before_decimal.extend();
write!(output, "{value:0>width$}")?;
Ok(width)
}
}
}
/// Format a number with the provided padding and width.
///
/// The sign must be written by the caller.
pub(crate) fn format_number<const WIDTH: u8>(
@@ -243,21 +165,33 @@ pub(crate) fn format_component(
(Period(modifier), _, Some(time), _) => fmt_period(output, time, modifier)?,
(Second(modifier), _, Some(time), _) => fmt_second(output, time, modifier)?,
(Subsecond(modifier), _, Some(time), _) => fmt_subsecond(output, time, modifier)?,
(OffsetHour(modifier), .., Some(offset)) => fmt_offset_hour(output, offset, modifier)?,
(OffsetMinute(modifier), .., Some(offset)) => fmt_offset_minute(output, offset, modifier)?,
(OffsetSecond(modifier), .., Some(offset)) => fmt_offset_second(output, offset, modifier)?,
(Ignore(_), ..) => 0,
(UnixTimestamp(modifier), Some(date), Some(time), Some(offset)) => {
fmt_unix_timestamp(output, date, time, offset, modifier)?
}
- _ => return Err(error::Format::InsufficientTypeInformation),
+ (End(modifier::End {}), ..) => 0,
+
+ // This is functionally the same as a wildcard arm, but it will cause an error if a new
+ // component is added. This is to avoid a bug where a new component, the code compiles, and
+ // formatting fails.
+ // Allow unreachable patterns because some branches may be fully matched above.
+ #[allow(unreachable_patterns)]
+ (
+ Day(_) | Month(_) | Ordinal(_) | Weekday(_) | WeekNumber(_) | Year(_) | Hour(_)
+ | Minute(_) | Period(_) | Second(_) | Subsecond(_) | OffsetHour(_) | OffsetMinute(_)
+ | OffsetSecond(_) | Ignore(_) | UnixTimestamp(_) | End(_),
+ ..,
+ ) => return Err(error::Format::InsufficientTypeInformation),
})
}
// region: date formatters
/// Format the day into the designated output.
fn fmt_day(
output: &mut impl io::Write,
date: Date,
modifier::Day { padding }: modifier::Day,
) -> Result<usize, io::Error> {
@@ -268,23 +202,31 @@ fn fmt_day(
fn fmt_month(
output: &mut impl io::Write,
date: Date,
modifier::Month {
padding,
repr,
case_sensitive: _, // no effect on formatting
}: modifier::Month,
) -> Result<usize, io::Error> {
match repr {
- modifier::MonthRepr::Numerical => format_number::<2>(output, date.month() as u8, padding),
- modifier::MonthRepr::Long => write(output, MONTH_NAMES[date.month() as usize - 1]),
- modifier::MonthRepr::Short => write(output, &MONTH_NAMES[date.month() as usize - 1][..3]),
+ modifier::MonthRepr::Numerical => {
+ format_number::<2>(output, u8::from(date.month()), padding)
+ }
+ modifier::MonthRepr::Long => write(
+ output,
+ MONTH_NAMES[u8::from(date.month()).extend::<usize>() - 1],
+ ),
+ modifier::MonthRepr::Short => write(
+ output,
+ &MONTH_NAMES[u8::from(date.month()).extend::<usize>() - 1][..3],
+ ),
}
}
/// Format the ordinal into the designated output.
fn fmt_ordinal(
output: &mut impl io::Write,
date: Date,
modifier::Ordinal { padding }: modifier::Ordinal,
) -> Result<usize, io::Error> {
format_number::<3>(output, date.ordinal(), padding)
@@ -296,34 +238,34 @@ fn fmt_weekday(
date: Date,
modifier::Weekday {
repr,
one_indexed,
case_sensitive: _, // no effect on formatting
}: modifier::Weekday,
) -> Result<usize, io::Error> {
match repr {
modifier::WeekdayRepr::Short => write(
output,
- &WEEKDAY_NAMES[date.weekday().number_days_from_monday() as usize][..3],
+ &WEEKDAY_NAMES[date.weekday().number_days_from_monday().extend::<usize>()][..3],
),
modifier::WeekdayRepr::Long => write(
output,
- WEEKDAY_NAMES[date.weekday().number_days_from_monday() as usize],
+ WEEKDAY_NAMES[date.weekday().number_days_from_monday().extend::<usize>()],
),
modifier::WeekdayRepr::Sunday => format_number::<1>(
output,
- date.weekday().number_days_from_sunday() + one_indexed as u8,
+ date.weekday().number_days_from_sunday() + u8::from(one_indexed),
modifier::Padding::None,
),
modifier::WeekdayRepr::Monday => format_number::<1>(
output,
- date.weekday().number_days_from_monday() + one_indexed as u8,
+ date.weekday().number_days_from_monday() + u8::from(one_indexed),
modifier::Padding::None,
),
}
}
/// Format the week number into the designated output.
fn fmt_week_number(
output: &mut impl io::Write,
date: Date,
modifier::WeekNumber { padding, repr }: modifier::WeekNumber,
@@ -508,38 +450,39 @@ fn fmt_offset_second(
fn fmt_unix_timestamp(
output: &mut impl io::Write,
date: Date,
time: Time,
offset: UtcOffset,
modifier::UnixTimestamp {
precision,
sign_is_mandatory,
}: modifier::UnixTimestamp,
) -> Result<usize, io::Error> {
- let date_time = date
- .with_time(time)
- .assume_offset(offset)
- .to_offset(UtcOffset::UTC);
+ let date_time = OffsetDateTime::new_in_offset(date, time, offset).to_offset(UtcOffset::UTC);
if date_time < OffsetDateTime::UNIX_EPOCH {
write(output, b"-")?;
} else if sign_is_mandatory {
write(output, b"+")?;
}
match precision {
modifier::UnixTimestampPrecision::Second => {
format_number_pad_none(output, date_time.unix_timestamp().unsigned_abs())
}
modifier::UnixTimestampPrecision::Millisecond => format_number_pad_none(
output,
- (date_time.unix_timestamp_nanos() / Nanosecond.per(Millisecond) as i128).unsigned_abs(),
+ (date_time.unix_timestamp_nanos()
+ / Nanosecond::per(Millisecond).cast_signed().extend::<i128>())
+ .unsigned_abs(),
),
modifier::UnixTimestampPrecision::Microsecond => format_number_pad_none(
output,
- (date_time.unix_timestamp_nanos() / Nanosecond.per(Microsecond) as i128).unsigned_abs(),
+ (date_time.unix_timestamp_nanos()
+ / Nanosecond::per(Microsecond).cast_signed().extend::<i128>())
+ .unsigned_abs(),
),
modifier::UnixTimestampPrecision::Nanosecond => {
format_number_pad_none(output, date_time.unix_timestamp_nanos().unsigned_abs())
}
}
}
diff --git a/third_party/rust/time/src/instant.rs b/third_party/rust/time/src/instant.rs
index 58579b101c..4c2a16f6ef 100644
--- a/third_party/rust/time/src/instant.rs
+++ b/third_party/rust/time/src/instant.rs
@@ -1,74 +1,81 @@
//! The [`Instant`] struct and its associated `impl`s.
+#![allow(deprecated)]
+
use core::borrow::Borrow;
use core::cmp::{Ord, Ordering, PartialEq, PartialOrd};
use core::ops::{Add, Sub};
use core::time::Duration as StdDuration;
use std::time::Instant as StdInstant;
+use crate::internal_macros::{impl_add_assign, impl_sub_assign};
use crate::Duration;
/// A measurement of a monotonically non-decreasing clock. Opaque and useful only with [`Duration`].
///
/// Instants are always guaranteed to be no less than any previously measured instant when created,
/// and are often useful for tasks such as measuring benchmarks or timing how long an operation
/// takes.
///
/// Note, however, that instants are not guaranteed to be **steady**. In other words, each tick of
/// the underlying clock may not be the same length (e.g. some seconds may be longer than others).
/// An instant may jump forwards or experience time dilation (slow down or speed up), but it will
/// never go backwards.
///
/// Instants are opaque types that can only be compared to one another. There is no method to get
/// "the number of seconds" from an instant. Instead, it only allows measuring the duration between
/// two instants (or comparing two instants).
///
/// This implementation allows for operations with signed [`Duration`]s, but is otherwise identical
/// to [`std::time::Instant`].
+#[deprecated(since = "0.3.35", note = "import `time::ext::InstantExt` instead")]
#[repr(transparent)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Instant(pub StdInstant);
impl Instant {
// region: delegation
/// Returns an `Instant` corresponding to "now".
///
/// ```rust
+ /// # #![allow(deprecated)]
/// # use time::Instant;
/// println!("{:?}", Instant::now());
/// ```
pub fn now() -> Self {
Self(StdInstant::now())
}
/// Returns the amount of time elapsed since this instant was created. The duration will always
/// be nonnegative if the instant is not synthetically created.
///
/// ```rust
+ /// # #![allow(deprecated)]
/// # use time::{Instant, ext::{NumericalStdDuration, NumericalDuration}};
/// # use std::thread;
/// let instant = Instant::now();
/// thread::sleep(1.std_milliseconds());
/// assert!(instant.elapsed() >= 1.milliseconds());
/// ```
pub fn elapsed(self) -> Duration {
Self::now() - self
}
// endregion delegation
// region: checked arithmetic
/// Returns `Some(t)` where `t` is the time `self + duration` if `t` can be represented as
/// `Instant` (which means it's inside the bounds of the underlying data structure), `None`
/// otherwise.
///
/// ```rust
+ /// # #![allow(deprecated)]
/// # use time::{Instant, ext::NumericalDuration};
/// let now = Instant::now();
/// assert_eq!(now.checked_add(5.seconds()), Some(now + 5.seconds()));
/// assert_eq!(now.checked_add((-5).seconds()), Some(now + (-5).seconds()));
/// ```
pub fn checked_add(self, duration: Duration) -> Option<Self> {
if duration.is_zero() {
Some(self)
} else if duration.is_positive() {
self.0.checked_add(duration.unsigned_abs()).map(Self)
@@ -76,40 +83,42 @@ impl Instant {
debug_assert!(duration.is_negative());
self.0.checked_sub(duration.unsigned_abs()).map(Self)
}
}
/// Returns `Some(t)` where `t` is the time `self - duration` if `t` can be represented as
/// `Instant` (which means it's inside the bounds of the underlying data structure), `None`
/// otherwise.
///
/// ```rust
+ /// # #![allow(deprecated)]
/// # use time::{Instant, ext::NumericalDuration};
/// let now = Instant::now();
/// assert_eq!(now.checked_sub(5.seconds()), Some(now - 5.seconds()));
/// assert_eq!(now.checked_sub((-5).seconds()), Some(now - (-5).seconds()));
/// ```
pub fn checked_sub(self, duration: Duration) -> Option<Self> {
if duration.is_zero() {
Some(self)
} else if duration.is_positive() {
self.0.checked_sub(duration.unsigned_abs()).map(Self)
} else {
debug_assert!(duration.is_negative());
self.0.checked_add(duration.unsigned_abs()).map(Self)
}
}
// endregion checked arithmetic
/// Obtain the inner [`std::time::Instant`].
///
/// ```rust
+ /// # #![allow(deprecated)]
/// # use time::Instant;
/// let now = Instant::now();
/// assert_eq!(now.into_inner(), now.0);
/// ```
pub const fn into_inner(self) -> StdInstant {
self.0
}
}
// region: trait impls
@@ -121,20 +130,23 @@ impl From<StdInstant> for Instant {
impl From<Instant> for StdInstant {
fn from(instant: Instant) -> Self {
instant.0
}
}
impl Sub for Instant {
type Output = Duration;
+ /// # Panics
+ ///
+ /// This may panic if an overflow occurs.
fn sub(self, other: Self) -> Self::Output {
match self.0.cmp(&other.0) {
Ordering::Equal => Duration::ZERO,
Ordering::Greater => (self.0 - other.0)
.try_into()
.expect("overflow converting `std::time::Duration` to `time::Duration`"),
Ordering::Less => -Duration::try_from(other.0 - self.0)
.expect("overflow converting `std::time::Duration` to `time::Duration`"),
}
}
diff --git a/third_party/rust/time/src/internal_macros.rs b/third_party/rust/time/src/internal_macros.rs
new file mode 100644
index 0000000000..e210573ecc
--- /dev/null
+++ b/third_party/rust/time/src/internal_macros.rs
@@ -0,0 +1,200 @@
+//! Macros for use within the library. They are not publicly available.
+
+/// Helper macro for easily implementing `OpAssign`.
+macro_rules! __impl_assign {
+ ($sym:tt $op:ident $fn:ident $target:ty : $($(#[$attr:meta])* $t:ty),+) => {$(
+ #[allow(unused_qualifications)]
+ $(#[$attr])*
+ impl core::ops::$op<$t> for $target {
+ fn $fn(&mut self, rhs: $t) {
+ *self = *self $sym rhs;
+ }
+ }
+ )+};
+}
+
+/// Implement `AddAssign` for the provided types.
+macro_rules! impl_add_assign {
+ ($target:ty : $($(#[$attr:meta])* $t:ty),+ $(,)?) => {
+ $crate::internal_macros::__impl_assign!(
+ + AddAssign add_assign $target : $($(#[$attr])* $t),+
+ );
+ };
+}
+
+/// Implement `SubAssign` for the provided types.
+macro_rules! impl_sub_assign {
+ ($target:ty : $($(#[$attr:meta])* $t:ty),+ $(,)?) => {
+ $crate::internal_macros::__impl_assign!(
+ - SubAssign sub_assign $target : $($(#[$attr])* $t),+
+ );
+ };
+}
+
+/// Implement `MulAssign` for the provided types.
+macro_rules! impl_mul_assign {
+ ($target:ty : $($(#[$attr:meta])* $t:ty),+ $(,)?) => {
+ $crate::internal_macros::__impl_assign!(
+ * MulAssign mul_assign $target : $($(#[$attr])* $t),+
+ );
+ };
+}
+
+/// Implement `DivAssign` for the provided types.
+macro_rules! impl_div_assign {
+ ($target:ty : $($(#[$attr:meta])* $t:ty),+ $(,)?) => {
+ $crate::internal_macros::__impl_assign!(
+ / DivAssign div_assign $target : $($(#[$attr])* $t),+
+ );
+ };
+}
+
+/// Division of integers, rounding the resulting value towards negative infinity.
+macro_rules! div_floor {
+ ($a:expr, $b:expr) => {{
+ let _a = $a;
+ let _b = $b;
+
+ let (_quotient, _remainder) = (_a / _b, _a % _b);
+
+ if (_remainder > 0 && _b < 0) || (_remainder < 0 && _b > 0) {
+ _quotient - 1
+ } else {
+ _quotient
+ }
+ }};
+}
+
+/// Cascade an out-of-bounds value.
+macro_rules! cascade {
+ (@ordinal ordinal) => {};
+ (@year year) => {};
+
+ // Cascade an out-of-bounds value from "from" to "to".
+ ($from:ident in $min:literal.. $max:expr => $to:tt) => {
+ #[allow(unused_comparisons, unused_assignments)]
+ let min = $min;
+ let max = $max;
+ if $from >= max {
+ $from -= max - min;
+ $to += 1;
+ } else if $from < min {
+ $from += max - min;
+ $to -= 1;
+ }
+ };
+
+ // Special case the ordinal-to-year cascade, as it has different behavior.
+ ($ordinal:ident => $year:ident) => {
+ // We need to actually capture the idents. Without this, macro hygiene causes errors.
+ cascade!(@ordinal $ordinal);
+ cascade!(@year $year);
+ #[allow(unused_assignments)]
+ if $ordinal > crate::util::days_in_year($year) as i16 {
+ $ordinal -= crate::util::days_in_year($year) as i16;
+ $year += 1;
+ } else if $ordinal < 1 {
+ $year -= 1;
+ $ordinal += crate::util::days_in_year($year) as i16;
+ }
+ };
+}
+
+/// Constructs a ranged integer, returning a `ComponentRange` error if the value is out of range.
+macro_rules! ensure_ranged {
+ ($type:ident : $value:ident) => {
+ match $type::new($value) {
+ Some(val) => val,
+ None => {
+ #[allow(trivial_numeric_casts)]
+ return Err(crate::error::ComponentRange {
+ name: stringify!($value),
+ minimum: $type::MIN.get() as _,
+ maximum: $type::MAX.get() as _,
+ value: $value as _,
+ conditional_range: false,
+ });
+ }
+ }
+ };
+
+ ($type:ident : $value:ident $(as $as_type:ident)? * $factor:expr) => {
+ match ($value $(as $as_type)?).checked_mul($factor) {
+ Some(val) => match $type::new(val) {
+ Some(val) => val,
+ None => {
+ #[allow(trivial_numeric_casts)]
+ return Err(crate::error::ComponentRange {
+ name: stringify!($value),
+ minimum: $type::MIN.get() as i64 / $factor as i64,
+ maximum: $type::MAX.get() as i64 / $factor as i64,
+ value: $value as _,
+ conditional_range: false,
+ });
+ }
+ },
+ None => {
+ return Err(crate::error::ComponentRange {
+ name: stringify!($value),
+ minimum: $type::MIN.get() as i64 / $factor as i64,
+ maximum: $type::MAX.get() as i64 / $factor as i64,
+ value: $value as _,
+ conditional_range: false,
+ });
+ }
+ }
+ };
+}
+
+/// Try to unwrap an expression, returning if not possible.
+///
+/// This is similar to the `?` operator, but does not perform `.into()`. Because of this, it is
+/// usable in `const` contexts.
+macro_rules! const_try {
+ ($e:expr) => {
+ match $e {
+ Ok(value) => value,
+ Err(error) => return Err(error),
+ }
+ };
+}
+
+/// Try to unwrap an expression, returning if not possible.
+///
+/// This is similar to the `?` operator, but is usable in `const` contexts.
+macro_rules! const_try_opt {
+ ($e:expr) => {
+ match $e {
+ Some(value) => value,
+ None => return None,
+ }
+ };
+}
+
+/// Try to unwrap an expression, panicking if not possible.
+///
+/// This is similar to `$e.expect($message)`, but is usable in `const` contexts.
+macro_rules! expect_opt {
+ ($e:expr, $message:literal) => {
+ match $e {
+ Some(value) => value,
+ None => crate::expect_failed($message),
+ }
+ };
+}
+
+/// `unreachable!()`, but better.
+#[cfg(any(feature = "formatting", feature = "parsing"))]
+macro_rules! bug {
+ () => { compile_error!("provide an error message to help fix a possible bug") };
+ ($descr:literal $($rest:tt)?) => {
+ panic!(concat!("internal error: ", $descr) $($rest)?)
+ }
+}
+
+#[cfg(any(feature = "formatting", feature = "parsing"))]
+pub(crate) use bug;
+pub(crate) use {
+ __impl_assign, cascade, const_try, const_try_opt, div_floor, ensure_ranged, expect_opt,
+ impl_add_assign, impl_div_assign, impl_mul_assign, impl_sub_assign,
+};
diff --git a/third_party/rust/time/src/lib.rs b/third_party/rust/time/src/lib.rs
index 2ab59cf8ef..511df04deb 100644
--- a/third_party/rust/time/src/lib.rs
+++ b/third_party/rust/time/src/lib.rs
@@ -45,291 +45,59 @@
//!
//! - `serde-human-readable` (_implicitly enables `serde`, `formatting`, and `parsing`_)
//!
//! Allows serde representations to use a human-readable format. This is determined by the
//! serializer, not the user. If this feature is not enabled or if the serializer requests a
//! non-human-readable format, a format optimized for binary representation will be used.
//!
//! Libraries should never enable this feature, as the decision of what format to use should be up
//! to the user.
//!
-//! - `serde-well-known` (_implicitly enables `serde-human-readable`_)
-//!
-//! _This feature flag is deprecated and will be removed in a future breaking release. Use the
-//! `serde-human-readable` feature instead._
-//!
-//! Enables support for serializing and deserializing well-known formats using serde's
-//! [`#[with]` attribute](https://serde.rs/field-attrs.html#with).
-//!
//! - `rand`
//!
//! Enables [rand](https://docs.rs/rand) support for all types.
//!
//! - `quickcheck` (_implicitly enables `alloc`_)
//!
//! Enables [quickcheck](https://docs.rs/quickcheck) support for all types except [`Instant`].
//!
//! - `wasm-bindgen`
//!
//! Enables [wasm-bindgen](https://github.com/rustwasm/wasm-bindgen) support for converting
//! [JavaScript dates](https://rustwasm.github.io/wasm-bindgen/api/js_sys/struct.Date.html), as
//! well as obtaining the UTC offset from JavaScript.
#![doc(html_playground_url = "https://play.rust-lang.org")]
#![cfg_attr(__time_03_docs, feature(doc_auto_cfg, doc_notable_trait))]
-#![cfg_attr(coverage_nightly, feature(no_coverage))]
-#![cfg_attr(not(feature = "std"), no_std)]
-#![deny(
- anonymous_parameters,
- clippy::all,
- clippy::alloc_instead_of_core,
- clippy::explicit_auto_deref,
- clippy::obfuscated_if_else,
- clippy::std_instead_of_core,
- clippy::undocumented_unsafe_blocks,
- illegal_floating_point_literal_pattern,
- late_bound_lifetime_arguments,
- path_statements,
- patterns_in_fns_without_body,
- rust_2018_idioms,
- trivial_casts,
- trivial_numeric_casts,
- unreachable_pub,
- unsafe_op_in_unsafe_fn,
- unused_extern_crates,
- rustdoc::broken_intra_doc_links,
- rustdoc::private_intra_doc_links
-)]
-#![warn(
- clippy::dbg_macro,
- clippy::decimal_literal_representation,
- clippy::get_unwrap,
- clippy::missing_docs_in_private_items,
- clippy::nursery,
- clippy::print_stdout,
- clippy::todo,
- clippy::unimplemented,
- clippy::uninlined_format_args,
- clippy::unnested_or_patterns,
- clippy::unwrap_in_result,
- clippy::unwrap_used,
- clippy::use_debug,
- deprecated_in_future,
- missing_copy_implementations,
- missing_debug_implementations,
- unused_qualifications,
- variant_size_differences
-)]
-#![allow(
- clippy::redundant_pub_crate, // suggests bad style
- clippy::option_if_let_else, // suggests terrible code
- clippy::unused_peekable, // temporary due to bug: remove when Rust 1.66 is released
- clippy::std_instead_of_core, // temporary due to bug: remove when Rust 1.66 is released
-)]
+#![no_std]
#![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))))]
#[allow(unused_extern_crates)]
#[cfg(feature = "alloc")]
extern crate alloc;
-// TODO(jhpratt) remove this after a while
-#[cfg(unsound_local_offset)]
-compile_error!(
- "The `unsound_local_offset` flag was removed in time 0.3.18. If you need this functionality, \
- see the `time::util::local_offset::set_soundness` function."
-);
-
-// region: macros
-/// Helper macro for easily implementing `OpAssign`.
-macro_rules! __impl_assign {
- ($sym:tt $op:ident $fn:ident $target:ty : $($(#[$attr:meta])* $t:ty),+) => {$(
- #[allow(unused_qualifications)]
- $(#[$attr])*
- impl core::ops::$op<$t> for $target {
- fn $fn(&mut self, rhs: $t) {
- *self = *self $sym rhs;
- }
- }
- )+};
-}
-
-/// Implement `AddAssign` for the provided types.
-macro_rules! impl_add_assign {
- ($target:ty : $($(#[$attr:meta])* $t:ty),+ $(,)?) => {
- __impl_assign!(+ AddAssign add_assign $target : $($(#[$attr])* $t),+);
- };
-}
-
-/// Implement `SubAssign` for the provided types.
-macro_rules! impl_sub_assign {
- ($target:ty : $($(#[$attr:meta])* $t:ty),+ $(,)?) => {
- __impl_assign!(- SubAssign sub_assign $target : $($(#[$attr])* $t),+);
- };
-}
-
-/// Implement `MulAssign` for the provided types.
-macro_rules! impl_mul_assign {
- ($target:ty : $($(#[$attr:meta])* $t:ty),+ $(,)?) => {
- __impl_assign!(* MulAssign mul_assign $target : $($(#[$attr])* $t),+);
- };
-}
-
-/// Implement `DivAssign` for the provided types.
-macro_rules! impl_div_assign {
- ($target:ty : $($(#[$attr:meta])* $t:ty),+ $(,)?) => {
- __impl_assign!(/ DivAssign div_assign $target : $($(#[$attr])* $t),+);
- };
-}
-
-/// Division of integers, rounding the resulting value towards negative infinity.
-macro_rules! div_floor {
- ($a:expr, $b:expr) => {{
- let _a = $a;
- let _b = $b;
-
- let (_quotient, _remainder) = (_a / _b, _a % _b);
-
- if (_remainder > 0 && _b < 0) || (_remainder < 0 && _b > 0) {
- _quotient - 1
- } else {
- _quotient
- }
- }};
-}
-
-/// Cascade an out-of-bounds value.
-macro_rules! cascade {
- (@ordinal ordinal) => {};
- (@year year) => {};
-
- // Cascade an out-of-bounds value from "from" to "to".
- ($from:ident in $min:literal.. $max:expr => $to:tt) => {
- #[allow(unused_comparisons, unused_assignments)]
- let min = $min;
- let max = $max;
- if $from >= max {
- $from -= max - min;
- $to += 1;
- } else if $from < min {
- $from += max - min;
- $to -= 1;
- }
- };
-
- // Special case the ordinal-to-year cascade, as it has different behavior.
- ($ordinal:ident => $year:ident) => {
- // We need to actually capture the idents. Without this, macro hygiene causes errors.
- cascade!(@ordinal $ordinal);
- cascade!(@year $year);
- #[allow(unused_assignments)]
- if $ordinal > crate::util::days_in_year($year) as i16 {
- $ordinal -= crate::util::days_in_year($year) as i16;
- $year += 1;
- } else if $ordinal < 1 {
- $year -= 1;
- $ordinal += crate::util::days_in_year($year) as i16;
- }
- };
-}
-
-/// Returns `Err(error::ComponentRange)` if the value is not in range.
-macro_rules! ensure_value_in_range {
- ($value:ident in $start:expr => $end:expr) => {{
- let _start = $start;
- let _end = $end;
- #[allow(trivial_numeric_casts, unused_comparisons)]
- if $value < _start || $value > _end {
- return Err(crate::error::ComponentRange {
- name: stringify!($value),
- minimum: _start as _,
- maximum: _end as _,
- value: $value as _,
- conditional_range: false,
- });
- }
- }};
-
- ($value:ident conditionally in $start:expr => $end:expr) => {{
- let _start = $start;
- let _end = $end;
- #[allow(trivial_numeric_casts, unused_comparisons)]
- if $value < _start || $value > _end {
- return Err(crate::error::ComponentRange {
- name: stringify!($value),
- minimum: _start as _,
- maximum: _end as _,
- value: $value as _,
- conditional_range: true,
- });
- }
- }};
-}
-
-/// Try to unwrap an expression, returning if not possible.
-///
-/// This is similar to the `?` operator, but does not perform `.into()`. Because of this, it is
-/// usable in `const` contexts.
-macro_rules! const_try {
- ($e:expr) => {
- match $e {
- Ok(value) => value,
- Err(error) => return Err(error),
- }
- };
-}
-
-/// Try to unwrap an expression, returning if not possible.
-///
-/// This is similar to the `?` operator, but is usable in `const` contexts.
-macro_rules! const_try_opt {
- ($e:expr) => {
- match $e {
- Some(value) => value,
- None => return None,
- }
- };
-}
-
-/// Try to unwrap an expression, panicking if not possible.
-///
-/// This is similar to `$e.expect($message)`, but is usable in `const` contexts.
-macro_rules! expect_opt {
- ($e:expr, $message:literal) => {
- match $e {
- Some(value) => value,
- None => crate::expect_failed($message),
- }
- };
-}
-
-/// `unreachable!()`, but better.
-macro_rules! bug {
- () => { compile_error!("provide an error message to help fix a possible bug") };
- ($descr:literal $($rest:tt)?) => {
- panic!(concat!("internal error: ", $descr) $($rest)?)
- }
-}
-// endregion macros
+#[cfg(feature = "std")]
+extern crate std;
mod date;
-mod date_time;
mod duration;
pub mod error;
pub mod ext;
#[cfg(any(feature = "formatting", feature = "parsing"))]
pub mod format_description;
#[cfg(feature = "formatting")]
pub mod formatting;
#[cfg(feature = "std")]
mod instant;
+mod internal_macros;
#[cfg(feature = "macros")]
pub mod macros;
mod month;
mod offset_date_time;
#[cfg(feature = "parsing")]
pub mod parsing;
mod primitive_date_time;
#[cfg(feature = "quickcheck")]
mod quickcheck;
#[cfg(feature = "rand")]
@@ -338,28 +106,27 @@ mod rand;
#[allow(missing_copy_implementations, missing_debug_implementations)]
pub mod serde;
mod sys;
#[cfg(test)]
mod tests;
mod time;
mod utc_offset;
pub mod util;
mod weekday;
-// Not public yet.
-use time_core::convert;
+pub use time_core::convert;
pub use crate::date::Date;
-use crate::date_time::DateTime;
pub use crate::duration::Duration;
pub use crate::error::Error;
#[cfg(feature = "std")]
+#[allow(deprecated)]
pub use crate::instant::Instant;
pub use crate::month::Month;
pub use crate::offset_date_time::OffsetDateTime;
pub use crate::primitive_date_time::PrimitiveDateTime;
pub use crate::time::Time;
pub use crate::utc_offset::UtcOffset;
pub use crate::weekday::Weekday;
/// An alias for [`std::result::Result`] with a generic error from the time crate.
pub type Result<T> = core::result::Result<T, Error>;
diff --git a/third_party/rust/time/src/macros.rs b/third_party/rust/time/src/macros.rs
index 4f295e2ed8..d788645683 100644
--- a/third_party/rust/time/src/macros.rs
+++ b/third_party/rust/time/src/macros.rs
@@ -44,22 +44,22 @@ pub use time_macros::date;
/// assert_eq!(
/// datetime!(2020-01-01 0:00 -1),
/// Date::from_calendar_date(2020, Month::January, 1)?.midnight()
/// .assume_offset(UtcOffset::from_hms(-1, 0, 0)?)
/// );
/// # Ok::<_, time::Error>(())
/// ```
pub use time_macros::datetime;
/// Equivalent of performing [`format_description::parse()`] at compile time.
///
-/// Using the macro instead of the function results in a static slice rather than a [`Vec`],
-/// such that it can be used in `#![no_alloc]` situations.
+/// Using the macro instead of the function results in a static slice rather than a
+/// [`Vec`](alloc::vec::Vec), such that it can be used in `#![no_alloc]` situations.
///
/// The resulting expression can be used in `const` or `static` declarations, and implements
/// the sealed traits required for both formatting and parsing.
#[cfg_attr(feature = "alloc", doc = "```rust")]
#[cfg_attr(not(feature = "alloc"), doc = "```rust,ignore")]
/// # use time::{format_description, macros::format_description};
/// assert_eq!(
/// format_description!("[hour]:[minute]:[second]"),
/// format_description::parse("[hour]:[minute]:[second]")?
/// );
diff --git a/third_party/rust/time/src/month.rs b/third_party/rust/time/src/month.rs
index 8d8c164b21..55e53b4b8a 100644
--- a/third_party/rust/time/src/month.rs
+++ b/third_party/rust/time/src/month.rs
@@ -1,35 +1,48 @@
//! The `Month` enum and its associated `impl`s.
use core::fmt;
use core::num::NonZeroU8;
use core::str::FromStr;
+use powerfmt::smart_display::{FormatterOptions, Metadata, SmartDisplay};
+
use self::Month::*;
use crate::error;
/// Months of the year.
-#[allow(clippy::missing_docs_in_private_items)] // variants
#[repr(u8)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Month {
+ #[allow(missing_docs)]
January = 1,
+ #[allow(missing_docs)]
February = 2,
+ #[allow(missing_docs)]
March = 3,
+ #[allow(missing_docs)]
April = 4,
+ #[allow(missing_docs)]
May = 5,
+ #[allow(missing_docs)]
June = 6,
+ #[allow(missing_docs)]
July = 7,
+ #[allow(missing_docs)]
August = 8,
+ #[allow(missing_docs)]
September = 9,
+ #[allow(missing_docs)]
October = 10,
+ #[allow(missing_docs)]
November = 11,
+ #[allow(missing_docs)]
December = 12,
}
impl Month {
/// Create a `Month` from its numerical value.
pub(crate) const fn from_number(n: NonZeroU8) -> Result<Self, error::ComponentRange> {
match n.get() {
1 => Ok(January),
2 => Ok(February),
3 => Ok(March),
@@ -146,39 +159,71 @@ impl Month {
10 | -2 => November,
11 | -1 => December,
val => {
debug_assert!(val == 0);
January
}
}
}
}
-impl fmt::Display for Month {
+mod private {
+ #[non_exhaustive]
+ #[derive(Debug, Clone, Copy)]
+ pub struct MonthMetadata;
+}
+use private::MonthMetadata;
+
+impl SmartDisplay for Month {
+ type Metadata = MonthMetadata;
+
+ fn metadata(&self, _: FormatterOptions) -> Metadata<Self> {
+ match self {
+ January => Metadata::new(7, self, MonthMetadata),
+ February => Metadata::new(8, self, MonthMetadata),
+ March => Metadata::new(5, self, MonthMetadata),
+ April => Metadata::new(5, self, MonthMetadata),
+ May => Metadata::new(3, self, MonthMetadata),
+ June => Metadata::new(4, self, MonthMetadata),
+ July => Metadata::new(4, self, MonthMetadata),
+ August => Metadata::new(6, self, MonthMetadata),
+ September => Metadata::new(9, self, MonthMetadata),
+ October => Metadata::new(7, self, MonthMetadata),
+ November => Metadata::new(8, self, MonthMetadata),
+ December => Metadata::new(8, self, MonthMetadata),
+ }
+ }
+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- f.write_str(match self {
+ f.pad(match self {
January => "January",
February => "February",
March => "March",
April => "April",
May => "May",
June => "June",
July => "July",
August => "August",
September => "September",
October => "October",
November => "November",
December => "December",
})
}
}
+impl fmt::Display for Month {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ SmartDisplay::fmt(self, f)
+ }
+}
+
impl FromStr for Month {
type Err = error::InvalidVariant;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"January" => Ok(January),
"February" => Ok(February),
"March" => Ok(March),
"April" => Ok(April),
"May" => Ok(May),
diff --git a/third_party/rust/time/src/offset_date_time.rs b/third_party/rust/time/src/offset_date_time.rs
index d979174e1d..52f1799851 100644
--- a/third_party/rust/time/src/offset_date_time.rs
+++ b/third_party/rust/time/src/offset_date_time.rs
@@ -1,80 +1,175 @@
//! The [`OffsetDateTime`] struct and its associated `impl`s.
-#[cfg(feature = "std")]
+#[cfg(feature = "formatting")]
+use alloc::string::String;
use core::cmp::Ordering;
-#[cfg(feature = "std")]
-use core::convert::From;
use core::fmt;
use core::hash::Hash;
use core::ops::{Add, AddAssign, Sub, SubAssign};
use core::time::Duration as StdDuration;
#[cfg(feature = "formatting")]
use std::io;
#[cfg(feature = "std")]
use std::time::SystemTime;
-use crate::date_time::offset_kind;
+use deranged::RangedI64;
+use num_conv::prelude::*;
+use powerfmt::ext::FormatterExt as _;
+use powerfmt::smart_display::{self, FormatterOptions, Metadata, SmartDisplay};
+use time_core::convert::*;
+
+use crate::date::{MAX_YEAR, MIN_YEAR};
#[cfg(feature = "formatting")]
use crate::formatting::Formattable;
+use crate::internal_macros::{
+ cascade, const_try, const_try_opt, div_floor, ensure_ranged, expect_opt,
+};
#[cfg(feature = "parsing")]
use crate::parsing::Parsable;
-use crate::{error, Date, DateTime, Duration, Month, PrimitiveDateTime, Time, UtcOffset, Weekday};
+use crate::{error, util, Date, Duration, Month, PrimitiveDateTime, Time, UtcOffset, Weekday};
-/// The actual type doing all the work.
-type Inner = DateTime<offset_kind::Fixed>;
+/// The Julian day of the Unix epoch.
+// Safety: `ordinal` is not zero.
+#[allow(clippy::undocumented_unsafe_blocks)]
+const UNIX_EPOCH_JULIAN_DAY: i32 =
+ unsafe { Date::__from_ordinal_date_unchecked(1970, 1) }.to_julian_day();
/// A [`PrimitiveDateTime`] with a [`UtcOffset`].
///
/// All comparisons are performed using the UTC time.
-#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
-pub struct OffsetDateTime(pub(crate) Inner);
+#[derive(Clone, Copy, Eq)]
+pub struct OffsetDateTime {
+ local_date_time: PrimitiveDateTime,
+ offset: UtcOffset,
+}
+
+impl PartialEq for OffsetDateTime {
+ fn eq(&self, other: &Self) -> bool {
+ self.to_offset_raw(UtcOffset::UTC) == other.to_offset_raw(UtcOffset::UTC)
+ }
+}
+
+impl PartialOrd for OffsetDateTime {
+ fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
+ Some(self.cmp(other))
+ }
+}
+
+impl Ord for OffsetDateTime {
+ fn cmp(&self, other: &Self) -> Ordering {
+ self.to_offset_raw(UtcOffset::UTC)
+ .cmp(&other.to_offset_raw(UtcOffset::UTC))
+ }
+}
+
+impl Hash for OffsetDateTime {
+ fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
+ self.to_offset_raw(UtcOffset::UTC).hash(state);
+ }
+}
impl OffsetDateTime {
/// Midnight, 1 January, 1970 (UTC).
///
/// ```rust
/// # use time::OffsetDateTime;
/// # use time_macros::datetime;
/// assert_eq!(OffsetDateTime::UNIX_EPOCH, datetime!(1970-01-01 0:00 UTC),);
/// ```
- pub const UNIX_EPOCH: Self = Self(Inner::UNIX_EPOCH);
+ pub const UNIX_EPOCH: Self = Self::new_in_offset(
+ // Safety: `ordinal` is not zero.
+ unsafe { Date::__from_ordinal_date_unchecked(1970, 1) },
+ Time::MIDNIGHT,
+ UtcOffset::UTC,
+ );
// region: now
/// Create a new `OffsetDateTime` with the current date and time in UTC.
///
/// ```rust
/// # use time::OffsetDateTime;
/// # use time_macros::offset;
/// assert!(OffsetDateTime::now_utc().year() >= 2019);
/// assert_eq!(OffsetDateTime::now_utc().offset(), offset!(UTC));
/// ```
#[cfg(feature = "std")]
pub fn now_utc() -> Self {
- Self(Inner::now_utc())
+ #[cfg(all(
+ target_family = "wasm",
+ not(any(target_os = "emscripten", target_os = "wasi")),
+ feature = "wasm-bindgen"
+ ))]
+ {
+ js_sys::Date::new_0().into()
+ }
+
+ #[cfg(not(all(
+ target_family = "wasm",
+ not(any(target_os = "emscripten", target_os = "wasi")),
+ feature = "wasm-bindgen"
+ )))]
+ SystemTime::now().into()
}
/// Attempt to create a new `OffsetDateTime` with the current date and time in the local offset.
/// If the offset cannot be determined, an error is returned.
///
/// ```rust
/// # use time::OffsetDateTime;
/// # if false {
/// assert!(OffsetDateTime::now_local().is_ok());
/// # }
/// ```
#[cfg(feature = "local-offset")]
pub fn now_local() -> Result<Self, error::IndeterminateOffset> {
- Inner::now_local().map(Self)
+ let t = Self::now_utc();
+ Ok(t.to_offset(UtcOffset::local_offset_at(t)?))
}
// endregion now
+ /// Create a new `OffsetDateTime` with the given [`Date`], [`Time`], and [`UtcOffset`].
+ ///
+ /// ```
+ /// # use time::{Date, Month, OffsetDateTime, Time, UtcOffset};
+ /// # use time_macros::datetime;
+ /// let dt = OffsetDateTime::new_in_offset(
+ /// Date::from_calendar_date(2024, Month::January, 1)?,
+ /// Time::from_hms_nano(12, 59, 59, 500_000_000)?,
+ /// UtcOffset::from_hms(-5, 0, 0)?,
+ /// );
+ /// assert_eq!(dt, datetime!(2024-01-01 12:59:59.5 -5));
+ /// # Ok::<_, time::error::Error>(())
+ /// ```
+ pub const fn new_in_offset(date: Date, time: Time, offset: UtcOffset) -> Self {
+ Self {
+ local_date_time: date.with_time(time),
+ offset,
+ }
+ }
+
+ /// Create a new `OffsetDateTime` with the given [`Date`] and [`Time`] in the UTC timezone.
+ ///
+ /// ```
+ /// # use time::{Date, Month, OffsetDateTime, Time};
+ /// # use time_macros::datetime;
+ /// let dt = OffsetDateTime::new_utc(
+ /// Date::from_calendar_date(2024, Month::January, 1)?,
+ /// Time::from_hms_nano(12, 59, 59, 500_000_000)?,
+ /// );
+ /// assert_eq!(dt, datetime!(2024-01-01 12:59:59.5 UTC));
+ /// # Ok::<_, time::error::Error>(())
+ /// ```
+ pub const fn new_utc(date: Date, time: Time) -> Self {
+ PrimitiveDateTime::new(date, time).assume_utc()
+ }
+
/// Convert the `OffsetDateTime` from the current [`UtcOffset`] to the provided [`UtcOffset`].
///
/// ```rust
/// # use time_macros::{datetime, offset};
/// assert_eq!(
/// datetime!(2000-01-01 0:00 UTC)
/// .to_offset(offset!(-1))
/// .year(),
/// 1999,
/// );
@@ -87,21 +182,24 @@ impl OffsetDateTime {
/// let los_angeles = sydney.to_offset(offset!(-8));
/// assert_eq!(sydney.hour(), 0);
/// assert_eq!(new_york.hour(), 8);
/// assert_eq!(los_angeles.hour(), 5);
/// ```
///
/// # Panics
///
/// This method panics if the local date-time in the new offset is outside the supported range.
pub const fn to_offset(self, offset: UtcOffset) -> Self {
- Self(self.0.to_offset(offset))
+ expect_opt!(
+ self.checked_to_offset(offset),
+ "local datetime out of valid range"
+ )
}
/// Convert the `OffsetDateTime` from the current [`UtcOffset`] to the provided [`UtcOffset`],
/// returning `None` if the date-time in the resulting offset is invalid.
///
/// ```rust
/// # use time::PrimitiveDateTime;
/// # use time_macros::{datetime, offset};
/// assert_eq!(
/// datetime!(2000-01-01 0:00 UTC)
@@ -111,21 +209,88 @@ impl OffsetDateTime {
/// 1999,
/// );
/// assert_eq!(
/// PrimitiveDateTime::MAX
/// .assume_utc()
/// .checked_to_offset(offset!(+1)),
/// None,
/// );
/// ```
pub const fn checked_to_offset(self, offset: UtcOffset) -> Option<Self> {
- Some(Self(const_try_opt!(self.0.checked_to_offset(offset))))
+ if self.offset.whole_hours() == offset.whole_hours()
+ && self.offset.minutes_past_hour() == offset.minutes_past_hour()
+ && self.offset.seconds_past_minute() == offset.seconds_past_minute()
+ {
+ return Some(self.replace_offset(offset));
+ }
+
+ let (year, ordinal, time) = self.to_offset_raw(offset);
+
+ if year > MAX_YEAR || year < MIN_YEAR {
+ return None;
+ }
+
+ Some(Self::new_in_offset(
+ // Safety: `ordinal` is not zero.
+ unsafe { Date::__from_ordinal_date_unchecked(year, ordinal) },
+ time,
+ offset,
+ ))
+ }
+
+ /// Equivalent to `.to_offset(UtcOffset::UTC)`, but returning the year, ordinal, and time. This
+ /// avoids constructing an invalid [`Date`] if the new value is out of range.
+ pub(crate) const fn to_offset_raw(self, offset: UtcOffset) -> (i32, u16, Time) {
+ let from = self.offset;
+ let to = offset;
+
+ // Fast path for when no conversion is necessary.
+ if from.whole_hours() == to.whole_hours()
+ && from.minutes_past_hour() == to.minutes_past_hour()
+ && from.seconds_past_minute() == to.seconds_past_minute()
+ {
+ return (self.year(), self.ordinal(), self.time());
+ }
+
+ let mut second = self.second() as i16 - from.seconds_past_minute() as i16
+ + to.seconds_past_minute() as i16;
+ let mut minute =
+ self.minute() as i16 - from.minutes_past_hour() as i16 + to.minutes_past_hour() as i16;
+ let mut hour = self.hour() as i8 - from.whole_hours() + to.whole_hours();
+ let (mut year, ordinal) = self.to_ordinal_date();
+ let mut ordinal = ordinal as i16;
+
+ // Cascade the values twice. This is needed because the values are adjusted twice above.
+ cascade!(second in 0..Second::per(Minute) as i16 => minute);
+ cascade!(second in 0..Second::per(Minute) as i16 => minute);
+ cascade!(minute in 0..Minute::per(Hour) as i16 => hour);
+ cascade!(minute in 0..Minute::per(Hour) as i16 => hour);
+ cascade!(hour in 0..Hour::per(Day) as i8 => ordinal);
+ cascade!(hour in 0..Hour::per(Day) as i8 => ordinal);
+ cascade!(ordinal => year);
+
+ debug_assert!(ordinal > 0);
+ debug_assert!(ordinal <= util::days_in_year(year) as i16);
+
+ (
+ year,
+ ordinal as _,
+ // Safety: The cascades above ensure the values are in range.
+ unsafe {
+ Time::__from_hms_nanos_unchecked(
+ hour as _,
+ minute as _,
+ second as _,
+ self.nanosecond(),
+ )
+ },
+ )
}
// region: constructors
/// Create an `OffsetDateTime` from the provided Unix timestamp. Calling `.offset()` on the
/// resulting value is guaranteed to return UTC.
///
/// ```rust
/// # use time::OffsetDateTime;
/// # use time_macros::datetime;
/// assert_eq!(
@@ -144,255 +309,308 @@ impl OffsetDateTime {
/// ```rust
/// # use time::{Duration, OffsetDateTime, ext::NumericalDuration};
/// let (timestamp, nanos) = (1, 500_000_000);
/// assert_eq!(
/// OffsetDateTime::from_unix_timestamp(timestamp)? + Duration::nanoseconds(nanos),
/// OffsetDateTime::UNIX_EPOCH + 1.5.seconds()
/// );
/// # Ok::<_, time::Error>(())
/// ```
pub const fn from_unix_timestamp(timestamp: i64) -> Result<Self, error::ComponentRange> {
- Ok(Self(const_try!(Inner::from_unix_timestamp(timestamp))))
+ type Timestamp = RangedI64<
+ {
+ OffsetDateTime::new_in_offset(Date::MIN, Time::MIDNIGHT, UtcOffset::UTC)
+ .unix_timestamp()
+ },
+ {
+ OffsetDateTime::new_in_offset(Date::MAX, Time::MAX, UtcOffset::UTC).unix_timestamp()
+ },
+ >;
+ ensure_ranged!(Timestamp: timestamp);
+
+ // Use the unchecked method here, as the input validity has already been verified.
+ let date = Date::from_julian_day_unchecked(
+ UNIX_EPOCH_JULIAN_DAY + div_floor!(timestamp, Second::per(Day) as i64) as i32,
+ );
+
+ let seconds_within_day = timestamp.rem_euclid(Second::per(Day) as _);
+ // Safety: All values are in range.
+ let time = unsafe {
+ Time::__from_hms_nanos_unchecked(
+ (seconds_within_day / Second::per(Hour) as i64) as _,
+ ((seconds_within_day % Second::per(Hour) as i64) / Minute::per(Hour) as i64) as _,
+ (seconds_within_day % Second::per(Minute) as i64) as _,
+ 0,
+ )
+ };
+
+ Ok(Self::new_in_offset(date, time, UtcOffset::UTC))
}
/// Construct an `OffsetDateTime` from the provided Unix timestamp (in nanoseconds). Calling
/// `.offset()` on the resulting value is guaranteed to return UTC.
///
/// ```rust
/// # use time::OffsetDateTime;
/// # use time_macros::datetime;
/// assert_eq!(
/// OffsetDateTime::from_unix_timestamp_nanos(0),
/// Ok(OffsetDateTime::UNIX_EPOCH),
/// );
/// assert_eq!(
/// OffsetDateTime::from_unix_timestamp_nanos(1_546_300_800_000_000_000),
/// Ok(datetime!(2019-01-01 0:00 UTC)),
/// );
/// ```
pub const fn from_unix_timestamp_nanos(timestamp: i128) -> Result<Self, error::ComponentRange> {
- Ok(Self(const_try!(Inner::from_unix_timestamp_nanos(
- timestamp
- ))))
+ let datetime = const_try!(Self::from_unix_timestamp(div_floor!(
+ timestamp,
+ Nanosecond::per(Second) as i128
+ ) as i64));
+
+ Ok(Self::new_in_offset(
+ datetime.date(),
+ // Safety: `nanosecond` is in range due to `rem_euclid`.
+ unsafe {
+ Time::__from_hms_nanos_unchecked(
+ datetime.hour(),
+ datetime.minute(),
+ datetime.second(),
+ timestamp.rem_euclid(Nanosecond::per(Second) as _) as u32,
+ )
+ },
+ UtcOffset::UTC,
+ ))
}
// endregion constructors
// region: getters
/// Get the [`UtcOffset`].
///
/// ```rust
/// # use time_macros::{datetime, offset};
/// assert_eq!(datetime!(2019-01-01 0:00 UTC).offset(), offset!(UTC));
/// assert_eq!(datetime!(2019-01-01 0:00 +1).offset(), offset!(+1));
/// ```
pub const fn offset(self) -> UtcOffset {
- self.0.offset()
+ self.offset
}
/// Get the [Unix timestamp](https://en.wikipedia.org/wiki/Unix_time).
///
/// ```rust
/// # use time_macros::datetime;
/// assert_eq!(datetime!(1970-01-01 0:00 UTC).unix_timestamp(), 0);
/// assert_eq!(datetime!(1970-01-01 0:00 -1).unix_timestamp(), 3_600);
/// ```
pub const fn unix_timestamp(self) -> i64 {
- self.0.unix_timestamp()
+ let days =
+ (self.to_julian_day() as i64 - UNIX_EPOCH_JULIAN_DAY as i64) * Second::per(Day) as i64;
+ let hours = self.hour() as i64 * Second::per(Hour) as i64;
+ let minutes = self.minute() as i64 * Second::per(Minute) as i64;
+ let seconds = self.second() as i64;
+ let offset_seconds = self.offset.whole_seconds() as i64;
+ days + hours + minutes + seconds - offset_seconds
}
/// Get the Unix timestamp in nanoseconds.
///
/// ```rust
/// use time_macros::datetime;
/// assert_eq!(datetime!(1970-01-01 0:00 UTC).unix_timestamp_nanos(), 0);
/// assert_eq!(
/// datetime!(1970-01-01 0:00 -1).unix_timestamp_nanos(),
/// 3_600_000_000_000,
/// );
/// ```
pub const fn unix_timestamp_nanos(self) -> i128 {
- self.0.unix_timestamp_nanos()
+ self.unix_timestamp() as i128 * Nanosecond::per(Second) as i128 + self.nanosecond() as i128
+ }
+
+ /// Get the [`PrimitiveDateTime`] in the stored offset.
+ const fn date_time(self) -> PrimitiveDateTime {
+ self.local_date_time
}
/// Get the [`Date`] in the stored offset.
///
/// ```rust
/// # use time_macros::{date, datetime, offset};
/// assert_eq!(datetime!(2019-01-01 0:00 UTC).date(), date!(2019-01-01));
/// assert_eq!(
/// datetime!(2019-01-01 0:00 UTC)
/// .to_offset(offset!(-1))
/// .date(),
/// date!(2018-12-31),
/// );
/// ```
pub const fn date(self) -> Date {
- self.0.date()
+ self.date_time().date()
}
/// Get the [`Time`] in the stored offset.
///
/// ```rust
/// # use time_macros::{datetime, offset, time};
/// assert_eq!(datetime!(2019-01-01 0:00 UTC).time(), time!(0:00));
/// assert_eq!(
/// datetime!(2019-01-01 0:00 UTC)
/// .to_offset(offset!(-1))
/// .time(),
/// time!(23:00)
/// );
/// ```
pub const fn time(self) -> Time {
- self.0.time()
+ self.date_time().time()
}
// region: date getters
/// Get the year of the date in the stored offset.
///
/// ```rust
/// # use time_macros::{datetime, offset};
/// assert_eq!(datetime!(2019-01-01 0:00 UTC).year(), 2019);
/// assert_eq!(
/// datetime!(2019-12-31 23:00 UTC)
/// .to_offset(offset!(+1))
/// .year(),
/// 2020,
/// );
/// assert_eq!(datetime!(2020-01-01 0:00 UTC).year(), 2020);
/// ```
pub const fn year(self) -> i32 {
- self.0.year()
+ self.date().year()
}
/// Get the month of the date in the stored offset.
///
/// ```rust
/// # use time::Month;
/// # use time_macros::{datetime, offset};
/// assert_eq!(datetime!(2019-01-01 0:00 UTC).month(), Month::January);
/// assert_eq!(
/// datetime!(2019-12-31 23:00 UTC)
/// .to_offset(offset!(+1))
/// .month(),
/// Month::January,
/// );
/// ```
pub const fn month(self) -> Month {
- self.0.month()
+ self.date().month()
}
/// Get the day of the date in the stored offset.
///
/// The returned value will always be in the range `1..=31`.
///
/// ```rust
/// # use time_macros::{datetime, offset};
/// assert_eq!(datetime!(2019-01-01 0:00 UTC).day(), 1);
/// assert_eq!(
/// datetime!(2019-12-31 23:00 UTC)
/// .to_offset(offset!(+1))
/// .day(),
/// 1,
/// );
/// ```
pub const fn day(self) -> u8 {
- self.0.day()
+ self.date().day()
}
/// Get the day of the year of the date in the stored offset.
///
/// The returned value will always be in the range `1..=366`.
///
/// ```rust
/// # use time_macros::{datetime, offset};
/// assert_eq!(datetime!(2019-01-01 0:00 UTC).ordinal(), 1);
/// assert_eq!(
/// datetime!(2019-12-31 23:00 UTC)
/// .to_offset(offset!(+1))
/// .ordinal(),
/// 1,
/// );
/// ```
pub const fn ordinal(self) -> u16 {
- self.0.ordinal()
+ self.date().ordinal()
}
/// Get the ISO week number of the date in the stored offset.
///
/// The returned value will always be in the range `1..=53`.
///
/// ```rust
/// # use time_macros::datetime;
/// assert_eq!(datetime!(2019-01-01 0:00 UTC).iso_week(), 1);
/// assert_eq!(datetime!(2020-01-01 0:00 UTC).iso_week(), 1);
/// assert_eq!(datetime!(2020-12-31 0:00 UTC).iso_week(), 53);
/// assert_eq!(datetime!(2021-01-01 0:00 UTC).iso_week(), 53);
/// ```
pub const fn iso_week(self) -> u8 {
- self.0.iso_week()
+ self.date().iso_week()
}
/// Get the week number where week 1 begins on the first Sunday.
///
/// The returned value will always be in the range `0..=53`.
///
/// ```rust
/// # use time_macros::datetime;
/// assert_eq!(datetime!(2019-01-01 0:00 UTC).sunday_based_week(), 0);
/// assert_eq!(datetime!(2020-01-01 0:00 UTC).sunday_based_week(), 0);
/// assert_eq!(datetime!(2020-12-31 0:00 UTC).sunday_based_week(), 52);
/// assert_eq!(datetime!(2021-01-01 0:00 UTC).sunday_based_week(), 0);
/// ```
pub const fn sunday_based_week(self) -> u8 {
- self.0.sunday_based_week()
+ self.date().sunday_based_week()
}
/// Get the week number where week 1 begins on the first Monday.
///
/// The returned value will always be in the range `0..=53`.
///
/// ```rust
/// # use time_macros::datetime;
/// assert_eq!(datetime!(2019-01-01 0:00 UTC).monday_based_week(), 0);
/// assert_eq!(datetime!(2020-01-01 0:00 UTC).monday_based_week(), 0);
/// assert_eq!(datetime!(2020-12-31 0:00 UTC).monday_based_week(), 52);
/// assert_eq!(datetime!(2021-01-01 0:00 UTC).monday_based_week(), 0);
/// ```
pub const fn monday_based_week(self) -> u8 {
- self.0.monday_based_week()
+ self.date().monday_based_week()
}
/// Get the year, month, and day.
///
/// ```rust
/// # use time::Month;
/// # use time_macros::datetime;
/// assert_eq!(
/// datetime!(2019-01-01 0:00 UTC).to_calendar_date(),
/// (2019, Month::January, 1)
/// );
/// ```
pub const fn to_calendar_date(self) -> (i32, Month, u8) {
- self.0.to_calendar_date()
+ self.date().to_calendar_date()
}
/// Get the year and ordinal day number.
///
/// ```rust
/// # use time_macros::datetime;
/// assert_eq!(
/// datetime!(2019-01-01 0:00 UTC).to_ordinal_date(),
/// (2019, 1)
/// );
/// ```
pub const fn to_ordinal_date(self) -> (i32, u16) {
- self.0.to_ordinal_date()
+ self.date().to_ordinal_date()
}
/// Get the ISO 8601 year, week number, and weekday.
///
/// ```rust
/// # use time::Weekday::*;
/// # use time_macros::datetime;
/// assert_eq!(
/// datetime!(2019-01-01 0:00 UTC).to_iso_week_date(),
/// (2019, 1, Tuesday)
@@ -408,216 +626,216 @@ impl OffsetDateTime {
/// assert_eq!(
/// datetime!(2020-12-31 0:00 UTC).to_iso_week_date(),
/// (2020, 53, Thursday)
/// );
/// assert_eq!(
/// datetime!(2021-01-01 0:00 UTC).to_iso_week_date(),
/// (2020, 53, Friday)
/// );
/// ```
pub const fn to_iso_week_date(self) -> (i32, u8, Weekday) {
- self.0.to_iso_week_date()
+ self.date().to_iso_week_date()
}
/// Get the weekday of the date in the stored offset.
///
/// ```rust
/// # use time::Weekday::*;
/// # use time_macros::datetime;
/// assert_eq!(datetime!(2019-01-01 0:00 UTC).weekday(), Tuesday);
/// assert_eq!(datetime!(2019-02-01 0:00 UTC).weekday(), Friday);
/// assert_eq!(datetime!(2019-03-01 0:00 UTC).weekday(), Friday);
/// ```
pub const fn weekday(self) -> Weekday {
- self.0.weekday()
+ self.date().weekday()
}
/// Get the Julian day for the date. The time is not taken into account for this calculation.
///
/// The algorithm to perform this conversion is derived from one provided by Peter Baum; it is
/// freely available [here](https://www.researchgate.net/publication/316558298_Date_Algorithms).
///
/// ```rust
/// # use time_macros::datetime;
/// assert_eq!(datetime!(-4713-11-24 0:00 UTC).to_julian_day(), 0);
/// assert_eq!(datetime!(2000-01-01 0:00 UTC).to_julian_day(), 2_451_545);
/// assert_eq!(datetime!(2019-01-01 0:00 UTC).to_julian_day(), 2_458_485);
/// assert_eq!(datetime!(2019-12-31 0:00 UTC).to_julian_day(), 2_458_849);
/// ```
pub const fn to_julian_day(self) -> i32 {
- self.0.to_julian_day()
+ self.date().to_julian_day()
}
// endregion date getters
// region: time getters
/// Get the clock hour, minute, and second.
///
/// ```rust
/// # use time_macros::datetime;
/// assert_eq!(datetime!(2020-01-01 0:00:00 UTC).to_hms(), (0, 0, 0));
/// assert_eq!(datetime!(2020-01-01 23:59:59 UTC).to_hms(), (23, 59, 59));
/// ```
pub const fn to_hms(self) -> (u8, u8, u8) {
- self.0.as_hms()
+ self.time().as_hms()
}
/// Get the clock hour, minute, second, and millisecond.
///
/// ```rust
/// # use time_macros::datetime;
/// assert_eq!(
/// datetime!(2020-01-01 0:00:00 UTC).to_hms_milli(),
/// (0, 0, 0, 0)
/// );
/// assert_eq!(
/// datetime!(2020-01-01 23:59:59.999 UTC).to_hms_milli(),
/// (23, 59, 59, 999)
/// );
/// ```
pub const fn to_hms_milli(self) -> (u8, u8, u8, u16) {
- self.0.as_hms_milli()
+ self.time().as_hms_milli()
}
/// Get the clock hour, minute, second, and microsecond.
///
/// ```rust
/// # use time_macros::datetime;
/// assert_eq!(
/// datetime!(2020-01-01 0:00:00 UTC).to_hms_micro(),
/// (0, 0, 0, 0)
/// );
/// assert_eq!(
/// datetime!(2020-01-01 23:59:59.999_999 UTC).to_hms_micro(),
/// (23, 59, 59, 999_999)
/// );
/// ```
pub const fn to_hms_micro(self) -> (u8, u8, u8, u32) {
- self.0.as_hms_micro()
+ self.time().as_hms_micro()
}
/// Get the clock hour, minute, second, and nanosecond.
///
/// ```rust
/// # use time_macros::datetime;
/// assert_eq!(
/// datetime!(2020-01-01 0:00:00 UTC).to_hms_nano(),
/// (0, 0, 0, 0)
/// );
/// assert_eq!(
/// datetime!(2020-01-01 23:59:59.999_999_999 UTC).to_hms_nano(),
/// (23, 59, 59, 999_999_999)
/// );
/// ```
pub const fn to_hms_nano(self) -> (u8, u8, u8, u32) {
- self.0.as_hms_nano()
+ self.time().as_hms_nano()
}
/// Get the clock hour in the stored offset.
///
/// The returned value will always be in the range `0..24`.
///
/// ```rust
/// # use time_macros::{datetime, offset};
/// assert_eq!(datetime!(2019-01-01 0:00 UTC).hour(), 0);
/// assert_eq!(
/// datetime!(2019-01-01 23:59:59 UTC)
/// .to_offset(offset!(-2))
/// .hour(),
/// 21,
/// );
/// ```
pub const fn hour(self) -> u8 {
- self.0.hour()
+ self.time().hour()
}
/// Get the minute within the hour in the stored offset.
///
/// The returned value will always be in the range `0..60`.
///
/// ```rust
/// # use time_macros::{datetime, offset};
/// assert_eq!(datetime!(2019-01-01 0:00 UTC).minute(), 0);
/// assert_eq!(
/// datetime!(2019-01-01 23:59:59 UTC)
/// .to_offset(offset!(+0:30))
/// .minute(),
/// 29,
/// );
/// ```
pub const fn minute(self) -> u8 {
- self.0.minute()
+ self.time().minute()
}
/// Get the second within the minute in the stored offset.
///
/// The returned value will always be in the range `0..60`.
///
/// ```rust
/// # use time_macros::{datetime, offset};
/// assert_eq!(datetime!(2019-01-01 0:00 UTC).second(), 0);
/// assert_eq!(
/// datetime!(2019-01-01 23:59:59 UTC)
/// .to_offset(offset!(+0:00:30))
/// .second(),
/// 29,
/// );
/// ```
pub const fn second(self) -> u8 {
- self.0.second()
+ self.time().second()
}
// Because a `UtcOffset` is limited in resolution to one second, any subsecond value will not
// change when adjusting for the offset.
/// Get the milliseconds within the second in the stored offset.
///
/// The returned value will always be in the range `0..1_000`.
///
/// ```rust
/// # use time_macros::datetime;
/// assert_eq!(datetime!(2019-01-01 0:00 UTC).millisecond(), 0);
/// assert_eq!(datetime!(2019-01-01 23:59:59.999 UTC).millisecond(), 999);
/// ```
pub const fn millisecond(self) -> u16 {
- self.0.millisecond()
+ self.time().millisecond()
}
/// Get the microseconds within the second in the stored offset.
///
/// The returned value will always be in the range `0..1_000_000`.
///
/// ```rust
/// # use time_macros::datetime;
/// assert_eq!(datetime!(2019-01-01 0:00 UTC).microsecond(), 0);
/// assert_eq!(
/// datetime!(2019-01-01 23:59:59.999_999 UTC).microsecond(),
/// 999_999,
/// );
/// ```
pub const fn microsecond(self) -> u32 {
- self.0.microsecond()
+ self.time().microsecond()
}
/// Get the nanoseconds within the second in the stored offset.
///
/// The returned value will always be in the range `0..1_000_000_000`.
///
/// ```rust
/// # use time_macros::datetime;
/// assert_eq!(datetime!(2019-01-01 0:00 UTC).nanosecond(), 0);
/// assert_eq!(
/// datetime!(2019-01-01 23:59:59.999_999_999 UTC).nanosecond(),
/// 999_999_999,
/// );
/// ```
pub const fn nanosecond(self) -> u32 {
- self.0.nanosecond()
+ self.time().nanosecond()
}
// endregion time getters
// endregion getters
// region: checked arithmetic
/// Computes `self + duration`, returning `None` if an overflow occurred.
///
/// ```
/// # use time::{Date, ext::NumericalDuration};
/// # use time_macros::{datetime, offset};
@@ -626,41 +844,41 @@ impl OffsetDateTime {
///
/// let datetime = Date::MAX.midnight().assume_offset(offset!(+10));
/// assert_eq!(datetime.checked_add(2.days()), None);
///
/// assert_eq!(
/// datetime!(2019 - 11 - 25 15:30 +10).checked_add(27.hours()),
/// Some(datetime!(2019 - 11 - 26 18:30 +10))
/// );
/// ```
pub const fn checked_add(self, duration: Duration) -> Option<Self> {
- Some(Self(const_try_opt!(self.0.checked_add(duration))))
+ Some(const_try_opt!(self.date_time().checked_add(duration)).assume_offset(self.offset()))
}
/// Computes `self - duration`, returning `None` if an overflow occurred.
///
/// ```
/// # use time::{Date, ext::NumericalDuration};
/// # use time_macros::{datetime, offset};
/// let datetime = Date::MIN.midnight().assume_offset(offset!(+10));
/// assert_eq!(datetime.checked_sub(2.days()), None);
///
/// let datetime = Date::MAX.midnight().assume_offset(offset!(+10));
/// assert_eq!(datetime.checked_sub((-2).days()), None);
///
/// assert_eq!(
/// datetime!(2019 - 11 - 25 15:30 +10).checked_sub(27.hours()),
/// Some(datetime!(2019 - 11 - 24 12:30 +10))
/// );
/// ```
pub const fn checked_sub(self, duration: Duration) -> Option<Self> {
- Some(Self(const_try_opt!(self.0.checked_sub(duration))))
+ Some(const_try_opt!(self.date_time().checked_sub(duration)).assume_offset(self.offset()))
}
// endregion: checked arithmetic
// region: saturating arithmetic
/// Computes `self + duration`, saturating value on overflow.
///
/// ```
/// # use time::ext::NumericalDuration;
/// # use time_macros::datetime;
/// assert_eq!(
@@ -697,21 +915,27 @@ impl OffsetDateTime {
doc = " datetime!(+9999-12-31 23:59:59.999_999_999 +10)"
)]
/// );
///
/// assert_eq!(
/// datetime!(2019 - 11 - 25 15:30 +10).saturating_add(27.hours()),
/// datetime!(2019 - 11 - 26 18:30 +10)
/// );
/// ```
pub const fn saturating_add(self, duration: Duration) -> Self {
- Self(self.0.saturating_add(duration))
+ if let Some(datetime) = self.checked_add(duration) {
+ datetime
+ } else if duration.is_negative() {
+ PrimitiveDateTime::MIN.assume_offset(self.offset())
+ } else {
+ PrimitiveDateTime::MAX.assume_offset(self.offset())
+ }
}
/// Computes `self - duration`, saturating value on overflow.
///
/// ```
/// # use time::ext::NumericalDuration;
/// # use time_macros::datetime;
/// assert_eq!(
#[cfg_attr(
feature = "large-dates",
@@ -746,21 +970,27 @@ impl OffsetDateTime {
doc = " datetime!(+9999-12-31 23:59:59.999_999_999 +10)"
)]
/// );
///
/// assert_eq!(
/// datetime!(2019 - 11 - 25 15:30 +10).saturating_sub(27.hours()),
/// datetime!(2019 - 11 - 24 12:30 +10)
/// );
/// ```
pub const fn saturating_sub(self, duration: Duration) -> Self {
- Self(self.0.saturating_sub(duration))
+ if let Some(datetime) = self.checked_sub(duration) {
+ datetime
+ } else if duration.is_negative() {
+ PrimitiveDateTime::MAX.assume_offset(self.offset())
+ } else {
+ PrimitiveDateTime::MIN.assume_offset(self.offset())
+ }
}
// endregion: saturating arithmetic
}
// region: replacement
/// Methods that replace part of the `OffsetDateTime`.
impl OffsetDateTime {
/// Replace the time, which is assumed to be in the stored offset. The date and offset
/// components are unchanged.
///
@@ -774,243 +1004,279 @@ impl OffsetDateTime {
/// datetime!(2020-01-01 12:00 -5).replace_time(time!(7:00)),
/// datetime!(2020-01-01 7:00 -5)
/// );
/// assert_eq!(
/// datetime!(2020-01-01 0:00 +1).replace_time(time!(12:00)),
/// datetime!(2020-01-01 12:00 +1)
/// );
/// ```
#[must_use = "This method does not mutate the original `OffsetDateTime`."]
pub const fn replace_time(self, time: Time) -> Self {
- Self(self.0.replace_time(time))
+ Self::new_in_offset(self.date(), time, self.offset())
}
/// Replace the date, which is assumed to be in the stored offset. The time and offset
/// components are unchanged.
///
/// ```rust
/// # use time_macros::{datetime, date};
/// assert_eq!(
/// datetime!(2020-01-01 12:00 UTC).replace_date(date!(2020-01-30)),
/// datetime!(2020-01-30 12:00 UTC)
/// );
/// assert_eq!(
/// datetime!(2020-01-01 0:00 +1).replace_date(date!(2020-01-30)),
/// datetime!(2020-01-30 0:00 +1)
/// );
/// ```
#[must_use = "This method does not mutate the original `OffsetDateTime`."]
pub const fn replace_date(self, date: Date) -> Self {
- Self(self.0.replace_date(date))
+ Self::new_in_offset(date, self.time(), self.offset())
}
/// Replace the date and time, which are assumed to be in the stored offset. The offset
/// component remains unchanged.
///
/// ```rust
/// # use time_macros::datetime;
/// assert_eq!(
/// datetime!(2020-01-01 12:00 UTC).replace_date_time(datetime!(2020-01-30 16:00)),
/// datetime!(2020-01-30 16:00 UTC)
/// );
/// assert_eq!(
/// datetime!(2020-01-01 12:00 +1).replace_date_time(datetime!(2020-01-30 0:00)),
/// datetime!(2020-01-30 0:00 +1)
/// );
/// ```
#[must_use = "This method does not mutate the original `OffsetDateTime`."]
pub const fn replace_date_time(self, date_time: PrimitiveDateTime) -> Self {
- Self(self.0.replace_date_time(date_time.0))
+ date_time.assume_offset(self.offset())
}
/// Replace the offset. The date and time components remain unchanged.
///
/// ```rust
/// # use time_macros::{datetime, offset};
/// assert_eq!(
/// datetime!(2020-01-01 0:00 UTC).replace_offset(offset!(-5)),
/// datetime!(2020-01-01 0:00 -5)
/// );
/// ```
#[must_use = "This method does not mutate the original `OffsetDateTime`."]
pub const fn replace_offset(self, offset: UtcOffset) -> Self {
- Self(self.0.replace_offset(offset))
+ self.date_time().assume_offset(offset)
}
/// Replace the year. The month and day will be unchanged.
///
/// ```rust
/// # use time_macros::datetime;
/// assert_eq!(
/// datetime!(2022 - 02 - 18 12:00 +01).replace_year(2019),
/// Ok(datetime!(2019 - 02 - 18 12:00 +01))
/// );
/// assert!(datetime!(2022 - 02 - 18 12:00 +01).replace_year(-1_000_000_000).is_err()); // -1_000_000_000 isn't a valid year
/// assert!(datetime!(2022 - 02 - 18 12:00 +01).replace_year(1_000_000_000).is_err()); // 1_000_000_000 isn't a valid year
/// ```
+ #[must_use = "This method does not mutate the original `OffsetDateTime`."]
pub const fn replace_year(self, year: i32) -> Result<Self, error::ComponentRange> {
- Ok(Self(const_try!(self.0.replace_year(year))))
+ Ok(const_try!(self.date_time().replace_year(year)).assume_offset(self.offset()))
}
/// Replace the month of the year.
///
/// ```rust
/// # use time_macros::datetime;
/// # use time::Month;
/// assert_eq!(
/// datetime!(2022 - 02 - 18 12:00 +01).replace_month(Month::January),
/// Ok(datetime!(2022 - 01 - 18 12:00 +01))
/// );
/// assert!(datetime!(2022 - 01 - 30 12:00 +01).replace_month(Month::February).is_err()); // 30 isn't a valid day in February
/// ```
+ #[must_use = "This method does not mutate the original `OffsetDateTime`."]
pub const fn replace_month(self, month: Month) -> Result<Self, error::ComponentRange> {
- Ok(Self(const_try!(self.0.replace_month(month))))
+ Ok(const_try!(self.date_time().replace_month(month)).assume_offset(self.offset()))
}
/// Replace the day of the month.
///
/// ```rust
/// # use time_macros::datetime;
/// assert_eq!(
/// datetime!(2022 - 02 - 18 12:00 +01).replace_day(1),
/// Ok(datetime!(2022 - 02 - 01 12:00 +01))
/// );
/// assert!(datetime!(2022 - 02 - 18 12:00 +01).replace_day(0).is_err()); // 00 isn't a valid day
/// assert!(datetime!(2022 - 02 - 18 12:00 +01).replace_day(30).is_err()); // 30 isn't a valid day in February
/// ```
+ #[must_use = "This method does not mutate the original `OffsetDateTime`."]
pub const fn replace_day(self, day: u8) -> Result<Self, error::ComponentRange> {
- Ok(Self(const_try!(self.0.replace_day(day))))
+ Ok(const_try!(self.date_time().replace_day(day)).assume_offset(self.offset()))
+ }
+
+ /// Replace the day of the year.
+ ///
+ /// ```rust
+ /// # use time_macros::datetime;
+ /// assert_eq!(datetime!(2022-049 12:00 +01).replace_ordinal(1), Ok(datetime!(2022-001 12:00 +01)));
+ /// assert!(datetime!(2022-049 12:00 +01).replace_ordinal(0).is_err()); // 0 isn't a valid ordinal
+ /// assert!(datetime!(2022-049 12:00 +01).replace_ordinal(366).is_err()); // 2022 isn't a leap year
+ /// ```
+ #[must_use = "This method does not mutate the original `OffsetDateTime`."]
+ pub const fn replace_ordinal(self, ordinal: u16) -> Result<Self, error::ComponentRange> {
+ Ok(const_try!(self.date_time().replace_ordinal(ordinal)).assume_offset(self.offset()))
}
/// Replace the clock hour.
///
/// ```rust
/// # use time_macros::datetime;
/// assert_eq!(
/// datetime!(2022 - 02 - 18 01:02:03.004_005_006 +01).replace_hour(7),
/// Ok(datetime!(2022 - 02 - 18 07:02:03.004_005_006 +01))
/// );
/// assert!(datetime!(2022 - 02 - 18 01:02:03.004_005_006 +01).replace_hour(24).is_err()); // 24 isn't a valid hour
/// ```
+ #[must_use = "This method does not mutate the original `OffsetDateTime`."]
pub const fn replace_hour(self, hour: u8) -> Result<Self, error::ComponentRange> {
- Ok(Self(const_try!(self.0.replace_hour(hour))))
+ Ok(const_try!(self.date_time().replace_hour(hour)).assume_offset(self.offset()))
}
/// Replace the minutes within the hour.
///
/// ```rust
/// # use time_macros::datetime;
/// assert_eq!(
/// datetime!(2022 - 02 - 18 01:02:03.004_005_006 +01).replace_minute(7),
/// Ok(datetime!(2022 - 02 - 18 01:07:03.004_005_006 +01))
/// );
/// assert!(datetime!(2022 - 02 - 18 01:02:03.004_005_006 +01).replace_minute(60).is_err()); // 60 isn't a valid minute
/// ```
+ #[must_use = "This method does not mutate the original `OffsetDateTime`."]
pub const fn replace_minute(self, minute: u8) -> Result<Self, error::ComponentRange> {
- Ok(Self(const_try!(self.0.replace_minute(minute))))
+ Ok(const_try!(self.date_time().replace_minute(minute)).assume_offset(self.offset()))
}
/// Replace the seconds within the minute.
///
/// ```rust
/// # use time_macros::datetime;
/// assert_eq!(
/// datetime!(2022 - 02 - 18 01:02:03.004_005_006 +01).replace_second(7),
/// Ok(datetime!(2022 - 02 - 18 01:02:07.004_005_006 +01))
/// );
/// assert!(datetime!(2022 - 02 - 18 01:02:03.004_005_006 +01).replace_second(60).is_err()); // 60 isn't a valid second
/// ```
+ #[must_use = "This method does not mutate the original `OffsetDateTime`."]
pub const fn replace_second(self, second: u8) -> Result<Self, error::ComponentRange> {
- Ok(Self(const_try!(self.0.replace_second(second))))
+ Ok(const_try!(self.date_time().replace_second(second)).assume_offset(self.offset()))
}
/// Replace the milliseconds within the second.
///
/// ```rust
/// # use time_macros::datetime;
/// assert_eq!(
/// datetime!(2022 - 02 - 18 01:02:03.004_005_006 +01).replace_millisecond(7),
/// Ok(datetime!(2022 - 02 - 18 01:02:03.007 +01))
/// );
/// assert!(datetime!(2022 - 02 - 18 01:02:03.004_005_006 +01).replace_millisecond(1_000).is_err()); // 1_000 isn't a valid millisecond
/// ```
+ #[must_use = "This method does not mutate the original `OffsetDateTime`."]
pub const fn replace_millisecond(
self,
millisecond: u16,
) -> Result<Self, error::ComponentRange> {
- Ok(Self(const_try!(self.0.replace_millisecond(millisecond))))
+ Ok(
+ const_try!(self.date_time().replace_millisecond(millisecond))
+ .assume_offset(self.offset()),
+ )
}
/// Replace the microseconds within the second.
///
/// ```rust
/// # use time_macros::datetime;
/// assert_eq!(
/// datetime!(2022 - 02 - 18 01:02:03.004_005_006 +01).replace_microsecond(7_008),
/// Ok(datetime!(2022 - 02 - 18 01:02:03.007_008 +01))
/// );
/// assert!(datetime!(2022 - 02 - 18 01:02:03.004_005_006 +01).replace_microsecond(1_000_000).is_err()); // 1_000_000 isn't a valid microsecond
/// ```
+ #[must_use = "This method does not mutate the original `OffsetDateTime`."]
pub const fn replace_microsecond(
self,
microsecond: u32,
) -> Result<Self, error::ComponentRange> {
- Ok(Self(const_try!(self.0.replace_microsecond(microsecond))))
+ Ok(
+ const_try!(self.date_time().replace_microsecond(microsecond))
+ .assume_offset(self.offset()),
+ )
}
/// Replace the nanoseconds within the second.
///
/// ```rust
/// # use time_macros::datetime;
/// assert_eq!(
/// datetime!(2022 - 02 - 18 01:02:03.004_005_006 +01).replace_nanosecond(7_008_009),
/// Ok(datetime!(2022 - 02 - 18 01:02:03.007_008_009 +01))
/// );
/// assert!(datetime!(2022 - 02 - 18 01:02:03.004_005_006 +01).replace_nanosecond(1_000_000_000).is_err()); // 1_000_000_000 isn't a valid nanosecond
/// ```
+ #[must_use = "This method does not mutate the original `OffsetDateTime`."]
pub const fn replace_nanosecond(self, nanosecond: u32) -> Result<Self, error::ComponentRange> {
- Ok(Self(const_try!(self.0.replace_nanosecond(nanosecond))))
+ Ok(
+ const_try!(self.date_time().replace_nanosecond(nanosecond))
+ .assume_offset(self.offset()),
+ )
}
}
// endregion replacement
// region: formatting & parsing
#[cfg(feature = "formatting")]
impl OffsetDateTime {
/// Format the `OffsetDateTime` using the provided [format
/// description](crate::format_description).
pub fn format_into(
self,
output: &mut impl io::Write,
format: &(impl Formattable + ?Sized),
) -> Result<usize, error::Format> {
- self.0.format_into(output, format)
+ format.format_into(
+ output,
+ Some(self.date()),
+ Some(self.time()),
+ Some(self.offset()),
+ )
}
/// Format the `OffsetDateTime` using the provided [format
/// description](crate::format_description).
///
/// ```rust
/// # use time::format_description;
/// # use time_macros::datetime;
/// let format = format_description::parse(
/// "[year]-[month]-[day] [hour]:[minute]:[second] [offset_hour \
/// sign:mandatory]:[offset_minute]:[offset_second]",
/// )?;
/// assert_eq!(
/// datetime!(2020-01-02 03:04:05 +06:07:08).format(&format)?,
/// "2020-01-02 03:04:05 +06:07:08"
/// );
/// # Ok::<_, time::Error>(())
/// ```
pub fn format(self, format: &(impl Formattable + ?Sized)) -> Result<String, error::Format> {
- self.0.format(format)
+ format.format(Some(self.date()), Some(self.time()), Some(self.offset()))
}
}
#[cfg(feature = "parsing")]
impl OffsetDateTime {
/// Parse an `OffsetDateTime` from the input using the provided [format
/// description](crate::format_description).
///
/// ```rust
/// # use time::OffsetDateTime;
@@ -1022,175 +1288,331 @@ impl OffsetDateTime {
/// assert_eq!(
/// OffsetDateTime::parse("2020-01-02 03:04:05 +06:07:08", &format)?,
/// datetime!(2020-01-02 03:04:05 +06:07:08)
/// );
/// # Ok::<_, time::Error>(())
/// ```
pub fn parse(
input: &str,
description: &(impl Parsable + ?Sized),
) -> Result<Self, error::Parse> {
- Inner::parse(input, description).map(Self)
+ description.parse_offset_date_time(input.as_bytes())
+ }
+
+ /// A helper method to check if the `OffsetDateTime` is a valid representation of a leap second.
+ /// Leap seconds, when parsed, are represented as the preceding nanosecond. However, leap
+ /// seconds can only occur as the last second of a month UTC.
+ #[cfg(feature = "parsing")]
+ pub(crate) const fn is_valid_leap_second_stand_in(self) -> bool {
+ // This comparison doesn't need to be adjusted for the stored offset, so check it first for
+ // speed.
+ if self.nanosecond() != 999_999_999 {
+ return false;
+ }
+
+ let (year, ordinal, time) = self.to_offset_raw(UtcOffset::UTC);
+ let Ok(date) = Date::from_ordinal_date(year, ordinal) else {
+ return false;
+ };
+
+ time.hour() == 23
+ && time.minute() == 59
+ && time.second() == 59
+ && date.day() == util::days_in_year_month(year, date.month())
+ }
+}
+
+impl SmartDisplay for OffsetDateTime {
+ type Metadata = ();
+
+ fn metadata(&self, _: FormatterOptions) -> Metadata<Self> {
+ let width =
+ smart_display::padded_width_of!(self.date(), " ", self.time(), " ", self.offset());
+ Metadata::new(width, self, ())
+ }
+
+ fn fmt_with_metadata(
+ &self,
+ f: &mut fmt::Formatter<'_>,
+ metadata: Metadata<Self>,
+ ) -> fmt::Result {
+ f.pad_with_width(
+ metadata.unpadded_width(),
+ format_args!("{} {} {}", self.date(), self.time(), self.offset()),
+ )
}
}
impl fmt::Display for OffsetDateTime {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- self.0.fmt(f)
+ SmartDisplay::fmt(self, f)
}
}
impl fmt::Debug for OffsetDateTime {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(self, f)
}
}
// endregion formatting & parsing
// region: trait impls
impl Add<Duration> for OffsetDateTime {
type Output = Self;
- fn add(self, rhs: Duration) -> Self::Output {
- Self(self.0.add(rhs))
+ /// # Panics
+ ///
+ /// This may panic if an overflow occurs.
+ fn add(self, duration: Duration) -> Self::Output {
+ self.checked_add(duration)
+ .expect("resulting value is out of range")
}
}
impl Add<StdDuration> for OffsetDateTime {
type Output = Self;
- fn add(self, rhs: StdDuration) -> Self::Output {
- Self(self.0.add(rhs))
+ /// # Panics
+ ///
+ /// This may panic if an overflow occurs.
+ fn add(self, duration: StdDuration) -> Self::Output {
+ let (is_next_day, time) = self.time().adjusting_add_std(duration);
+
+ Self::new_in_offset(
+ if is_next_day {
+ (self.date() + duration)
+ .next_day()
+ .expect("resulting value is out of range")
+ } else {
+ self.date() + duration
+ },
+ time,
+ self.offset,
+ )
}
}
impl AddAssign<Duration> for OffsetDateTime {
+ /// # Panics
+ ///
+ /// This may panic if an overflow occurs.
fn add_assign(&mut self, rhs: Duration) {
- self.0.add_assign(rhs);
+ *self = *self + rhs;
}
}
impl AddAssign<StdDuration> for OffsetDateTime {
+ /// # Panics
+ ///
+ /// This may panic if an overflow occurs.
fn add_assign(&mut self, rhs: StdDuration) {
- self.0.add_assign(rhs);
+ *self = *self + rhs;
}
}
impl Sub<Duration> for OffsetDateTime {
type Output = Self;
+ /// # Panics
+ ///
+ /// This may panic if an overflow occurs.
fn sub(self, rhs: Duration) -> Self::Output {
- Self(self.0.sub(rhs))
+ self.checked_sub(rhs)
+ .expect("resulting value is out of range")
}
}
impl Sub<StdDuration> for OffsetDateTime {
type Output = Self;
- fn sub(self, rhs: StdDuration) -> Self::Output {
- Self(self.0.sub(rhs))
+ /// # Panics
+ ///
+ /// This may panic if an overflow occurs.
+ fn sub(self, duration: StdDuration) -> Self::Output {
+ let (is_previous_day, time) = self.time().adjusting_sub_std(duration);
+
+ Self::new_in_offset(
+ if is_previous_day {
+ (self.date() - duration)
+ .previous_day()
+ .expect("resulting value is out of range")
+ } else {
+ self.date() - duration
+ },
+ time,
+ self.offset,
+ )
}
}
impl SubAssign<Duration> for OffsetDateTime {
+ /// # Panics
+ ///
+ /// This may panic if an overflow occurs.
fn sub_assign(&mut self, rhs: Duration) {
- self.0.sub_assign(rhs);
+ *self = *self - rhs;
}
}
impl SubAssign<StdDuration> for OffsetDateTime {
+ /// # Panics
+ ///
+ /// This may panic if an overflow occurs.
fn sub_assign(&mut self, rhs: StdDuration) {
- self.0.sub_assign(rhs);
+ *self = *self - rhs;
}
}
impl Sub for OffsetDateTime {
type Output = Duration;
+ /// # Panics
+ ///
+ /// This may panic if an overflow occurs.
fn sub(self, rhs: Self) -> Self::Output {
- self.0.sub(rhs.0)
+ let base = self.date_time() - rhs.date_time();
+ let adjustment = Duration::seconds(
+ (self.offset.whole_seconds() - rhs.offset.whole_seconds()).extend::<i64>(),
+ );
+ base - adjustment
}
}
#[cfg(feature = "std")]
impl Sub<SystemTime> for OffsetDateTime {
type Output = Duration;
+ /// # Panics
+ ///
+ /// This may panic if an overflow occurs.
fn sub(self, rhs: SystemTime) -> Self::Output {
- self.0.sub(rhs)
+ self - Self::from(rhs)
}
}
#[cfg(feature = "std")]
impl Sub<OffsetDateTime> for SystemTime {
type Output = Duration;
+ /// # Panics
+ ///
+ /// This may panic if an overflow occurs.
fn sub(self, rhs: OffsetDateTime) -> Self::Output {
- self.sub(rhs.0)
+ OffsetDateTime::from(self) - rhs
+ }
+}
+
+#[cfg(feature = "std")]
+impl Add<Duration> for SystemTime {
+ type Output = Self;
+
+ fn add(self, duration: Duration) -> Self::Output {
+ if duration.is_zero() {
+ self
+ } else if duration.is_positive() {
+ self + duration.unsigned_abs()
+ } else {
+ debug_assert!(duration.is_negative());
+ self - duration.unsigned_abs()
+ }
}
}
+crate::internal_macros::impl_add_assign!(SystemTime: #[cfg(feature = "std")] Duration);
+
+#[cfg(feature = "std")]
+impl Sub<Duration> for SystemTime {
+ type Output = Self;
+
+ fn sub(self, duration: Duration) -> Self::Output {
+ (OffsetDateTime::from(self) - duration).into()
+ }
+}
+
+crate::internal_macros::impl_sub_assign!(SystemTime: #[cfg(feature = "std")] Duration);
+
#[cfg(feature = "std")]
impl PartialEq<SystemTime> for OffsetDateTime {
fn eq(&self, rhs: &SystemTime) -> bool {
- self.0.eq(rhs)
+ self == &Self::from(*rhs)
}
}
#[cfg(feature = "std")]
impl PartialEq<OffsetDateTime> for SystemTime {
fn eq(&self, rhs: &OffsetDateTime) -> bool {
- self.eq(&rhs.0)
+ &OffsetDateTime::from(*self) == rhs
}
}
#[cfg(feature = "std")]
impl PartialOrd<SystemTime> for OffsetDateTime {
fn partial_cmp(&self, other: &SystemTime) -> Option<Ordering> {
- self.0.partial_cmp(other)
+ self.partial_cmp(&Self::from(*other))
}
}
#[cfg(feature = "std")]
impl PartialOrd<OffsetDateTime> for SystemTime {
fn partial_cmp(&self, other: &OffsetDateTime) -> Option<Ordering> {
- self.partial_cmp(&other.0)
+ OffsetDateTime::from(*self).partial_cmp(other)
}
}
#[cfg(feature = "std")]
impl From<SystemTime> for OffsetDateTime {
fn from(system_time: SystemTime) -> Self {
- Self(Inner::from(system_time))
+ match system_time.duration_since(SystemTime::UNIX_EPOCH) {
+ Ok(duration) => Self::UNIX_EPOCH + duration,
+ Err(err) => Self::UNIX_EPOCH - err.duration(),
+ }
}
}
#[cfg(feature = "std")]
impl From<OffsetDateTime> for SystemTime {
fn from(datetime: OffsetDateTime) -> Self {
- datetime.0.into()
+ let duration = datetime - OffsetDateTime::UNIX_EPOCH;
+
+ if duration.is_zero() {
+ Self::UNIX_EPOCH
+ } else if duration.is_positive() {
+ Self::UNIX_EPOCH + duration.unsigned_abs()
+ } else {
+ debug_assert!(duration.is_negative());
+ Self::UNIX_EPOCH - duration.unsigned_abs()
+ }
}
}
#[cfg(all(
target_family = "wasm",
not(any(target_os = "emscripten", target_os = "wasi")),
feature = "wasm-bindgen"
))]
impl From<js_sys::Date> for OffsetDateTime {
+ /// # Panics
+ ///
+ /// This may panic if the timestamp can not be represented.
fn from(js_date: js_sys::Date) -> Self {
- Self(Inner::from(js_date))
+ // get_time() returns milliseconds
+ let timestamp_nanos = (js_date.get_time() * Nanosecond::per(Millisecond) as f64) as i128;
+ Self::from_unix_timestamp_nanos(timestamp_nanos)
+ .expect("invalid timestamp: Timestamp cannot fit in range")
}
}
#[cfg(all(
target_family = "wasm",
not(any(target_os = "emscripten", target_os = "wasi")),
feature = "wasm-bindgen"
))]
impl From<OffsetDateTime> for js_sys::Date {
fn from(datetime: OffsetDateTime) -> Self {
- datetime.0.into()
+ // new Date() takes milliseconds
+ let timestamp = (datetime.unix_timestamp_nanos()
+ / Nanosecond::per(Millisecond).cast_signed().extend::<i128>())
+ as f64;
+ Self::new(&timestamp.into())
}
}
-
// endregion trait impls
diff --git a/third_party/rust/time/src/parsing/combinator/mod.rs b/third_party/rust/time/src/parsing/combinator/mod.rs
index 3b4bc7a81b..e3e1df76cb 100644
--- a/third_party/rust/time/src/parsing/combinator/mod.rs
+++ b/third_party/rust/time/src/parsing/combinator/mod.rs
@@ -1,14 +1,16 @@
//! Implementations of the low-level parser combinators.
pub(crate) mod rfc;
+use num_conv::prelude::*;
+
use crate::format_description::modifier::Padding;
use crate::parsing::shim::{Integer, IntegerParseBytes};
use crate::parsing::ParsedItem;
/// Parse a "+" or "-" sign. Returns the ASCII byte representing the sign, if present.
pub(crate) const fn sign(input: &[u8]) -> Option<ParsedItem<'_, u8>> {
match input {
[sign @ (b'-' | b'+'), remaining @ ..] => Some(ParsedItem(remaining, *sign)),
_ => None,
}
@@ -128,21 +130,21 @@ pub(crate) fn n_to_m_digits_padded<'a, const N: u8, const M: u8, T: Integer>(
Padding::Space => {
debug_assert!(N > 0);
let mut orig_input = input;
for _ in 0..(N - 1) {
match ascii_char::<b' '>(input) {
Some(parsed) => input = parsed.0,
None => break,
}
}
- let pad_width = (orig_input.len() - input.len()) as u8;
+ let pad_width = (orig_input.len() - input.len()).truncate::<u8>();
orig_input = input;
for _ in 0..(N - pad_width) {
input = any_digit(input)?.0;
}
for _ in N..M {
match any_digit(input) {
Some(parsed) => input = parsed.0,
None => break,
}
diff --git a/third_party/rust/time/src/parsing/combinator/rfc/iso8601.rs b/third_party/rust/time/src/parsing/combinator/rfc/iso8601.rs
index 613a9057ff..91eea2dc25 100644
--- a/third_party/rust/time/src/parsing/combinator/rfc/iso8601.rs
+++ b/third_party/rust/time/src/parsing/combinator/rfc/iso8601.rs
@@ -1,16 +1,18 @@
//! Rules defined in [ISO 8601].
//!
//! [ISO 8601]: https://www.iso.org/iso-8601-date-and-time-format.html
use core::num::{NonZeroU16, NonZeroU8};
+use num_conv::prelude::*;
+
use crate::parsing::combinator::{any_digit, ascii_char, exactly_n_digits, first_match, sign};
use crate::parsing::ParsedItem;
use crate::{Month, Weekday};
/// What kind of format is being parsed. This is used to ensure each part of the format (date, time,
/// offset) is the same kind.
#[derive(Debug, Clone, Copy)]
pub(crate) enum ExtendedKind {
/// The basic format.
Basic,
@@ -55,24 +57,24 @@ impl ExtendedKind {
Some(())
}
}
}
}
/// Parse a possibly expanded year.
pub(crate) fn year(input: &[u8]) -> Option<ParsedItem<'_, i32>> {
Some(match sign(input) {
Some(ParsedItem(input, sign)) => exactly_n_digits::<6, u32>(input)?.map(|val| {
- let val = val as i32;
+ let val = val.cast_signed();
if sign == b'-' { -val } else { val }
}),
- None => exactly_n_digits::<4, u32>(input)?.map(|val| val as _),
+ None => exactly_n_digits::<4, u32>(input)?.map(|val| val.cast_signed()),
})
}
/// Parse a month.
pub(crate) fn month(input: &[u8]) -> Option<ParsedItem<'_, Month>> {
first_match(
[
(b"01".as_slice(), Month::January),
(b"02".as_slice(), Month::February),
(b"03".as_slice(), Month::March),
diff --git a/third_party/rust/time/src/parsing/component.rs b/third_party/rust/time/src/parsing/component.rs
index 23035893e4..e2a7e7c0d9 100644
--- a/third_party/rust/time/src/parsing/component.rs
+++ b/third_party/rust/time/src/parsing/component.rs
@@ -1,14 +1,16 @@
//! Parsing implementations for all [`Component`](crate::format_description::Component)s.
use core::num::{NonZeroU16, NonZeroU8};
+use num_conv::prelude::*;
+
use crate::convert::*;
use crate::format_description::modifier;
#[cfg(feature = "large-dates")]
use crate::parsing::combinator::n_to_m_digits_padded;
use crate::parsing::combinator::{
any_digit, exactly_n_digits, exactly_n_digits_padded, first_match, n_to_m_digits, opt, sign,
};
use crate::parsing::ParsedItem;
use crate::{Month, Weekday};
@@ -18,28 +20,28 @@ pub(crate) fn parse_year(input: &[u8], modifiers: modifier::Year) -> Option<Pars
match modifiers.repr {
modifier::YearRepr::Full => {
let ParsedItem(input, sign) = opt(sign)(input);
#[cfg(not(feature = "large-dates"))]
let ParsedItem(input, year) =
exactly_n_digits_padded::<4, u32>(modifiers.padding)(input)?;
#[cfg(feature = "large-dates")]
let ParsedItem(input, year) =
n_to_m_digits_padded::<4, 6, u32>(modifiers.padding)(input)?;
match sign {
- Some(b'-') => Some(ParsedItem(input, -(year as i32))),
+ Some(b'-') => Some(ParsedItem(input, -year.cast_signed())),
None if modifiers.sign_is_mandatory || year >= 10_000 => None,
- _ => Some(ParsedItem(input, year as i32)),
+ _ => Some(ParsedItem(input, year.cast_signed())),
}
}
- modifier::YearRepr::LastTwo => {
- Some(exactly_n_digits_padded::<2, u32>(modifiers.padding)(input)?.map(|v| v as i32))
- }
+ modifier::YearRepr::LastTwo => Some(
+ exactly_n_digits_padded::<2, u32>(modifiers.padding)(input)?.map(|v| v.cast_signed()),
+ ),
}
}
/// Parse the "month" component of a `Date`.
pub(crate) fn parse_month(
input: &[u8],
modifiers: modifier::Month,
) -> Option<ParsedItem<'_, Month>> {
use Month::*;
let ParsedItem(remaining, value) = first_match(
@@ -236,25 +238,25 @@ pub(crate) fn parse_subsecond(
Two => exactly_n_digits::<2, u32>(input)?.map(|v| v * 10_000_000),
Three => exactly_n_digits::<3, u32>(input)?.map(|v| v * 1_000_000),
Four => exactly_n_digits::<4, u32>(input)?.map(|v| v * 100_000),
Five => exactly_n_digits::<5, u32>(input)?.map(|v| v * 10_000),
Six => exactly_n_digits::<6, u32>(input)?.map(|v| v * 1_000),
Seven => exactly_n_digits::<7, u32>(input)?.map(|v| v * 100),
Eight => exactly_n_digits::<8, u32>(input)?.map(|v| v * 10),
Nine => exactly_n_digits::<9, _>(input)?,
OneOrMore => {
let ParsedItem(mut input, mut value) =
- any_digit(input)?.map(|v| (v - b'0') as u32 * 100_000_000);
+ any_digit(input)?.map(|v| (v - b'0').extend::<u32>() * 100_000_000);
let mut multiplier = 10_000_000;
while let Some(ParsedItem(new_input, digit)) = any_digit(input) {
- value += (digit - b'0') as u32 * multiplier;
+ value += (digit - b'0').extend::<u32>() * multiplier;
input = new_input;
multiplier /= 10;
}
ParsedItem(input, value)
}
})
}
// endregion time components
@@ -262,72 +264,83 @@ pub(crate) fn parse_subsecond(
/// Parse the "hour" component of a `UtcOffset`.
///
/// Returns the value and whether the value is negative. This is used for when "-0" is parsed.
pub(crate) fn parse_offset_hour(
input: &[u8],
modifiers: modifier::OffsetHour,
) -> Option<ParsedItem<'_, (i8, bool)>> {
let ParsedItem(input, sign) = opt(sign)(input);
let ParsedItem(input, hour) = exactly_n_digits_padded::<2, u8>(modifiers.padding)(input)?;
match sign {
- Some(b'-') => Some(ParsedItem(input, (-(hour as i8), true))),
+ Some(b'-') => Some(ParsedItem(input, (-hour.cast_signed(), true))),
None if modifiers.sign_is_mandatory => None,
- _ => Some(ParsedItem(input, (hour as i8, false))),
+ _ => Some(ParsedItem(input, (hour.cast_signed(), false))),
}
}
/// Parse the "minute" component of a `UtcOffset`.
pub(crate) fn parse_offset_minute(
input: &[u8],
modifiers: modifier::OffsetMinute,
) -> Option<ParsedItem<'_, i8>> {
Some(
exactly_n_digits_padded::<2, u8>(modifiers.padding)(input)?
- .map(|offset_minute| offset_minute as _),
+ .map(|offset_minute| offset_minute.cast_signed()),
)
}
/// Parse the "second" component of a `UtcOffset`.
pub(crate) fn parse_offset_second(
input: &[u8],
modifiers: modifier::OffsetSecond,
) -> Option<ParsedItem<'_, i8>> {
Some(
exactly_n_digits_padded::<2, u8>(modifiers.padding)(input)?
- .map(|offset_second| offset_second as _),
+ .map(|offset_second| offset_second.cast_signed()),
)
}
// endregion offset components
/// Ignore the given number of bytes.
pub(crate) fn parse_ignore(
input: &[u8],
modifiers: modifier::Ignore,
) -> Option<ParsedItem<'_, ()>> {
let modifier::Ignore { count } = modifiers;
- let input = input.get((count.get() as usize)..)?;
+ let input = input.get((count.get().extend())..)?;
Some(ParsedItem(input, ()))
}
/// Parse the Unix timestamp component.
pub(crate) fn parse_unix_timestamp(
input: &[u8],
modifiers: modifier::UnixTimestamp,
) -> Option<ParsedItem<'_, i128>> {
let ParsedItem(input, sign) = opt(sign)(input);
let ParsedItem(input, nano_timestamp) = match modifiers.precision {
- modifier::UnixTimestampPrecision::Second => {
- n_to_m_digits::<1, 14, u128>(input)?.map(|val| val * Nanosecond.per(Second) as u128)
- }
+ modifier::UnixTimestampPrecision::Second => n_to_m_digits::<1, 14, u128>(input)?
+ .map(|val| val * Nanosecond::per(Second).extend::<u128>()),
modifier::UnixTimestampPrecision::Millisecond => n_to_m_digits::<1, 17, u128>(input)?
- .map(|val| val * Nanosecond.per(Millisecond) as u128),
+ .map(|val| val * Nanosecond::per(Millisecond).extend::<u128>()),
modifier::UnixTimestampPrecision::Microsecond => n_to_m_digits::<1, 20, u128>(input)?
- .map(|val| val * Nanosecond.per(Microsecond) as u128),
+ .map(|val| val * Nanosecond::per(Microsecond).extend::<u128>()),
modifier::UnixTimestampPrecision::Nanosecond => n_to_m_digits::<1, 23, _>(input)?,
};
match sign {
- Some(b'-') => Some(ParsedItem(input, -(nano_timestamp as i128))),
+ Some(b'-') => Some(ParsedItem(input, -nano_timestamp.cast_signed())),
None if modifiers.sign_is_mandatory => None,
- _ => Some(ParsedItem(input, nano_timestamp as _)),
+ _ => Some(ParsedItem(input, nano_timestamp.cast_signed())),
+ }
+}
+
+/// Parse the `end` component, which represents the end of input. If any input is remaining, `None`
+/// is returned.
+pub(crate) const fn parse_end(input: &[u8], end: modifier::End) -> Option<ParsedItem<'_, ()>> {
+ let modifier::End {} = end;
+
+ if input.is_empty() {
+ Some(ParsedItem(input, ()))
+ } else {
+ None
}
}
diff --git a/third_party/rust/time/src/parsing/iso8601.rs b/third_party/rust/time/src/parsing/iso8601.rs
index 87e517e376..000835b0dd 100644
--- a/third_party/rust/time/src/parsing/iso8601.rs
+++ b/third_party/rust/time/src/parsing/iso8601.rs
@@ -1,12 +1,14 @@
//! Parse parts of an ISO 8601-formatted value.
+use num_conv::prelude::*;
+
use crate::convert::*;
use crate::error;
use crate::error::ParseFromDescription::{InvalidComponent, InvalidLiteral};
use crate::format_description::well_known::iso8601::EncodedConfig;
use crate::format_description::well_known::Iso8601;
use crate::parsing::combinator::rfc::iso8601::{
day, dayk, dayo, float, hour, min, month, week, year, ExtendedKind,
};
use crate::parsing::combinator::{ascii_char, sign};
use crate::parsing::{Parsed, ParsedItem};
@@ -27,30 +29,31 @@ impl<const CONFIG: EncodedConfig> Iso8601<CONFIG> {
// Same for any acceptable format.
let ParsedItem(mut input, year) = year(input).ok_or(InvalidComponent("year"))?;
*extended_kind = match ascii_char::<b'-'>(input) {
Some(ParsedItem(new_input, ())) => {
input = new_input;
ExtendedKind::Extended
}
None => ExtendedKind::Basic, // no separator before mandatory month/ordinal/week
};
- let mut ret_error = match (|| {
+ let parsed_month_day = (|| {
let ParsedItem(mut input, month) = month(input).ok_or(InvalidComponent("month"))?;
if extended_kind.is_extended() {
input = ascii_char::<b'-'>(input)
.ok_or(InvalidLiteral)?
.into_inner();
}
let ParsedItem(input, day) = day(input).ok_or(InvalidComponent("day"))?;
Ok(ParsedItem(input, (month, day)))
- })() {
+ })();
+ let mut ret_error = match parsed_month_day {
Ok(ParsedItem(input, (month, day))) => {
*parsed = parsed
.with_year(year)
.ok_or(InvalidComponent("year"))?
.with_month(month)
.ok_or(InvalidComponent("month"))?
.with_day(day)
.ok_or(InvalidComponent("day"))?;
return Ok(input);
}
@@ -60,35 +63,36 @@ impl<const CONFIG: EncodedConfig> Iso8601<CONFIG> {
// Don't check for `None`, as the error from year-month-day will always take priority.
if let Some(ParsedItem(input, ordinal)) = dayo(input) {
*parsed = parsed
.with_year(year)
.ok_or(InvalidComponent("year"))?
.with_ordinal(ordinal)
.ok_or(InvalidComponent("ordinal"))?;
return Ok(input);
}
- match (|| {
+ let parsed_week_weekday = (|| {
let input = ascii_char::<b'W'>(input)
.ok_or((false, InvalidLiteral))?
.into_inner();
let ParsedItem(mut input, week) =
week(input).ok_or((true, InvalidComponent("week")))?;
if extended_kind.is_extended() {
input = ascii_char::<b'-'>(input)
.ok_or((true, InvalidLiteral))?
.into_inner();
}
let ParsedItem(input, weekday) =
dayk(input).ok_or((true, InvalidComponent("weekday")))?;
Ok(ParsedItem(input, (week, weekday)))
- })() {
+ })();
+ match parsed_week_weekday {
Ok(ParsedItem(input, (week, weekday))) => {
*parsed = parsed
.with_iso_year(year)
.ok_or(InvalidComponent("year"))?
.with_iso_week_number(week)
.ok_or(InvalidComponent("week"))?
.with_weekday(weekday)
.ok_or(InvalidComponent("weekday"))?;
return Ok(input);
}
@@ -118,30 +122,30 @@ impl<const CONFIG: EncodedConfig> Iso8601<CONFIG> {
.into_inner();
}
let ParsedItem(mut input, hour) = float(input).ok_or(InvalidComponent("hour"))?;
match hour {
(hour, None) => parsed.set_hour_24(hour).ok_or(InvalidComponent("hour"))?,
(hour, Some(fractional_part)) => {
*parsed = parsed
.with_hour_24(hour)
.ok_or(InvalidComponent("hour"))?
- .with_minute((fractional_part * Second.per(Minute) as f64) as _)
+ .with_minute((fractional_part * Second::per(Minute) as f64) as _)
.ok_or(InvalidComponent("minute"))?
.with_second(
- (fractional_part * Second.per(Hour) as f64 % Minute.per(Hour) as f64)
+ (fractional_part * Second::per(Hour) as f64 % Minute::per(Hour) as f64)
as _,
)
.ok_or(InvalidComponent("second"))?
.with_subsecond(
- (fractional_part * Nanosecond.per(Hour) as f64
- % Nanosecond.per(Second) as f64) as _,
+ (fractional_part * Nanosecond::per(Hour) as f64
+ % Nanosecond::per(Second) as f64) as _,
)
.ok_or(InvalidComponent("subsecond"))?;
return Ok(input);
}
};
if let Some(ParsedItem(new_input, ())) = ascii_char::<b':'>(input) {
extended_kind
.coerce_extended()
.ok_or(InvalidComponent("minute"))?;
@@ -155,25 +159,25 @@ impl<const CONFIG: EncodedConfig> Iso8601<CONFIG> {
.set_minute(minute)
.ok_or(InvalidComponent("minute"))?;
input
}
Some(ParsedItem(input, (minute, Some(fractional_part)))) => {
// `None` is valid behavior, so don't error if this fails.
extended_kind.coerce_basic();
*parsed = parsed
.with_minute(minute)
.ok_or(InvalidComponent("minute"))?
- .with_second((fractional_part * Second.per(Minute) as f64) as _)
+ .with_second((fractional_part * Second::per(Minute) as f64) as _)
.ok_or(InvalidComponent("second"))?
.with_subsecond(
- (fractional_part * Nanosecond.per(Minute) as f64
- % Nanosecond.per(Second) as f64) as _,
+ (fractional_part * Nanosecond::per(Minute) as f64
+ % Nanosecond::per(Second) as f64) as _,
)
.ok_or(InvalidComponent("subsecond"))?;
return Ok(input);
}
// colon was present, so minutes are required
None if extended_kind.is_extended() => {
return Err(error::Parse::ParseFromDescription(InvalidComponent(
"minute",
)));
}
@@ -202,21 +206,21 @@ impl<const CONFIG: EncodedConfig> Iso8601<CONFIG> {
return Ok(input);
}
}
}
let (input, second, subsecond) = match float(input) {
Some(ParsedItem(input, (second, None))) => (input, second, 0),
Some(ParsedItem(input, (second, Some(fractional_part)))) => (
input,
second,
- round(fractional_part * Nanosecond.per(Second) as f64) as _,
+ round(fractional_part * Nanosecond::per(Second) as f64) as _,
),
None if extended_kind.is_extended() => {
return Err(error::Parse::ParseFromDescription(InvalidComponent(
"second",
)));
}
// Missing components are assumed to be zero.
None => (input, 0, 0),
};
*parsed = parsed
@@ -247,45 +251,45 @@ impl<const CONFIG: EncodedConfig> Iso8601<CONFIG> {
.with_offset_second_signed(0)
.ok_or(InvalidComponent("offset second"))?;
return Ok(input);
}
let ParsedItem(input, sign) = sign(input).ok_or(InvalidComponent("offset hour"))?;
let mut input = hour(input)
.and_then(|parsed_item| {
parsed_item.consume_value(|hour| {
parsed.set_offset_hour(if sign == b'-' {
- -(hour as i8)
+ -hour.cast_signed()
} else {
- hour as _
+ hour.cast_signed()
})
})
})
.ok_or(InvalidComponent("offset hour"))?;
if extended_kind.maybe_extended() {
if let Some(ParsedItem(new_input, ())) = ascii_char::<b':'>(input) {
extended_kind
.coerce_extended()
.ok_or(InvalidComponent("offset minute"))?;
input = new_input;
};
}
match min(input) {
Some(ParsedItem(new_input, min)) => {
input = new_input;
parsed
.set_offset_minute_signed(if sign == b'-' {
- -(min as i8)
+ -min.cast_signed()
} else {
- min as _
+ min.cast_signed()
})
.ok_or(InvalidComponent("offset minute"))?;
}
None => {
// Omitted offset minute is assumed to be zero.
parsed.set_offset_minute_signed(0);
}
}
// If `:` was present, the format has already been set to extended. As such, this call
diff --git a/third_party/rust/time/src/parsing/mod.rs b/third_party/rust/time/src/parsing/mod.rs
index 4a2aa829f1..e65cfd5664 100644
--- a/third_party/rust/time/src/parsing/mod.rs
+++ b/third_party/rust/time/src/parsing/mod.rs
@@ -24,20 +24,27 @@ impl<'a, T> ParsedItem<'a, T> {
pub(crate) fn flat_map<U>(self, f: impl FnOnce(T) -> Option<U>) -> Option<ParsedItem<'a, U>> {
Some(ParsedItem(self.0, f(self.1)?))
}
/// Consume the stored value with the provided function. The remaining input is returned.
#[must_use = "this returns the remaining input"]
pub(crate) fn consume_value(self, f: impl FnOnce(T) -> Option<()>) -> Option<&'a [u8]> {
f(self.1)?;
Some(self.0)
}
+
+ /// Filter the value with the provided function. If the function returns `false`, the value
+ /// is discarded and `None` is returned. Otherwise, the value is preserved and `Some(self)` is
+ /// returned.
+ pub(crate) fn filter(self, f: impl FnOnce(&T) -> bool) -> Option<ParsedItem<'a, T>> {
+ f(&self.1).then_some(self)
+ }
}
impl<'a> ParsedItem<'a, ()> {
/// Discard the unit value, returning the remaining input.
#[must_use = "this returns the remaining input"]
pub(crate) const fn into_inner(self) -> &'a [u8] {
self.0
}
}
diff --git a/third_party/rust/time/src/parsing/parsable.rs b/third_party/rust/time/src/parsing/parsable.rs
index 78bbe64f0f..1679f63686 100644
--- a/third_party/rust/time/src/parsing/parsable.rs
+++ b/third_party/rust/time/src/parsing/parsable.rs
@@ -1,43 +1,47 @@
//! A trait that can be used to parse an item from an input.
use core::ops::Deref;
-use crate::date_time::{maybe_offset_from_offset, MaybeOffset};
+use num_conv::prelude::*;
+
use crate::error::TryFromParsed;
use crate::format_description::well_known::iso8601::EncodedConfig;
use crate::format_description::well_known::{Iso8601, Rfc2822, Rfc3339};
-use crate::format_description::FormatItem;
+use crate::format_description::BorrowedFormatItem;
#[cfg(feature = "alloc")]
use crate::format_description::OwnedFormatItem;
+use crate::internal_macros::bug;
use crate::parsing::{Parsed, ParsedItem};
-use crate::{error, Date, DateTime, Month, Time, UtcOffset, Weekday};
+use crate::{error, Date, Month, OffsetDateTime, Time, UtcOffset, Weekday};
/// A type that can be parsed.
#[cfg_attr(__time_03_docs, doc(notable_trait))]
+#[doc(alias = "Parseable")]
pub trait Parsable: sealed::Sealed {}
-impl Parsable for FormatItem<'_> {}
-impl Parsable for [FormatItem<'_>] {}
+impl Parsable for BorrowedFormatItem<'_> {}
+impl Parsable for [BorrowedFormatItem<'_>] {}
#[cfg(feature = "alloc")]
impl Parsable for OwnedFormatItem {}
#[cfg(feature = "alloc")]
impl Parsable for [OwnedFormatItem] {}
impl Parsable for Rfc2822 {}
impl Parsable for Rfc3339 {}
impl<const CONFIG: EncodedConfig> Parsable for Iso8601<CONFIG> {}
impl<T: Deref> Parsable for T where T::Target: Parsable {}
/// Seal the trait to prevent downstream users from implementing it, while still allowing it to
/// exist in generic bounds.
mod sealed {
#[allow(clippy::wildcard_imports)]
use super::*;
+ use crate::PrimitiveDateTime;
/// Parse the item using a format description and an input.
pub trait Sealed {
/// Parse the item into the provided [`Parsed`] struct.
///
/// This method can be used to parse a single component without parsing the full value.
fn parse_into<'a>(
&self,
input: &'a [u8],
parsed: &mut Parsed,
@@ -45,61 +49,68 @@ mod sealed {
/// Parse the item into a new [`Parsed`] struct.
///
/// This method can only be used to parse a complete value of a type. If any characters
/// remain after parsing, an error will be returned.
fn parse(&self, input: &[u8]) -> Result<Parsed, error::Parse> {
let mut parsed = Parsed::new();
if self.parse_into(input, &mut parsed)?.is_empty() {
Ok(parsed)
} else {
- Err(error::Parse::UnexpectedTrailingCharacters)
+ Err(error::Parse::ParseFromDescription(
+ error::ParseFromDescription::UnexpectedTrailingCharacters,
+ ))
}
}
/// Parse a [`Date`] from the format description.
fn parse_date(&self, input: &[u8]) -> Result<Date, error::Parse> {
Ok(self.parse(input)?.try_into()?)
}
/// Parse a [`Time`] from the format description.
fn parse_time(&self, input: &[u8]) -> Result<Time, error::Parse> {
Ok(self.parse(input)?.try_into()?)
}
/// Parse a [`UtcOffset`] from the format description.
fn parse_offset(&self, input: &[u8]) -> Result<UtcOffset, error::Parse> {
Ok(self.parse(input)?.try_into()?)
}
- /// Parse a [`DateTime`] from the format description.
- fn parse_date_time<O: MaybeOffset>(
+ /// Parse a [`PrimitiveDateTime`] from the format description.
+ fn parse_primitive_date_time(
&self,
input: &[u8],
- ) -> Result<DateTime<O>, error::Parse> {
+ ) -> Result<PrimitiveDateTime, error::Parse> {
+ Ok(self.parse(input)?.try_into()?)
+ }
+
+ /// Parse a [`OffsetDateTime`] from the format description.
+ fn parse_offset_date_time(&self, input: &[u8]) -> Result<OffsetDateTime, error::Parse> {
Ok(self.parse(input)?.try_into()?)
}
}
}
// region: custom formats
-impl sealed::Sealed for FormatItem<'_> {
+impl sealed::Sealed for BorrowedFormatItem<'_> {
fn parse_into<'a>(
&self,
input: &'a [u8],
parsed: &mut Parsed,
) -> Result<&'a [u8], error::Parse> {
Ok(parsed.parse_item(input, self)?)
}
}
-impl sealed::Sealed for [FormatItem<'_>] {
+impl sealed::Sealed for [BorrowedFormatItem<'_>] {
fn parse_into<'a>(
&self,
input: &'a [u8],
parsed: &mut Parsed,
) -> Result<&'a [u8], error::Parse> {
Ok(parsed.parse_items(input, self)?)
}
}
#[cfg(feature = "alloc")]
@@ -147,37 +158,42 @@ impl sealed::Sealed for Rfc2822 {
) -> Result<&'a [u8], error::Parse> {
use crate::error::ParseFromDescription::{InvalidComponent, InvalidLiteral};
use crate::parsing::combinator::rfc::rfc2822::{cfws, fws};
use crate::parsing::combinator::{
ascii_char, exactly_n_digits, first_match, n_to_m_digits, opt, sign,
};
let colon = ascii_char::<b':'>;
let comma = ascii_char::<b','>;
- let input = opt(fws)(input).into_inner();
- let input = first_match(
+ let input = opt(cfws)(input).into_inner();
+ let weekday = first_match(
[
(b"Mon".as_slice(), Weekday::Monday),
(b"Tue".as_slice(), Weekday::Tuesday),
(b"Wed".as_slice(), Weekday::Wednesday),
(b"Thu".as_slice(), Weekday::Thursday),
(b"Fri".as_slice(), Weekday::Friday),
(b"Sat".as_slice(), Weekday::Saturday),
(b"Sun".as_slice(), Weekday::Sunday),
],
false,
- )(input)
- .and_then(|item| item.consume_value(|value| parsed.set_weekday(value)))
- .ok_or(InvalidComponent("weekday"))?;
- let input = comma(input).ok_or(InvalidLiteral)?.into_inner();
- let input = cfws(input).ok_or(InvalidLiteral)?.into_inner();
+ )(input);
+ let input = if let Some(item) = weekday {
+ let input = item
+ .consume_value(|value| parsed.set_weekday(value))
+ .ok_or(InvalidComponent("weekday"))?;
+ let input = comma(input).ok_or(InvalidLiteral)?.into_inner();
+ opt(cfws)(input).into_inner()
+ } else {
+ input
+ };
let input = n_to_m_digits::<1, 2, _>(input)
.and_then(|item| item.consume_value(|value| parsed.set_day(value)))
.ok_or(InvalidComponent("day"))?;
let input = cfws(input).ok_or(InvalidLiteral)?.into_inner();
let input = first_match(
[
(b"Jan".as_slice(), Month::January),
(b"Feb".as_slice(), Month::February),
(b"Mar".as_slice(), Month::March),
(b"Apr".as_slice(), Month::April),
@@ -192,29 +208,31 @@ impl sealed::Sealed for Rfc2822 {
],
false,
)(input)
.and_then(|item| item.consume_value(|value| parsed.set_month(value)))
.ok_or(InvalidComponent("month"))?;
let input = cfws(input).ok_or(InvalidLiteral)?.into_inner();
let input = match exactly_n_digits::<4, u32>(input) {
Some(item) => {
let input = item
.flat_map(|year| if year >= 1900 { Some(year) } else { None })
- .and_then(|item| item.consume_value(|value| parsed.set_year(value as _)))
+ .and_then(|item| {
+ item.consume_value(|value| parsed.set_year(value.cast_signed()))
+ })
.ok_or(InvalidComponent("year"))?;
fws(input).ok_or(InvalidLiteral)?.into_inner()
}
None => {
let input = exactly_n_digits::<2, u32>(input)
.and_then(|item| {
item.map(|year| if year < 50 { year + 2000 } else { year + 1900 })
- .map(|year| year as _)
+ .map(|year| year.cast_signed())
.consume_value(|value| parsed.set_year(value))
})
.ok_or(InvalidComponent("year"))?;
cfws(input).ok_or(InvalidLiteral)?.into_inner()
}
};
let input = exactly_n_digits::<2, _>(input)
.and_then(|item| item.consume_value(|value| parsed.set_hour_24(value)))
.ok_or(InvalidComponent("hour"))?;
@@ -230,21 +248,21 @@ impl sealed::Sealed for Rfc2822 {
let input = opt(cfws)(input).into_inner();
let input = exactly_n_digits::<2, _>(input)
.and_then(|item| item.consume_value(|value| parsed.set_second(value)))
.ok_or(InvalidComponent("second"))?;
cfws(input).ok_or(InvalidLiteral)?.into_inner()
} else {
cfws(input).ok_or(InvalidLiteral)?.into_inner()
};
// The RFC explicitly allows leap seconds.
- parsed.set_flag(Parsed::LEAP_SECOND_ALLOWED_FLAG, true);
+ parsed.leap_second_allowed = true;
#[allow(clippy::unnecessary_lazy_evaluations)] // rust-lang/rust-clippy#8522
let zone_literal = first_match(
[
(b"UT".as_slice(), 0),
(b"GMT".as_slice(), 0),
(b"EST".as_slice(), -5),
(b"EDT".as_slice(), -4),
(b"CST".as_slice(), -6),
(b"CDT".as_slice(), -5),
@@ -273,65 +291,71 @@ impl sealed::Sealed for Rfc2822 {
.set_offset_second_signed(0)
.ok_or(InvalidComponent("offset second"))?;
return Ok(input);
}
let ParsedItem(input, offset_sign) = sign(input).ok_or(InvalidComponent("offset hour"))?;
let input = exactly_n_digits::<2, u8>(input)
.and_then(|item| {
item.map(|offset_hour| {
if offset_sign == b'-' {
- -(offset_hour as i8)
+ -offset_hour.cast_signed()
} else {
- offset_hour as _
+ offset_hour.cast_signed()
}
})
.consume_value(|value| parsed.set_offset_hour(value))
})
.ok_or(InvalidComponent("offset hour"))?;
let input = exactly_n_digits::<2, u8>(input)
.and_then(|item| {
- item.consume_value(|value| parsed.set_offset_minute_signed(value as _))
+ item.consume_value(|value| parsed.set_offset_minute_signed(value.cast_signed()))
})
.ok_or(InvalidComponent("offset minute"))?;
+ let input = opt(cfws)(input).into_inner();
+
Ok(input)
}
- fn parse_date_time<O: MaybeOffset>(&self, input: &[u8]) -> Result<DateTime<O>, error::Parse> {
+ fn parse_offset_date_time(&self, input: &[u8]) -> Result<OffsetDateTime, error::Parse> {
use crate::error::ParseFromDescription::{InvalidComponent, InvalidLiteral};
use crate::parsing::combinator::rfc::rfc2822::{cfws, fws};
use crate::parsing::combinator::{
ascii_char, exactly_n_digits, first_match, n_to_m_digits, opt, sign,
};
let colon = ascii_char::<b':'>;
let comma = ascii_char::<b','>;
- let input = opt(fws)(input).into_inner();
+ let input = opt(cfws)(input).into_inner();
// This parses the weekday, but we don't actually use the value anywhere. Because of this,
// just return `()` to avoid unnecessary generated code.
- let ParsedItem(input, ()) = first_match(
+ let weekday = first_match(
[
(b"Mon".as_slice(), ()),
(b"Tue".as_slice(), ()),
(b"Wed".as_slice(), ()),
(b"Thu".as_slice(), ()),
(b"Fri".as_slice(), ()),
(b"Sat".as_slice(), ()),
(b"Sun".as_slice(), ()),
],
false,
- )(input)
- .ok_or(InvalidComponent("weekday"))?;
- let input = comma(input).ok_or(InvalidLiteral)?.into_inner();
- let input = cfws(input).ok_or(InvalidLiteral)?.into_inner();
+ )(input);
+ let input = if let Some(item) = weekday {
+ let input = item.into_inner();
+ let input = comma(input).ok_or(InvalidLiteral)?.into_inner();
+ opt(cfws)(input).into_inner()
+ } else {
+ input
+ };
let ParsedItem(input, day) =
n_to_m_digits::<1, 2, _>(input).ok_or(InvalidComponent("day"))?;
let input = cfws(input).ok_or(InvalidLiteral)?.into_inner();
let ParsedItem(input, month) = first_match(
[
(b"Jan".as_slice(), Month::January),
(b"Feb".as_slice(), Month::February),
(b"Mar".as_slice(), Month::March),
(b"Apr".as_slice(), Month::April),
(b"May".as_slice(), Month::May),
@@ -410,56 +434,54 @@ impl sealed::Sealed for Rfc2822 {
let (input, offset_hour, offset_minute) = if let Some(zone_literal) = zone_literal {
let ParsedItem(input, offset_hour) = zone_literal;
(input, offset_hour, 0)
} else {
let ParsedItem(input, offset_sign) =
sign(input).ok_or(InvalidComponent("offset hour"))?;
let ParsedItem(input, offset_hour) = exactly_n_digits::<2, u8>(input)
.map(|item| {
item.map(|offset_hour| {
if offset_sign == b'-' {
- -(offset_hour as i8)
+ -offset_hour.cast_signed()
} else {
- offset_hour as _
+ offset_hour.cast_signed()
}
})
})
.ok_or(InvalidComponent("offset hour"))?;
let ParsedItem(input, offset_minute) =
exactly_n_digits::<2, u8>(input).ok_or(InvalidComponent("offset minute"))?;
- (input, offset_hour, offset_minute as i8)
+ (input, offset_hour, offset_minute.cast_signed())
};
+ let input = opt(cfws)(input).into_inner();
+
if !input.is_empty() {
- return Err(error::Parse::UnexpectedTrailingCharacters);
+ return Err(error::Parse::ParseFromDescription(
+ error::ParseFromDescription::UnexpectedTrailingCharacters,
+ ));
}
let mut nanosecond = 0;
- let leap_second_input = if !O::HAS_LOGICAL_OFFSET {
- false
- } else if second == 60 {
+ let leap_second_input = if second == 60 {
second = 59;
nanosecond = 999_999_999;
true
} else {
false
};
let dt = (|| {
- let date = Date::from_calendar_date(year as _, month, day)?;
+ let date = Date::from_calendar_date(year.cast_signed(), month, day)?;
let time = Time::from_hms_nano(hour, minute, second, nanosecond)?;
let offset = UtcOffset::from_hms(offset_hour, offset_minute, 0)?;
- Ok(DateTime {
- date,
- time,
- offset: maybe_offset_from_offset::<O>(offset),
- })
+ Ok(OffsetDateTime::new_in_offset(date, time, offset))
})()
.map_err(TryFromParsed::ComponentRange)?;
if leap_second_input && !dt.is_valid_leap_second_stand_in() {
return Err(error::Parse::TryFromParsed(TryFromParsed::ComponentRange(
error::ComponentRange {
name: "second",
minimum: 0,
maximum: 59,
value: 60,
@@ -480,21 +502,21 @@ impl sealed::Sealed for Rfc3339 {
) -> Result<&'a [u8], error::Parse> {
use crate::error::ParseFromDescription::{InvalidComponent, InvalidLiteral};
use crate::parsing::combinator::{
any_digit, ascii_char, ascii_char_ignore_case, exactly_n_digits, sign,
};
let dash = ascii_char::<b'-'>;
let colon = ascii_char::<b':'>;
let input = exactly_n_digits::<4, u32>(input)
- .and_then(|item| item.consume_value(|value| parsed.set_year(value as _)))
+ .and_then(|item| item.consume_value(|value| parsed.set_year(value.cast_signed())))
.ok_or(InvalidComponent("year"))?;
let input = dash(input).ok_or(InvalidLiteral)?.into_inner();
let input = exactly_n_digits::<2, _>(input)
.and_then(|item| item.flat_map(|value| Month::from_number(value).ok()))
.and_then(|item| item.consume_value(|value| parsed.set_month(value)))
.ok_or(InvalidComponent("month"))?;
let input = dash(input).ok_or(InvalidLiteral)?.into_inner();
let input = exactly_n_digits::<2, _>(input)
.and_then(|item| item.consume_value(|value| parsed.set_day(value)))
.ok_or(InvalidComponent("day"))?;
@@ -508,84 +530,85 @@ impl sealed::Sealed for Rfc3339 {
let input = exactly_n_digits::<2, _>(input)
.and_then(|item| item.consume_value(|value| parsed.set_minute(value)))
.ok_or(InvalidComponent("minute"))?;
let input = colon(input).ok_or(InvalidLiteral)?.into_inner();
let input = exactly_n_digits::<2, _>(input)
.and_then(|item| item.consume_value(|value| parsed.set_second(value)))
.ok_or(InvalidComponent("second"))?;
let input = if let Some(ParsedItem(input, ())) = ascii_char::<b'.'>(input) {
let ParsedItem(mut input, mut value) = any_digit(input)
.ok_or(InvalidComponent("subsecond"))?
- .map(|v| (v - b'0') as u32 * 100_000_000);
+ .map(|v| (v - b'0').extend::<u32>() * 100_000_000);
let mut multiplier = 10_000_000;
while let Some(ParsedItem(new_input, digit)) = any_digit(input) {
- value += (digit - b'0') as u32 * multiplier;
+ value += (digit - b'0').extend::<u32>() * multiplier;
input = new_input;
multiplier /= 10;
}
parsed
.set_subsecond(value)
.ok_or(InvalidComponent("subsecond"))?;
input
} else {
input
};
// The RFC explicitly allows leap seconds.
- parsed.set_flag(Parsed::LEAP_SECOND_ALLOWED_FLAG, true);
+ parsed.leap_second_allowed = true;
if let Some(ParsedItem(input, ())) = ascii_char_ignore_case::<b'Z'>(input) {
parsed
.set_offset_hour(0)
.ok_or(InvalidComponent("offset hour"))?;
parsed
.set_offset_minute_signed(0)
.ok_or(InvalidComponent("offset minute"))?;
parsed
.set_offset_second_signed(0)
.ok_or(InvalidComponent("offset second"))?;
return Ok(input);
}
let ParsedItem(input, offset_sign) = sign(input).ok_or(InvalidComponent("offset hour"))?;
let input = exactly_n_digits::<2, u8>(input)
.and_then(|item| {
- item.map(|offset_hour| {
- if offset_sign == b'-' {
- -(offset_hour as i8)
- } else {
- offset_hour as _
- }
- })
- .consume_value(|value| parsed.set_offset_hour(value))
+ item.filter(|&offset_hour| offset_hour <= 23)?
+ .map(|offset_hour| {
+ if offset_sign == b'-' {
+ -offset_hour.cast_signed()
+ } else {
+ offset_hour.cast_signed()
+ }
+ })
+ .consume_value(|value| parsed.set_offset_hour(value))
})
.ok_or(InvalidComponent("offset hour"))?;
let input = colon(input).ok_or(InvalidLiteral)?.into_inner();
let input = exactly_n_digits::<2, u8>(input)
.and_then(|item| {
item.map(|offset_minute| {
if offset_sign == b'-' {
- -(offset_minute as i8)
+ -offset_minute.cast_signed()
} else {
- offset_minute as _
+ offset_minute.cast_signed()
}
})
.consume_value(|value| parsed.set_offset_minute_signed(value))
})
.ok_or(InvalidComponent("offset minute"))?;
Ok(input)
}
- fn parse_date_time<O: MaybeOffset>(&self, input: &[u8]) -> Result<DateTime<O>, error::Parse> {
+ fn parse_offset_date_time(&self, input: &[u8]) -> Result<OffsetDateTime, error::Parse> {
use crate::error::ParseFromDescription::{InvalidComponent, InvalidLiteral};
use crate::parsing::combinator::{
any_digit, ascii_char, ascii_char_ignore_case, exactly_n_digits, sign,
};
let dash = ascii_char::<b'-'>;
let colon = ascii_char::<b':'>;
let ParsedItem(input, year) =
exactly_n_digits::<4, u32>(input).ok_or(InvalidComponent("year"))?;
@@ -603,93 +626,95 @@ impl sealed::Sealed for Rfc3339 {
let input = colon(input).ok_or(InvalidLiteral)?.into_inner();
let ParsedItem(input, minute) =
exactly_n_digits::<2, _>(input).ok_or(InvalidComponent("minute"))?;
let input = colon(input).ok_or(InvalidLiteral)?.into_inner();
let ParsedItem(input, mut second) =
exactly_n_digits::<2, _>(input).ok_or(InvalidComponent("second"))?;
let ParsedItem(input, mut nanosecond) =
if let Some(ParsedItem(input, ())) = ascii_char::<b'.'>(input) {
let ParsedItem(mut input, mut value) = any_digit(input)
.ok_or(InvalidComponent("subsecond"))?
- .map(|v| (v - b'0') as u32 * 100_000_000);
+ .map(|v| (v - b'0').extend::<u32>() * 100_000_000);
let mut multiplier = 10_000_000;
while let Some(ParsedItem(new_input, digit)) = any_digit(input) {
- value += (digit - b'0') as u32 * multiplier;
+ value += (digit - b'0').extend::<u32>() * multiplier;
input = new_input;
multiplier /= 10;
}
ParsedItem(input, value)
} else {
ParsedItem(input, 0)
};
let ParsedItem(input, offset) = {
if let Some(ParsedItem(input, ())) = ascii_char_ignore_case::<b'Z'>(input) {
ParsedItem(input, UtcOffset::UTC)
} else {
let ParsedItem(input, offset_sign) =
sign(input).ok_or(InvalidComponent("offset hour"))?;
- let ParsedItem(input, offset_hour) =
- exactly_n_digits::<2, u8>(input).ok_or(InvalidComponent("offset hour"))?;
+ let ParsedItem(input, offset_hour) = exactly_n_digits::<2, u8>(input)
+ .and_then(|parsed| parsed.filter(|&offset_hour| offset_hour <= 23))
+ .ok_or(InvalidComponent("offset hour"))?;
let input = colon(input).ok_or(InvalidLiteral)?.into_inner();
let ParsedItem(input, offset_minute) =
exactly_n_digits::<2, u8>(input).ok_or(InvalidComponent("offset minute"))?;
UtcOffset::from_hms(
if offset_sign == b'-' {
- -(offset_hour as i8)
+ -offset_hour.cast_signed()
} else {
- offset_hour as _
+ offset_hour.cast_signed()
},
if offset_sign == b'-' {
- -(offset_minute as i8)
+ -offset_minute.cast_signed()
} else {
- offset_minute as _
+ offset_minute.cast_signed()
},
0,
)
.map(|offset| ParsedItem(input, offset))
.map_err(|mut err| {
// Provide the user a more accurate error.
if err.name == "hours" {
err.name = "offset hour";
} else if err.name == "minutes" {
err.name = "offset minute";
}
err
})
.map_err(TryFromParsed::ComponentRange)?
}
};
if !input.is_empty() {
- return Err(error::Parse::UnexpectedTrailingCharacters);
+ return Err(error::Parse::ParseFromDescription(
+ error::ParseFromDescription::UnexpectedTrailingCharacters,
+ ));
}
// The RFC explicitly permits leap seconds. We don't currently support them, so treat it as
// the preceding nanosecond. However, leap seconds can only occur as the last second of the
// month UTC.
let leap_second_input = if second == 60 {
second = 59;
nanosecond = 999_999_999;
true
} else {
false
};
let date = Month::from_number(month)
- .and_then(|month| Date::from_calendar_date(year as _, month, day))
+ .and_then(|month| Date::from_calendar_date(year.cast_signed(), month, day))
.map_err(TryFromParsed::ComponentRange)?;
let time = Time::from_hms_nano(hour, minute, second, nanosecond)
.map_err(TryFromParsed::ComponentRange)?;
- let offset = maybe_offset_from_offset::<O>(offset);
- let dt = DateTime { date, time, offset };
+ let dt = OffsetDateTime::new_in_offset(date, time, offset);
if leap_second_input && !dt.is_valid_leap_second_stand_in() {
return Err(error::Parse::TryFromParsed(TryFromParsed::ComponentRange(
error::ComponentRange {
name: "second",
minimum: 0,
maximum: 59,
value: 60,
conditional_range: true,
},
@@ -707,20 +732,22 @@ impl<const CONFIG: EncodedConfig> sealed::Sealed for Iso8601<CONFIG> {
parsed: &mut Parsed,
) -> Result<&'a [u8], error::Parse> {
use crate::parsing::combinator::rfc::iso8601::ExtendedKind;
let mut extended_kind = ExtendedKind::Unknown;
let mut date_is_present = false;
let mut time_is_present = false;
let mut offset_is_present = false;
let mut first_error = None;
+ parsed.leap_second_allowed = true;
+
match Self::parse_date(parsed, &mut extended_kind)(input) {
Ok(new_input) => {
input = new_input;
date_is_present = true;
}
Err(err) => {
first_error.get_or_insert(err);
}
}
diff --git a/third_party/rust/time/src/parsing/parsed.rs b/third_party/rust/time/src/parsing/parsed.rs
index 04c74a7202..3c6c4f5119 100644
--- a/third_party/rust/time/src/parsing/parsed.rs
+++ b/third_party/rust/time/src/parsing/parsed.rs
@@ -1,23 +1,29 @@
//! Information parsed from an input and format description.
-use core::mem::MaybeUninit;
use core::num::{NonZeroU16, NonZeroU8};
-use crate::date_time::{maybe_offset_from_offset, offset_kind, DateTime, MaybeOffset};
+use deranged::{
+ OptionRangedI128, OptionRangedI32, OptionRangedI8, OptionRangedU16, OptionRangedU32,
+ OptionRangedU8, RangedI128, RangedI32, RangedI8, RangedU16, RangedU32, RangedU8,
+};
+use num_conv::prelude::*;
+
+use crate::convert::{Day, Hour, Minute, Nanosecond, Second};
+use crate::date::{MAX_YEAR, MIN_YEAR};
use crate::error::TryFromParsed::InsufficientInformation;
-use crate::format_description::modifier::{WeekNumberRepr, YearRepr};
#[cfg(feature = "alloc")]
use crate::format_description::OwnedFormatItem;
-use crate::format_description::{Component, FormatItem};
+use crate::format_description::{modifier, BorrowedFormatItem, Component};
+use crate::internal_macros::{bug, const_try_opt};
use crate::parsing::component::{
- parse_day, parse_hour, parse_ignore, parse_minute, parse_month, parse_offset_hour,
+ parse_day, parse_end, parse_hour, parse_ignore, parse_minute, parse_month, parse_offset_hour,
parse_offset_minute, parse_offset_second, parse_ordinal, parse_period, parse_second,
parse_subsecond, parse_unix_timestamp, parse_week_number, parse_weekday, parse_year, Period,
};
use crate::parsing::ParsedItem;
use crate::{error, Date, Month, OffsetDateTime, PrimitiveDateTime, Time, UtcOffset, Weekday};
/// Sealed to prevent downstream implementations.
mod sealed {
use super::*;
@@ -25,21 +31,21 @@ mod sealed {
pub trait AnyFormatItem {
/// Parse a single item, returning the remaining input on success.
fn parse_item<'a>(
&self,
parsed: &mut Parsed,
input: &'a [u8],
) -> Result<&'a [u8], error::ParseFromDescription>;
}
}
-impl sealed::AnyFormatItem for FormatItem<'_> {
+impl sealed::AnyFormatItem for BorrowedFormatItem<'_> {
fn parse_item<'a>(
&self,
parsed: &mut Parsed,
input: &'a [u8],
) -> Result<&'a [u8], error::ParseFromDescription> {
match self {
Self::Literal(literal) => Parsed::parse_literal(input, literal),
Self::Component(component) => parsed.parse_component(input, *component),
Self::Compound(compound) => parsed.parse_items(input, compound),
Self::Optional(item) => parsed.parse_item(input, *item).or(Ok(input)),
@@ -92,153 +98,146 @@ impl sealed::AnyFormatItem for OwnedFormatItem {
Some(err) => Err(err),
// This location will be reached if the slice is empty, skipping the `for` loop.
// As this case is expected to be uncommon, there's no need to check up front.
None => Ok(input),
}
}
}
}
}
-/// The type of the `flags` field in [`Parsed`]. Allows for changing a single location and having it
-/// effect all uses.
-type Flag = u32;
-
/// All information parsed.
///
/// This information is directly used to construct the final values.
///
/// Most users will not need think about this struct in any way. It is public to allow for manual
/// control over values, in the instance that the default parser is insufficient.
#[derive(Debug, Clone, Copy)]
pub struct Parsed {
- /// Bitflags indicating whether a particular field is present.
- flags: Flag,
/// Calendar year.
- year: MaybeUninit<i32>,
+ year: OptionRangedI32<{ MIN_YEAR }, { MAX_YEAR }>,
/// The last two digits of the calendar year.
- year_last_two: MaybeUninit<u8>,
+ year_last_two: OptionRangedU8<0, 99>,
/// Year of the [ISO week date](https://en.wikipedia.org/wiki/ISO_week_date).
- iso_year: MaybeUninit<i32>,
+ iso_year: OptionRangedI32<{ MIN_YEAR }, { MAX_YEAR }>,
/// The last two digits of the ISO week year.
- iso_year_last_two: MaybeUninit<u8>,
+ iso_year_last_two: OptionRangedU8<0, 99>,
/// Month of the year.
month: Option<Month>,
/// Week of the year, where week one begins on the first Sunday of the calendar year.
- sunday_week_number: MaybeUninit<u8>,
+ sunday_week_number: OptionRangedU8<0, 53>,
/// Week of the year, where week one begins on the first Monday of the calendar year.
- monday_week_number: MaybeUninit<u8>,
+ monday_week_number: OptionRangedU8<0, 53>,
/// Week of the year, where week one is the Monday-to-Sunday period containing January 4.
- iso_week_number: Option<NonZeroU8>,
+ iso_week_number: OptionRangedU8<1, 53>,
/// Day of the week.
weekday: Option<Weekday>,
/// Day of the year.
- ordinal: Option<NonZeroU16>,
+ ordinal: OptionRangedU16<1, 366>,
/// Day of the month.
- day: Option<NonZeroU8>,
+ day: OptionRangedU8<1, 31>,
/// Hour within the day.
- hour_24: MaybeUninit<u8>,
+ hour_24: OptionRangedU8<0, { Hour::per(Day) - 1 }>,
/// Hour within the 12-hour period (midnight to noon or vice versa). This is typically used in
/// conjunction with AM/PM, which is indicated by the `hour_12_is_pm` field.
- hour_12: Option<NonZeroU8>,
+ hour_12: OptionRangedU8<1, 12>,
/// Whether the `hour_12` field indicates a time that "PM".
hour_12_is_pm: Option<bool>,
/// Minute within the hour.
- minute: MaybeUninit<u8>,
+ // minute: MaybeUninit<u8>,
+ minute: OptionRangedU8<0, { Minute::per(Hour) - 1 }>,
/// Second within the minute.
- second: MaybeUninit<u8>,
+ // do not subtract one, as leap seconds may be allowed
+ second: OptionRangedU8<0, { Second::per(Minute) }>,
/// Nanosecond within the second.
- subsecond: MaybeUninit<u32>,
+ subsecond: OptionRangedU32<0, { Nanosecond::per(Second) - 1 }>,
/// Whole hours of the UTC offset.
- offset_hour: MaybeUninit<i8>,
+ offset_hour: OptionRangedI8<-23, 23>,
/// Minutes within the hour of the UTC offset.
- offset_minute: MaybeUninit<i8>,
+ offset_minute:
+ OptionRangedI8<{ -((Minute::per(Hour) - 1) as i8) }, { (Minute::per(Hour) - 1) as _ }>,
/// Seconds within the minute of the UTC offset.
- offset_second: MaybeUninit<i8>,
+ offset_second:
+ OptionRangedI8<{ -((Second::per(Minute) - 1) as i8) }, { (Second::per(Minute) - 1) as _ }>,
/// The Unix timestamp in nanoseconds.
- unix_timestamp_nanos: MaybeUninit<i128>,
-}
-
-#[allow(clippy::missing_docs_in_private_items)]
-impl Parsed {
- const YEAR_FLAG: Flag = 1 << 0;
- const YEAR_LAST_TWO_FLAG: Flag = 1 << 1;
- const ISO_YEAR_FLAG: Flag = 1 << 2;
- const ISO_YEAR_LAST_TWO_FLAG: Flag = 1 << 3;
- const SUNDAY_WEEK_NUMBER_FLAG: Flag = 1 << 4;
- const MONDAY_WEEK_NUMBER_FLAG: Flag = 1 << 5;
- const HOUR_24_FLAG: Flag = 1 << 6;
- const MINUTE_FLAG: Flag = 1 << 7;
- const SECOND_FLAG: Flag = 1 << 8;
- const SUBSECOND_FLAG: Flag = 1 << 9;
- const OFFSET_HOUR_FLAG: Flag = 1 << 10;
- const OFFSET_MINUTE_FLAG: Flag = 1 << 11;
- const OFFSET_SECOND_FLAG: Flag = 1 << 12;
+ unix_timestamp_nanos: OptionRangedI128<
+ {
+ OffsetDateTime::new_in_offset(Date::MIN, Time::MIDNIGHT, UtcOffset::UTC)
+ .unix_timestamp_nanos()
+ },
+ {
+ OffsetDateTime::new_in_offset(Date::MAX, Time::MAX, UtcOffset::UTC)
+ .unix_timestamp_nanos()
+ },
+ >,
+ /// Indicates whether the [`UtcOffset`] is negative. This information is obtained when parsing
+ /// the offset hour, but may not otherwise be stored due to "-0" being equivalent to "0".
+ offset_is_negative: Option<bool>,
/// Indicates whether a leap second is permitted to be parsed. This is required by some
/// well-known formats.
- pub(super) const LEAP_SECOND_ALLOWED_FLAG: Flag = 1 << 13;
- /// Indicates whether the `UtcOffset` is negative. This information is obtained when parsing the
- /// offset hour, but may not otherwise be stored due to "-0" being equivalent to "0".
- const OFFSET_IS_NEGATIVE_FLAG: Flag = 1 << 14;
- /// Does the value at `OFFSET_IS_NEGATIVE_FLAG` have any semantic meaning, or is it just the
- /// default value? If the latter, the value should be considered to have no meaning.
- const OFFSET_IS_NEGATIVE_FLAG_IS_INITIALIZED: Flag = 1 << 15;
- const UNIX_TIMESTAMP_NANOS_FLAG: Flag = 1 << 16;
+ pub(super) leap_second_allowed: bool,
+}
+
+impl Default for Parsed {
+ fn default() -> Self {
+ Self::new()
+ }
}
impl Parsed {
/// Create a new instance of `Parsed` with no information known.
pub const fn new() -> Self {
Self {
- flags: 0,
- year: MaybeUninit::uninit(),
- year_last_two: MaybeUninit::uninit(),
- iso_year: MaybeUninit::uninit(),
- iso_year_last_two: MaybeUninit::uninit(),
+ year: OptionRangedI32::None,
+ year_last_two: OptionRangedU8::None,
+ iso_year: OptionRangedI32::None,
+ iso_year_last_two: OptionRangedU8::None,
month: None,
- sunday_week_number: MaybeUninit::uninit(),
- monday_week_number: MaybeUninit::uninit(),
- iso_week_number: None,
+ sunday_week_number: OptionRangedU8::None,
+ monday_week_number: OptionRangedU8::None,
+ iso_week_number: OptionRangedU8::None,
weekday: None,
- ordinal: None,
- day: None,
- hour_24: MaybeUninit::uninit(),
- hour_12: None,
+ ordinal: OptionRangedU16::None,
+ day: OptionRangedU8::None,
+ hour_24: OptionRangedU8::None,
+ hour_12: OptionRangedU8::None,
hour_12_is_pm: None,
- minute: MaybeUninit::uninit(),
- second: MaybeUninit::uninit(),
- subsecond: MaybeUninit::uninit(),
- offset_hour: MaybeUninit::uninit(),
- offset_minute: MaybeUninit::uninit(),
- offset_second: MaybeUninit::uninit(),
- unix_timestamp_nanos: MaybeUninit::uninit(),
+ minute: OptionRangedU8::None,
+ second: OptionRangedU8::None,
+ subsecond: OptionRangedU32::None,
+ offset_hour: OptionRangedI8::None,
+ offset_minute: OptionRangedI8::None,
+ offset_second: OptionRangedI8::None,
+ unix_timestamp_nanos: OptionRangedI128::None,
+ offset_is_negative: None,
+ leap_second_allowed: false,
}
}
- /// Parse a single [`FormatItem`] or [`OwnedFormatItem`], mutating the struct. The remaining
- /// input is returned as the `Ok` value.
+ /// Parse a single [`BorrowedFormatItem`] or [`OwnedFormatItem`], mutating the struct. The
+ /// remaining input is returned as the `Ok` value.
///
- /// If a [`FormatItem::Optional`] or [`OwnedFormatItem::Optional`] is passed, parsing will not
- /// fail; the input will be returned as-is if the expected format is not present.
+ /// If a [`BorrowedFormatItem::Optional`] or [`OwnedFormatItem::Optional`] is passed, parsing
+ /// will not fail; the input will be returned as-is if the expected format is not present.
pub fn parse_item<'a>(
&mut self,
input: &'a [u8],
item: &impl sealed::AnyFormatItem,
) -> Result<&'a [u8], error::ParseFromDescription> {
item.parse_item(self, input)
}
- /// Parse a sequence of [`FormatItem`]s or [`OwnedFormatItem`]s, mutating the struct. The
- /// remaining input is returned as the `Ok` value.
+ /// Parse a sequence of [`BorrowedFormatItem`]s or [`OwnedFormatItem`]s, mutating the struct.
+ /// The remaining input is returned as the `Ok` value.
///
- /// This method will fail if any of the contained [`FormatItem`]s or [`OwnedFormatItem`]s fail
- /// to parse. `self` will not be mutated in this instance.
+ /// This method will fail if any of the contained [`BorrowedFormatItem`]s or
+ /// [`OwnedFormatItem`]s fail to parse. `self` will not be mutated in this instance.
pub fn parse_items<'a>(
&mut self,
mut input: &'a [u8],
items: &[impl sealed::AnyFormatItem],
) -> Result<&'a [u8], error::ParseFromDescription> {
// Make a copy that we can mutate. It will only be set to the user's copy if everything
// succeeds.
let mut this = *self;
for item in items {
input = this.parse_item(input, item)?;
@@ -276,37 +275,41 @@ impl Parsed {
Component::Ordinal(modifiers) => parse_ordinal(input, modifiers)
.and_then(|parsed| parsed.consume_value(|value| self.set_ordinal(value)))
.ok_or(InvalidComponent("ordinal")),
Component::Weekday(modifiers) => parse_weekday(input, modifiers)
.and_then(|parsed| parsed.consume_value(|value| self.set_weekday(value)))
.ok_or(InvalidComponent("weekday")),
Component::WeekNumber(modifiers) => {
let ParsedItem(remaining, value) =
parse_week_number(input, modifiers).ok_or(InvalidComponent("week number"))?;
match modifiers.repr {
- WeekNumberRepr::Iso => {
+ modifier::WeekNumberRepr::Iso => {
NonZeroU8::new(value).and_then(|value| self.set_iso_week_number(value))
}
- WeekNumberRepr::Sunday => self.set_sunday_week_number(value),
- WeekNumberRepr::Monday => self.set_monday_week_number(value),
+ modifier::WeekNumberRepr::Sunday => self.set_sunday_week_number(value),
+ modifier::WeekNumberRepr::Monday => self.set_monday_week_number(value),
}
.ok_or(InvalidComponent("week number"))?;
Ok(remaining)
}
Component::Year(modifiers) => {
let ParsedItem(remaining, value) =
parse_year(input, modifiers).ok_or(InvalidComponent("year"))?;
match (modifiers.iso_week_based, modifiers.repr) {
- (false, YearRepr::Full) => self.set_year(value),
- (false, YearRepr::LastTwo) => self.set_year_last_two(value as _),
- (true, YearRepr::Full) => self.set_iso_year(value),
- (true, YearRepr::LastTwo) => self.set_iso_year_last_two(value as _),
+ (false, modifier::YearRepr::Full) => self.set_year(value),
+ (false, modifier::YearRepr::LastTwo) => {
+ self.set_year_last_two(value.cast_unsigned().truncate())
+ }
+ (true, modifier::YearRepr::Full) => self.set_iso_year(value),
+ (true, modifier::YearRepr::LastTwo) => {
+ self.set_iso_year_last_two(value.cast_unsigned().truncate())
+ }
}
.ok_or(InvalidComponent("year"))?;
Ok(remaining)
}
Component::Hour(modifiers) => {
let ParsedItem(remaining, value) =
parse_hour(input, modifiers).ok_or(InvalidComponent("hour"))?;
if modifiers.is_12_hour_clock {
NonZeroU8::new(value).and_then(|value| self.set_hour_12(value))
} else {
@@ -325,23 +328,23 @@ impl Parsed {
.ok_or(InvalidComponent("period")),
Component::Second(modifiers) => parse_second(input, modifiers)
.and_then(|parsed| parsed.consume_value(|value| self.set_second(value)))
.ok_or(InvalidComponent("second")),
Component::Subsecond(modifiers) => parse_subsecond(input, modifiers)
.and_then(|parsed| parsed.consume_value(|value| self.set_subsecond(value)))
.ok_or(InvalidComponent("subsecond")),
Component::OffsetHour(modifiers) => parse_offset_hour(input, modifiers)
.and_then(|parsed| {
parsed.consume_value(|(value, is_negative)| {
- self.set_flag(Self::OFFSET_IS_NEGATIVE_FLAG_IS_INITIALIZED, true);
- self.set_flag(Self::OFFSET_IS_NEGATIVE_FLAG, is_negative);
- self.set_offset_hour(value)
+ self.set_offset_hour(value)?;
+ self.offset_is_negative = Some(is_negative);
+ Some(())
})
})
.ok_or(InvalidComponent("offset hour")),
Component::OffsetMinute(modifiers) => parse_offset_minute(input, modifiers)
.and_then(|parsed| {
parsed.consume_value(|value| self.set_offset_minute_signed(value))
})
.ok_or(InvalidComponent("offset minute")),
Component::OffsetSecond(modifiers) => parse_offset_second(input, modifiers)
.and_then(|parsed| {
@@ -349,321 +352,382 @@ impl Parsed {
})
.ok_or(InvalidComponent("offset second")),
Component::Ignore(modifiers) => parse_ignore(input, modifiers)
.map(ParsedItem::<()>::into_inner)
.ok_or(InvalidComponent("ignore")),
Component::UnixTimestamp(modifiers) => parse_unix_timestamp(input, modifiers)
.and_then(|parsed| {
parsed.consume_value(|value| self.set_unix_timestamp_nanos(value))
})
.ok_or(InvalidComponent("unix_timestamp")),
+ Component::End(modifiers) => parse_end(input, modifiers)
+ .map(ParsedItem::<()>::into_inner)
+ .ok_or(error::ParseFromDescription::UnexpectedTrailingCharacters),
}
}
+}
- /// Get the value of the provided flag.
- const fn get_flag(&self, flag: Flag) -> bool {
- self.flags & flag == flag
+/// Getter methods
+impl Parsed {
+ /// Obtain the `year` component.
+ pub const fn year(&self) -> Option<i32> {
+ self.year.get_primitive()
}
- /// Set the value of the provided flag.
- pub(super) fn set_flag(&mut self, flag: Flag, value: bool) {
- if value {
- self.flags |= flag;
- } else {
- self.flags &= !flag;
- }
+ /// Obtain the `year_last_two` component.
+ pub const fn year_last_two(&self) -> Option<u8> {
+ self.year_last_two.get_primitive()
}
-}
-/// Generate getters for each of the fields.
-macro_rules! getters {
- ($($(@$flag:ident)? $name:ident: $ty:ty),+ $(,)?) => {$(
- getters!(! $(@$flag)? $name: $ty);
- )*};
- (! $name:ident : $ty:ty) => {
- /// Obtain the named component.
- pub const fn $name(&self) -> Option<$ty> {
- self.$name
- }
- };
- (! @$flag:ident $name:ident : $ty:ty) => {
- /// Obtain the named component.
- pub const fn $name(&self) -> Option<$ty> {
- if !self.get_flag(Self::$flag) {
- None
- } else {
- // SAFETY: We just checked if the field is present.
- Some(unsafe { self.$name.assume_init() })
- }
- }
- };
-}
+ /// Obtain the `iso_year` component.
+ pub const fn iso_year(&self) -> Option<i32> {
+ self.iso_year.get_primitive()
+ }
-/// Getter methods
-impl Parsed {
- getters! {
- @YEAR_FLAG year: i32,
- @YEAR_LAST_TWO_FLAG year_last_two: u8,
- @ISO_YEAR_FLAG iso_year: i32,
- @ISO_YEAR_LAST_TWO_FLAG iso_year_last_two: u8,
- month: Month,
- @SUNDAY_WEEK_NUMBER_FLAG sunday_week_number: u8,
- @MONDAY_WEEK_NUMBER_FLAG monday_week_number: u8,
- iso_week_number: NonZeroU8,
- weekday: Weekday,
- ordinal: NonZeroU16,
- day: NonZeroU8,
- @HOUR_24_FLAG hour_24: u8,
- hour_12: NonZeroU8,
- hour_12_is_pm: bool,
- @MINUTE_FLAG minute: u8,
- @SECOND_FLAG second: u8,
- @SUBSECOND_FLAG subsecond: u32,
- @OFFSET_HOUR_FLAG offset_hour: i8,
- @UNIX_TIMESTAMP_NANOS_FLAG unix_timestamp_nanos: i128,
- }
-
- /// Obtain the absolute value of the offset minute.
+ /// Obtain the `iso_year_last_two` component.
+ pub const fn iso_year_last_two(&self) -> Option<u8> {
+ self.iso_year_last_two.get_primitive()
+ }
+
+ /// Obtain the `month` component.
+ pub const fn month(&self) -> Option<Month> {
+ self.month
+ }
+
+ /// Obtain the `sunday_week_number` component.
+ pub const fn sunday_week_number(&self) -> Option<u8> {
+ self.sunday_week_number.get_primitive()
+ }
+
+ /// Obtain the `monday_week_number` component.
+ pub const fn monday_week_number(&self) -> Option<u8> {
+ self.monday_week_number.get_primitive()
+ }
+
+ /// Obtain the `iso_week_number` component.
+ pub const fn iso_week_number(&self) -> Option<NonZeroU8> {
+ NonZeroU8::new(const_try_opt!(self.iso_week_number.get_primitive()))
+ }
+
+ /// Obtain the `weekday` component.
+ pub const fn weekday(&self) -> Option<Weekday> {
+ self.weekday
+ }
+
+ /// Obtain the `ordinal` component.
+ pub const fn ordinal(&self) -> Option<NonZeroU16> {
+ NonZeroU16::new(const_try_opt!(self.ordinal.get_primitive()))
+ }
+
+ /// Obtain the `day` component.
+ pub const fn day(&self) -> Option<NonZeroU8> {
+ NonZeroU8::new(const_try_opt!(self.day.get_primitive()))
+ }
+
+ /// Obtain the `hour_24` component.
+ pub const fn hour_24(&self) -> Option<u8> {
+ self.hour_24.get_primitive()
+ }
+
+ /// Obtain the `hour_12` component.
+ pub const fn hour_12(&self) -> Option<NonZeroU8> {
+ NonZeroU8::new(const_try_opt!(self.hour_12.get_primitive()))
+ }
+
+ /// Obtain the `hour_12_is_pm` component.
+ pub const fn hour_12_is_pm(&self) -> Option<bool> {
+ self.hour_12_is_pm
+ }
+
+ /// Obtain the `minute` component.
+ pub const fn minute(&self) -> Option<u8> {
+ self.minute.get_primitive()
+ }
+
+ /// Obtain the `second` component.
+ pub const fn second(&self) -> Option<u8> {
+ self.second.get_primitive()
+ }
+
+ /// Obtain the `subsecond` component.
+ pub const fn subsecond(&self) -> Option<u32> {
+ self.subsecond.get_primitive()
+ }
+
+ /// Obtain the `offset_hour` component.
+ pub const fn offset_hour(&self) -> Option<i8> {
+ self.offset_hour.get_primitive()
+ }
+
+ /// Obtain the absolute value of the `offset_minute` component.
#[doc(hidden)]
#[deprecated(since = "0.3.8", note = "use `parsed.offset_minute_signed()` instead")]
pub const fn offset_minute(&self) -> Option<u8> {
Some(const_try_opt!(self.offset_minute_signed()).unsigned_abs())
}
- /// Obtain the offset minute as an `i8`.
+ /// Obtain the `offset_minute` component.
pub const fn offset_minute_signed(&self) -> Option<i8> {
- if !self.get_flag(Self::OFFSET_MINUTE_FLAG) {
- None
- } else {
- // SAFETY: We just checked if the field is present.
- let value = unsafe { self.offset_minute.assume_init() };
-
- if self.get_flag(Self::OFFSET_IS_NEGATIVE_FLAG_IS_INITIALIZED)
- && (value.is_negative() != self.get_flag(Self::OFFSET_IS_NEGATIVE_FLAG))
- {
- Some(-value)
- } else {
- Some(value)
- }
+ match (self.offset_minute.get_primitive(), self.offset_is_negative) {
+ (Some(offset_minute), Some(true)) => Some(-offset_minute),
+ (Some(offset_minute), _) => Some(offset_minute),
+ (None, _) => None,
}
}
- /// Obtain the absolute value of the offset second.
+ /// Obtain the absolute value of the `offset_second` component.
#[doc(hidden)]
#[deprecated(since = "0.3.8", note = "use `parsed.offset_second_signed()` instead")]
pub const fn offset_second(&self) -> Option<u8> {
Some(const_try_opt!(self.offset_second_signed()).unsigned_abs())
}
- /// Obtain the offset second as an `i8`.
+ /// Obtain the `offset_second` component.
pub const fn offset_second_signed(&self) -> Option<i8> {
- if !self.get_flag(Self::OFFSET_SECOND_FLAG) {
- None
- } else {
- // SAFETY: We just checked if the field is present.
- let value = unsafe { self.offset_second.assume_init() };
-
- if self.get_flag(Self::OFFSET_IS_NEGATIVE_FLAG_IS_INITIALIZED)
- && (value.is_negative() != self.get_flag(Self::OFFSET_IS_NEGATIVE_FLAG))
- {
- Some(-value)
- } else {
- Some(value)
- }
+ match (self.offset_second.get_primitive(), self.offset_is_negative) {
+ (Some(offset_second), Some(true)) => Some(-offset_second),
+ (Some(offset_second), _) => Some(offset_second),
+ (None, _) => None,
}
}
+
+ /// Obtain the `unix_timestamp_nanos` component.
+ pub const fn unix_timestamp_nanos(&self) -> Option<i128> {
+ self.unix_timestamp_nanos.get_primitive()
+ }
}
-/// Generate setters for each of the fields.
-///
-/// This macro should only be used for fields where the value is not validated beyond its type.
+/// Generate setters based on the builders.
macro_rules! setters {
- ($($(@$flag:ident)? $setter_name:ident $name:ident: $ty:ty),+ $(,)?) => {$(
- setters!(! $(@$flag)? $setter_name $name: $ty);
- )*};
- (! $setter_name:ident $name:ident : $ty:ty) => {
- /// Set the named component.
- pub fn $setter_name(&mut self, value: $ty) -> Option<()> {
- self.$name = Some(value);
- Some(())
- }
- };
- (! @$flag:ident $setter_name:ident $name:ident : $ty:ty) => {
- /// Set the named component.
- pub fn $setter_name(&mut self, value: $ty) -> Option<()> {
- self.$name = MaybeUninit::new(value);
- self.set_flag(Self::$flag, true);
+ ($($name:ident $setter:ident $builder:ident $type:ty;)*) => {$(
+ #[doc = concat!("Set the `", stringify!($setter), "` component.")]
+ pub fn $setter(&mut self, value: $type) -> Option<()> {
+ *self = self.$builder(value)?;
Some(())
}
- };
+ )*};
}
/// Setter methods
///
/// All setters return `Option<()>`, which is `Some` if the value was set, and `None` if not. The
/// setters _may_ fail if the value is invalid, though behavior is not guaranteed.
impl Parsed {
setters! {
- @YEAR_FLAG set_year year: i32,
- @YEAR_LAST_TWO_FLAG set_year_last_two year_last_two: u8,
- @ISO_YEAR_FLAG set_iso_year iso_year: i32,
- @ISO_YEAR_LAST_TWO_FLAG set_iso_year_last_two iso_year_last_two: u8,
- set_month month: Month,
- @SUNDAY_WEEK_NUMBER_FLAG set_sunday_week_number sunday_week_number: u8,
- @MONDAY_WEEK_NUMBER_FLAG set_monday_week_number monday_week_number: u8,
- set_iso_week_number iso_week_number: NonZeroU8,
- set_weekday weekday: Weekday,
- set_ordinal ordinal: NonZeroU16,
- set_day day: NonZeroU8,
- @HOUR_24_FLAG set_hour_24 hour_24: u8,
- set_hour_12 hour_12: NonZeroU8,
- set_hour_12_is_pm hour_12_is_pm: bool,
- @MINUTE_FLAG set_minute minute: u8,
- @SECOND_FLAG set_second second: u8,
- @SUBSECOND_FLAG set_subsecond subsecond: u32,
- @OFFSET_HOUR_FLAG set_offset_hour offset_hour: i8,
- @UNIX_TIMESTAMP_NANOS_FLAG set_unix_timestamp_nanos unix_timestamp_nanos: i128,
- }
-
- /// Set the named component.
+ year set_year with_year i32;
+ year_last_two set_year_last_two with_year_last_two u8;
+ iso_year set_iso_year with_iso_year i32;
+ iso_year_last_two set_iso_year_last_two with_iso_year_last_two u8;
+ month set_month with_month Month;
+ sunday_week_number set_sunday_week_number with_sunday_week_number u8;
+ monday_week_number set_monday_week_number with_monday_week_number u8;
+ iso_week_number set_iso_week_number with_iso_week_number NonZeroU8;
+ weekday set_weekday with_weekday Weekday;
+ ordinal set_ordinal with_ordinal NonZeroU16;
+ day set_day with_day NonZeroU8;
+ hour_24 set_hour_24 with_hour_24 u8;
+ hour_12 set_hour_12 with_hour_12 NonZeroU8;
+ hour_12_is_pm set_hour_12_is_pm with_hour_12_is_pm bool;
+ minute set_minute with_minute u8;
+ second set_second with_second u8;
+ subsecond set_subsecond with_subsecond u32;
+ offset_hour set_offset_hour with_offset_hour i8;
+ offset_minute set_offset_minute_signed with_offset_minute_signed i8;
+ offset_second set_offset_second_signed with_offset_second_signed i8;
+ unix_timestamp_nanos set_unix_timestamp_nanos with_unix_timestamp_nanos i128;
+ }
+
+ /// Set the `offset_minute` component.
#[doc(hidden)]
#[deprecated(
since = "0.3.8",
note = "use `parsed.set_offset_minute_signed()` instead"
)]
pub fn set_offset_minute(&mut self, value: u8) -> Option<()> {
- if value > i8::MAX as u8 {
+ if value > i8::MAX.cast_unsigned() {
None
} else {
- self.set_offset_minute_signed(value as _)
+ self.set_offset_minute_signed(value.cast_signed())
}
}
/// Set the `offset_minute` component.
- pub fn set_offset_minute_signed(&mut self, value: i8) -> Option<()> {
- self.offset_minute = MaybeUninit::new(value);
- self.set_flag(Self::OFFSET_MINUTE_FLAG, true);
- Some(())
- }
-
- /// Set the named component.
#[doc(hidden)]
#[deprecated(
since = "0.3.8",
note = "use `parsed.set_offset_second_signed()` instead"
)]
pub fn set_offset_second(&mut self, value: u8) -> Option<()> {
- if value > i8::MAX as u8 {
+ if value > i8::MAX.cast_unsigned() {
None
} else {
- self.set_offset_second_signed(value as _)
+ self.set_offset_second_signed(value.cast_signed())
}
}
-
- /// Set the `offset_second` component.
- pub fn set_offset_second_signed(&mut self, value: i8) -> Option<()> {
- self.offset_second = MaybeUninit::new(value);
- self.set_flag(Self::OFFSET_SECOND_FLAG, true);
- Some(())
- }
-}
-
-/// Generate build methods for each of the fields.
-///
-/// This macro should only be used for fields where the value is not validated beyond its type.
-macro_rules! builders {
- ($($(@$flag:ident)? $builder_name:ident $name:ident: $ty:ty),+ $(,)?) => {$(
- builders!(! $(@$flag)? $builder_name $name: $ty);
- )*};
- (! $builder_name:ident $name:ident : $ty:ty) => {
- /// Set the named component and return `self`.
- pub const fn $builder_name(mut self, value: $ty) -> Option<Self> {
- self.$name = Some(value);
- Some(self)
- }
- };
- (! @$flag:ident $builder_name:ident $name:ident : $ty:ty) => {
- /// Set the named component and return `self`.
- pub const fn $builder_name(mut self, value: $ty) -> Option<Self> {
- self.$name = MaybeUninit::new(value);
- self.flags |= Self::$flag;
- Some(self)
- }
- };
}
/// Builder methods
///
/// All builder methods return `Option<Self>`, which is `Some` if the value was set, and `None` if
/// not. The builder methods _may_ fail if the value is invalid, though behavior is not guaranteed.
impl Parsed {
- builders! {
- @YEAR_FLAG with_year year: i32,
- @YEAR_LAST_TWO_FLAG with_year_last_two year_last_two: u8,
- @ISO_YEAR_FLAG with_iso_year iso_year: i32,
- @ISO_YEAR_LAST_TWO_FLAG with_iso_year_last_two iso_year_last_two: u8,
- with_month month: Month,
- @SUNDAY_WEEK_NUMBER_FLAG with_sunday_week_number sunday_week_number: u8,
- @MONDAY_WEEK_NUMBER_FLAG with_monday_week_number monday_week_number: u8,
- with_iso_week_number iso_week_number: NonZeroU8,
- with_weekday weekday: Weekday,
- with_ordinal ordinal: NonZeroU16,
- with_day day: NonZeroU8,
- @HOUR_24_FLAG with_hour_24 hour_24: u8,
- with_hour_12 hour_12: NonZeroU8,
- with_hour_12_is_pm hour_12_is_pm: bool,
- @MINUTE_FLAG with_minute minute: u8,
- @SECOND_FLAG with_second second: u8,
- @SUBSECOND_FLAG with_subsecond subsecond: u32,
- @OFFSET_HOUR_FLAG with_offset_hour offset_hour: i8,
- @UNIX_TIMESTAMP_NANOS_FLAG with_unix_timestamp_nanos unix_timestamp_nanos: i128,
- }
-
- /// Set the named component and return `self`.
+ /// Set the `year` component and return `self`.
+ pub const fn with_year(mut self, value: i32) -> Option<Self> {
+ self.year = OptionRangedI32::Some(const_try_opt!(RangedI32::new(value)));
+ Some(self)
+ }
+
+ /// Set the `year_last_two` component and return `self`.
+ pub const fn with_year_last_two(mut self, value: u8) -> Option<Self> {
+ self.year_last_two = OptionRangedU8::Some(const_try_opt!(RangedU8::new(value)));
+ Some(self)
+ }
+
+ /// Set the `iso_year` component and return `self`.
+ pub const fn with_iso_year(mut self, value: i32) -> Option<Self> {
+ self.iso_year = OptionRangedI32::Some(const_try_opt!(RangedI32::new(value)));
+ Some(self)
+ }
+
+ /// Set the `iso_year_last_two` component and return `self`.
+ pub const fn with_iso_year_last_two(mut self, value: u8) -> Option<Self> {
+ self.iso_year_last_two = OptionRangedU8::Some(const_try_opt!(RangedU8::new(value)));
+ Some(self)
+ }
+
+ /// Set the `month` component and return `self`.
+ pub const fn with_month(mut self, value: Month) -> Option<Self> {
+ self.month = Some(value);
+ Some(self)
+ }
+
+ /// Set the `sunday_week_number` component and return `self`.
+ pub const fn with_sunday_week_number(mut self, value: u8) -> Option<Self> {
+ self.sunday_week_number = OptionRangedU8::Some(const_try_opt!(RangedU8::new(value)));
+ Some(self)
+ }
+
+ /// Set the `monday_week_number` component and return `self`.
+ pub const fn with_monday_week_number(mut self, value: u8) -> Option<Self> {
+ self.monday_week_number = OptionRangedU8::Some(const_try_opt!(RangedU8::new(value)));
+ Some(self)
+ }
+
+ /// Set the `iso_week_number` component and return `self`.
+ pub const fn with_iso_week_number(mut self, value: NonZeroU8) -> Option<Self> {
+ self.iso_week_number = OptionRangedU8::Some(const_try_opt!(RangedU8::new(value.get())));
+ Some(self)
+ }
+
+ /// Set the `weekday` component and return `self`.
+ pub const fn with_weekday(mut self, value: Weekday) -> Option<Self> {
+ self.weekday = Some(value);
+ Some(self)
+ }
+
+ /// Set the `ordinal` component and return `self`.
+ pub const fn with_ordinal(mut self, value: NonZeroU16) -> Option<Self> {
+ self.ordinal = OptionRangedU16::Some(const_try_opt!(RangedU16::new(value.get())));
+ Some(self)
+ }
+
+ /// Set the `day` component and return `self`.
+ pub const fn with_day(mut self, value: NonZeroU8) -> Option<Self> {
+ self.day = OptionRangedU8::Some(const_try_opt!(RangedU8::new(value.get())));
+ Some(self)
+ }
+
+ /// Set the `hour_24` component and return `self`.
+ pub const fn with_hour_24(mut self, value: u8) -> Option<Self> {
+ self.hour_24 = OptionRangedU8::Some(const_try_opt!(RangedU8::new(value)));
+ Some(self)
+ }
+
+ /// Set the `hour_12` component and return `self`.
+ pub const fn with_hour_12(mut self, value: NonZeroU8) -> Option<Self> {
+ self.hour_12 = OptionRangedU8::Some(const_try_opt!(RangedU8::new(value.get())));
+ Some(self)
+ }
+
+ /// Set the `hour_12_is_pm` component and return `self`.
+ pub const fn with_hour_12_is_pm(mut self, value: bool) -> Option<Self> {
+ self.hour_12_is_pm = Some(value);
+ Some(self)
+ }
+
+ /// Set the `minute` component and return `self`.
+ pub const fn with_minute(mut self, value: u8) -> Option<Self> {
+ self.minute = OptionRangedU8::Some(const_try_opt!(RangedU8::new(value)));
+ Some(self)
+ }
+
+ /// Set the `second` component and return `self`.
+ pub const fn with_second(mut self, value: u8) -> Option<Self> {
+ self.second = OptionRangedU8::Some(const_try_opt!(RangedU8::new(value)));
+ Some(self)
+ }
+
+ /// Set the `subsecond` component and return `self`.
+ pub const fn with_subsecond(mut self, value: u32) -> Option<Self> {
+ self.subsecond = OptionRangedU32::Some(const_try_opt!(RangedU32::new(value)));
+ Some(self)
+ }
+
+ /// Set the `offset_hour` component and return `self`.
+ pub const fn with_offset_hour(mut self, value: i8) -> Option<Self> {
+ self.offset_hour = OptionRangedI8::Some(const_try_opt!(RangedI8::new(value)));
+ Some(self)
+ }
+
+ /// Set the `offset_minute` component and return `self`.
#[doc(hidden)]
#[deprecated(
since = "0.3.8",
note = "use `parsed.with_offset_minute_signed()` instead"
)]
pub const fn with_offset_minute(self, value: u8) -> Option<Self> {
if value > i8::MAX as u8 {
None
} else {
self.with_offset_minute_signed(value as _)
}
}
/// Set the `offset_minute` component and return `self`.
pub const fn with_offset_minute_signed(mut self, value: i8) -> Option<Self> {
- self.offset_minute = MaybeUninit::new(value);
- self.flags |= Self::OFFSET_MINUTE_FLAG;
+ self.offset_minute = OptionRangedI8::Some(const_try_opt!(RangedI8::new(value)));
Some(self)
}
- /// Set the named component and return `self`.
+ /// Set the `offset_minute` component and return `self`.
#[doc(hidden)]
#[deprecated(
since = "0.3.8",
note = "use `parsed.with_offset_second_signed()` instead"
)]
pub const fn with_offset_second(self, value: u8) -> Option<Self> {
if value > i8::MAX as u8 {
None
} else {
self.with_offset_second_signed(value as _)
}
}
/// Set the `offset_second` component and return `self`.
pub const fn with_offset_second_signed(mut self, value: i8) -> Option<Self> {
- self.offset_second = MaybeUninit::new(value);
- self.flags |= Self::OFFSET_SECOND_FLAG;
+ self.offset_second = OptionRangedI8::Some(const_try_opt!(RangedI8::new(value)));
+ Some(self)
+ }
+
+ /// Set the `unix_timestamp_nanos` component and return `self`.
+ pub const fn with_unix_timestamp_nanos(mut self, value: i128) -> Option<Self> {
+ self.unix_timestamp_nanos = OptionRangedI128::Some(const_try_opt!(RangedI128::new(value)));
Some(self)
}
}
impl TryFrom<Parsed> for Date {
type Error = error::TryFromParsed;
fn try_from(parsed: Parsed) -> Result<Self, Self::Error> {
/// Match on the components that need to be present.
macro_rules! match_ {
@@ -675,21 +739,22 @@ impl TryFrom<Parsed> for Date {
$arm
} else {
match_!($($rest)*)
}
};
}
/// Get the value needed to adjust the ordinal day for Sunday and Monday-based week
/// numbering.
const fn adjustment(year: i32) -> i16 {
- match Date::__from_ordinal_date_unchecked(year, 1).weekday() {
+ // Safety: `ordinal` is not zero.
+ match unsafe { Date::__from_ordinal_date_unchecked(year, 1) }.weekday() {
Weekday::Monday => 7,
Weekday::Tuesday => 1,
Weekday::Wednesday => 2,
Weekday::Thursday => 3,
Weekday::Friday => 4,
Weekday::Saturday => 5,
Weekday::Sunday => 6,
}
}
@@ -699,60 +764,70 @@ impl TryFrom<Parsed> for Date {
match_! {
(year, ordinal) => Ok(Self::from_ordinal_date(year, ordinal.get())?),
(year, month, day) => Ok(Self::from_calendar_date(year, month, day.get())?),
(iso_year, iso_week_number, weekday) => Ok(Self::from_iso_week_date(
iso_year,
iso_week_number.get(),
weekday,
)?),
(year, sunday_week_number, weekday) => Ok(Self::from_ordinal_date(
year,
- (sunday_week_number as i16 * 7 + weekday.number_days_from_sunday() as i16
+ (sunday_week_number.cast_signed().extend::<i16>() * 7
+ + weekday.number_days_from_sunday().cast_signed().extend::<i16>()
- adjustment(year)
- + 1) as u16,
+ + 1).cast_unsigned(),
)?),
(year, monday_week_number, weekday) => Ok(Self::from_ordinal_date(
year,
- (monday_week_number as i16 * 7 + weekday.number_days_from_monday() as i16
+ (monday_week_number.cast_signed().extend::<i16>() * 7
+ + weekday.number_days_from_monday().cast_signed().extend::<i16>()
- adjustment(year)
- + 1) as u16,
+ + 1).cast_unsigned(),
)?),
_ => Err(InsufficientInformation),
}
}
}
impl TryFrom<Parsed> for Time {
type Error = error::TryFromParsed;
fn try_from(parsed: Parsed) -> Result<Self, Self::Error> {
let hour = match (parsed.hour_24(), parsed.hour_12(), parsed.hour_12_is_pm()) {
(Some(hour), _, _) => hour,
(_, Some(hour), Some(false)) if hour.get() == 12 => 0,
(_, Some(hour), Some(true)) if hour.get() == 12 => 12,
(_, Some(hour), Some(false)) => hour.get(),
(_, Some(hour), Some(true)) => hour.get() + 12,
_ => return Err(InsufficientInformation),
};
+
if parsed.hour_24().is_none()
&& parsed.hour_12().is_some()
&& parsed.hour_12_is_pm().is_some()
&& parsed.minute().is_none()
&& parsed.second().is_none()
&& parsed.subsecond().is_none()
{
return Ok(Self::from_hms_nano(hour, 0, 0, 0)?);
}
- let minute = parsed.minute().ok_or(InsufficientInformation)?;
- let second = parsed.second().unwrap_or(0);
- let subsecond = parsed.subsecond().unwrap_or(0);
- Ok(Self::from_hms_nano(hour, minute, second, subsecond)?)
+
+ // Reject combinations such as hour-second with minute omitted.
+ match (parsed.minute(), parsed.second(), parsed.subsecond()) {
+ (None, None, None) => Ok(Self::from_hms_nano(hour, 0, 0, 0)?),
+ (Some(minute), None, None) => Ok(Self::from_hms_nano(hour, minute, 0, 0)?),
+ (Some(minute), Some(second), None) => Ok(Self::from_hms_nano(hour, minute, second, 0)?),
+ (Some(minute), Some(second), Some(subsecond)) => {
+ Ok(Self::from_hms_nano(hour, minute, second, subsecond)?)
+ }
+ _ => Err(InsufficientInformation),
+ }
}
}
impl TryFrom<Parsed> for UtcOffset {
type Error = error::TryFromParsed;
fn try_from(parsed: Parsed) -> Result<Self, Self::Error> {
let hour = parsed.offset_hour().ok_or(InsufficientInformation)?;
let minute = parsed.offset_minute_signed().unwrap_or(0);
let second = parsed.offset_second_signed().unwrap_or(0);
@@ -765,72 +840,60 @@ impl TryFrom<Parsed> for UtcOffset {
err.name = "offset minute";
} else if err.name == "seconds" {
err.name = "offset second";
}
err.into()
})
}
}
impl TryFrom<Parsed> for PrimitiveDateTime {
- type Error = <DateTime<offset_kind::None> as TryFrom<Parsed>>::Error;
+ type Error = error::TryFromParsed;
fn try_from(parsed: Parsed) -> Result<Self, Self::Error> {
- parsed.try_into().map(Self)
+ Ok(Self::new(parsed.try_into()?, parsed.try_into()?))
}
}
impl TryFrom<Parsed> for OffsetDateTime {
- type Error = <DateTime<offset_kind::Fixed> as TryFrom<Parsed>>::Error;
-
- fn try_from(parsed: Parsed) -> Result<Self, Self::Error> {
- parsed.try_into().map(Self)
- }
-}
-
-impl<O: MaybeOffset> TryFrom<Parsed> for DateTime<O> {
type Error = error::TryFromParsed;
- #[allow(clippy::unwrap_in_result)] // We know the values are valid.
fn try_from(mut parsed: Parsed) -> Result<Self, Self::Error> {
- if O::HAS_LOGICAL_OFFSET {
- if let Some(timestamp) = parsed.unix_timestamp_nanos() {
- let DateTime { date, time, offset } =
- DateTime::<offset_kind::Fixed>::from_unix_timestamp_nanos(timestamp)?;
- return Ok(Self {
- date,
- time,
- offset: maybe_offset_from_offset::<O>(offset),
- });
+ if let Some(timestamp) = parsed.unix_timestamp_nanos() {
+ let mut value = Self::from_unix_timestamp_nanos(timestamp)?;
+ if let Some(subsecond) = parsed.subsecond() {
+ value = value.replace_nanosecond(subsecond)?;
}
+ return Ok(value);
}
// Some well-known formats explicitly allow leap seconds. We don't currently support them,
// so treat it as the nearest preceding moment that can be represented. Because leap seconds
// always fall at the end of a month UTC, reject any that are at other times.
- let leap_second_input =
- if parsed.get_flag(Parsed::LEAP_SECOND_ALLOWED_FLAG) && parsed.second() == Some(60) {
- parsed.set_second(59).expect("59 is a valid second");
- parsed
- .set_subsecond(999_999_999)
- .expect("999_999_999 is a valid subsecond");
- true
- } else {
- false
- };
-
- let dt = Self {
- date: Date::try_from(parsed)?,
- time: Time::try_from(parsed)?,
- offset: O::try_from_parsed(parsed)?,
+ let leap_second_input = if parsed.leap_second_allowed && parsed.second() == Some(60) {
+ if parsed.set_second(59).is_none() {
+ bug!("59 is a valid second");
+ }
+ if parsed.set_subsecond(999_999_999).is_none() {
+ bug!("999_999_999 is a valid subsecond");
+ }
+ true
+ } else {
+ false
};
+ let dt = Self::new_in_offset(
+ Date::try_from(parsed)?,
+ Time::try_from(parsed)?,
+ UtcOffset::try_from(parsed)?,
+ );
+
if leap_second_input && !dt.is_valid_leap_second_stand_in() {
return Err(error::TryFromParsed::ComponentRange(
error::ComponentRange {
name: "second",
minimum: 0,
maximum: 59,
value: 60,
conditional_range: true,
},
));
diff --git a/third_party/rust/time/src/primitive_date_time.rs b/third_party/rust/time/src/primitive_date_time.rs
index 9850f96d4d..85ae075e80 100644
--- a/third_party/rust/time/src/primitive_date_time.rs
+++ b/third_party/rust/time/src/primitive_date_time.rs
@@ -1,31 +1,36 @@
//! The [`PrimitiveDateTime`] struct and its associated `impl`s.
+#[cfg(feature = "formatting")]
+use alloc::string::String;
use core::fmt;
use core::ops::{Add, AddAssign, Sub, SubAssign};
use core::time::Duration as StdDuration;
#[cfg(feature = "formatting")]
use std::io;
-use crate::date_time::offset_kind;
+use powerfmt::ext::FormatterExt as _;
+use powerfmt::smart_display::{self, FormatterOptions, Metadata, SmartDisplay};
+
#[cfg(feature = "formatting")]
use crate::formatting::Formattable;
+use crate::internal_macros::{const_try, const_try_opt};
#[cfg(feature = "parsing")]
use crate::parsing::Parsable;
-use crate::{error, Date, DateTime, Duration, Month, OffsetDateTime, Time, UtcOffset, Weekday};
-
-/// The actual type doing all the work.
-type Inner = DateTime<offset_kind::None>;
+use crate::{error, util, Date, Duration, Month, OffsetDateTime, Time, UtcOffset, Weekday};
/// Combined date and time.
#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
-pub struct PrimitiveDateTime(#[allow(clippy::missing_docs_in_private_items)] pub(crate) Inner);
+pub struct PrimitiveDateTime {
+ date: Date,
+ time: Time,
+}
impl PrimitiveDateTime {
/// The smallest value that can be represented by `PrimitiveDateTime`.
///
/// Depending on `large-dates` feature flag, value of this constant may vary.
///
/// 1. With `large-dates` disabled it is equal to `-9999-01-01 00:00:00.0`
/// 2. With `large-dates` enabled it is equal to `-999999-01-01 00:00:00.0`
///
/// ```rust
@@ -41,21 +46,24 @@ impl PrimitiveDateTime {
)]
#[cfg_attr(
not(feature = "large-dates"),
doc = "// Assuming `large-dates` feature is disabled."
)]
#[cfg_attr(
not(feature = "large-dates"),
doc = "assert_eq!(PrimitiveDateTime::MIN, datetime!(-9999-01-01 0:00));"
)]
/// ```
- pub const MIN: Self = Self(Inner::MIN);
+ pub const MIN: Self = Self {
+ date: Date::MIN,
+ time: Time::MIDNIGHT,
+ };
/// The largest value that can be represented by `PrimitiveDateTime`.
///
/// Depending on `large-dates` feature flag, value of this constant may vary.
///
/// 1. With `large-dates` disabled it is equal to `9999-12-31 23:59:59.999_999_999`
/// 2. With `large-dates` enabled it is equal to `999999-12-31 23:59:59.999_999_999`
///
/// ```rust
/// # use time::PrimitiveDateTime;
@@ -70,176 +78,180 @@ impl PrimitiveDateTime {
)]
#[cfg_attr(
not(feature = "large-dates"),
doc = "// Assuming `large-dates` feature is disabled."
)]
#[cfg_attr(
not(feature = "large-dates"),
doc = "assert_eq!(PrimitiveDateTime::MAX, datetime!(+9999-12-31 23:59:59.999_999_999));"
)]
/// ```
- pub const MAX: Self = Self(Inner::MAX);
+ pub const MAX: Self = Self {
+ date: Date::MAX,
+ time: Time::MAX,
+ };
/// Create a new `PrimitiveDateTime` from the provided [`Date`] and [`Time`].
///
/// ```rust
/// # use time::PrimitiveDateTime;
/// # use time_macros::{date, datetime, time};
/// assert_eq!(
/// PrimitiveDateTime::new(date!(2019-01-01), time!(0:00)),
/// datetime!(2019-01-01 0:00),
/// );
/// ```
pub const fn new(date: Date, time: Time) -> Self {
- Self(Inner::new(date, time))
+ Self { date, time }
}
// region: component getters
/// Get the [`Date`] component of the `PrimitiveDateTime`.
///
/// ```rust
/// # use time_macros::{date, datetime};
/// assert_eq!(datetime!(2019-01-01 0:00).date(), date!(2019-01-01));
/// ```
pub const fn date(self) -> Date {
- self.0.date()
+ self.date
}
/// Get the [`Time`] component of the `PrimitiveDateTime`.
///
/// ```rust
/// # use time_macros::{datetime, time};
/// assert_eq!(datetime!(2019-01-01 0:00).time(), time!(0:00));
+ /// ```
pub const fn time(self) -> Time {
- self.0.time()
+ self.time
}
// endregion component getters
// region: date getters
/// Get the year of the date.
///
/// ```rust
/// # use time_macros::datetime;
/// assert_eq!(datetime!(2019-01-01 0:00).year(), 2019);
/// assert_eq!(datetime!(2019-12-31 0:00).year(), 2019);
/// assert_eq!(datetime!(2020-01-01 0:00).year(), 2020);
/// ```
pub const fn year(self) -> i32 {
- self.0.year()
+ self.date().year()
}
/// Get the month of the date.
///
/// ```rust
/// # use time::Month;
/// # use time_macros::datetime;
/// assert_eq!(datetime!(2019-01-01 0:00).month(), Month::January);
/// assert_eq!(datetime!(2019-12-31 0:00).month(), Month::December);
/// ```
pub const fn month(self) -> Month {
- self.0.month()
+ self.date().month()
}
/// Get the day of the date.
///
/// The returned value will always be in the range `1..=31`.
///
/// ```rust
/// # use time_macros::datetime;
/// assert_eq!(datetime!(2019-01-01 0:00).day(), 1);
/// assert_eq!(datetime!(2019-12-31 0:00).day(), 31);
/// ```
pub const fn day(self) -> u8 {
- self.0.day()
+ self.date().day()
}
/// Get the day of the year.
///
/// The returned value will always be in the range `1..=366` (`1..=365` for common years).
///
/// ```rust
/// # use time_macros::datetime;
/// assert_eq!(datetime!(2019-01-01 0:00).ordinal(), 1);
/// assert_eq!(datetime!(2019-12-31 0:00).ordinal(), 365);
/// ```
pub const fn ordinal(self) -> u16 {
- self.0.ordinal()
+ self.date().ordinal()
}
/// Get the ISO week number.
///
/// The returned value will always be in the range `1..=53`.
///
/// ```rust
/// # use time_macros::datetime;
/// assert_eq!(datetime!(2019-01-01 0:00).iso_week(), 1);
/// assert_eq!(datetime!(2019-10-04 0:00).iso_week(), 40);
/// assert_eq!(datetime!(2020-01-01 0:00).iso_week(), 1);
/// assert_eq!(datetime!(2020-12-31 0:00).iso_week(), 53);
/// assert_eq!(datetime!(2021-01-01 0:00).iso_week(), 53);
/// ```
pub const fn iso_week(self) -> u8 {
- self.0.iso_week()
+ self.date().iso_week()
}
/// Get the week number where week 1 begins on the first Sunday.
///
/// The returned value will always be in the range `0..=53`.
///
/// ```rust
/// # use time_macros::datetime;
/// assert_eq!(datetime!(2019-01-01 0:00).sunday_based_week(), 0);
/// assert_eq!(datetime!(2020-01-01 0:00).sunday_based_week(), 0);
/// assert_eq!(datetime!(2020-12-31 0:00).sunday_based_week(), 52);
/// assert_eq!(datetime!(2021-01-01 0:00).sunday_based_week(), 0);
/// ```
pub const fn sunday_based_week(self) -> u8 {
- self.0.sunday_based_week()
+ self.date().sunday_based_week()
}
/// Get the week number where week 1 begins on the first Monday.
///
/// The returned value will always be in the range `0..=53`.
///
/// ```rust
/// # use time_macros::datetime;
/// assert_eq!(datetime!(2019-01-01 0:00).monday_based_week(), 0);
/// assert_eq!(datetime!(2020-01-01 0:00).monday_based_week(), 0);
/// assert_eq!(datetime!(2020-12-31 0:00).monday_based_week(), 52);
/// assert_eq!(datetime!(2021-01-01 0:00).monday_based_week(), 0);
/// ```
pub const fn monday_based_week(self) -> u8 {
- self.0.monday_based_week()
+ self.date().monday_based_week()
}
/// Get the year, month, and day.
///
/// ```rust
/// # use time::Month;
/// # use time_macros::datetime;
/// assert_eq!(
/// datetime!(2019-01-01 0:00).to_calendar_date(),
/// (2019, Month::January, 1)
/// );
/// ```
pub const fn to_calendar_date(self) -> (i32, Month, u8) {
- self.0.to_calendar_date()
+ self.date().to_calendar_date()
}
/// Get the year and ordinal day number.
///
/// ```rust
/// # use time_macros::datetime;
/// assert_eq!(datetime!(2019-01-01 0:00).to_ordinal_date(), (2019, 1));
/// ```
pub const fn to_ordinal_date(self) -> (i32, u16) {
- self.0.to_ordinal_date()
+ self.date().to_ordinal_date()
}
/// Get the ISO 8601 year, week number, and weekday.
///
/// ```rust
/// # use time::Weekday::*;
/// # use time_macros::datetime;
/// assert_eq!(
/// datetime!(2019-01-01 0:00).to_iso_week_date(),
/// (2019, 1, Tuesday)
@@ -255,21 +267,21 @@ impl PrimitiveDateTime {
/// assert_eq!(
/// datetime!(2020-12-31 0:00).to_iso_week_date(),
/// (2020, 53, Thursday)
/// );
/// assert_eq!(
/// datetime!(2021-01-01 0:00).to_iso_week_date(),
/// (2020, 53, Friday)
/// );
/// ```
pub const fn to_iso_week_date(self) -> (i32, u8, Weekday) {
- self.0.to_iso_week_date()
+ self.date().to_iso_week_date()
}
/// Get the weekday.
///
/// ```rust
/// # use time::Weekday::*;
/// # use time_macros::datetime;
/// assert_eq!(datetime!(2019-01-01 0:00).weekday(), Tuesday);
/// assert_eq!(datetime!(2019-02-01 0:00).weekday(), Friday);
/// assert_eq!(datetime!(2019-03-01 0:00).weekday(), Friday);
@@ -277,176 +289,176 @@ impl PrimitiveDateTime {
/// assert_eq!(datetime!(2019-05-01 0:00).weekday(), Wednesday);
/// assert_eq!(datetime!(2019-06-01 0:00).weekday(), Saturday);
/// assert_eq!(datetime!(2019-07-01 0:00).weekday(), Monday);
/// assert_eq!(datetime!(2019-08-01 0:00).weekday(), Thursday);
/// assert_eq!(datetime!(2019-09-01 0:00).weekday(), Sunday);
/// assert_eq!(datetime!(2019-10-01 0:00).weekday(), Tuesday);
/// assert_eq!(datetime!(2019-11-01 0:00).weekday(), Friday);
/// assert_eq!(datetime!(2019-12-01 0:00).weekday(), Sunday);
/// ```
pub const fn weekday(self) -> Weekday {
- self.0.weekday()
+ self.date().weekday()
}
/// Get the Julian day for the date. The time is not taken into account for this calculation.
///
/// The algorithm to perform this conversion is derived from one provided by Peter Baum; it is
/// freely available [here](https://www.researchgate.net/publication/316558298_Date_Algorithms).
///
/// ```rust
/// # use time_macros::datetime;
/// assert_eq!(datetime!(-4713-11-24 0:00).to_julian_day(), 0);
/// assert_eq!(datetime!(2000-01-01 0:00).to_julian_day(), 2_451_545);
/// assert_eq!(datetime!(2019-01-01 0:00).to_julian_day(), 2_458_485);
/// assert_eq!(datetime!(2019-12-31 0:00).to_julian_day(), 2_458_849);
/// ```
pub const fn to_julian_day(self) -> i32 {
- self.0.to_julian_day()
+ self.date().to_julian_day()
}
// endregion date getters
// region: time getters
/// Get the clock hour, minute, and second.
///
/// ```rust
/// # use time_macros::datetime;
/// assert_eq!(datetime!(2020-01-01 0:00:00).as_hms(), (0, 0, 0));
/// assert_eq!(datetime!(2020-01-01 23:59:59).as_hms(), (23, 59, 59));
/// ```
pub const fn as_hms(self) -> (u8, u8, u8) {
- self.0.as_hms()
+ self.time().as_hms()
}
/// Get the clock hour, minute, second, and millisecond.
///
/// ```rust
/// # use time_macros::datetime;
/// assert_eq!(datetime!(2020-01-01 0:00:00).as_hms_milli(), (0, 0, 0, 0));
/// assert_eq!(
/// datetime!(2020-01-01 23:59:59.999).as_hms_milli(),
/// (23, 59, 59, 999)
/// );
/// ```
pub const fn as_hms_milli(self) -> (u8, u8, u8, u16) {
- self.0.as_hms_milli()
+ self.time().as_hms_milli()
}
/// Get the clock hour, minute, second, and microsecond.
///
/// ```rust
/// # use time_macros::datetime;
/// assert_eq!(datetime!(2020-01-01 0:00:00).as_hms_micro(), (0, 0, 0, 0));
/// assert_eq!(
/// datetime!(2020-01-01 23:59:59.999_999).as_hms_micro(),
/// (23, 59, 59, 999_999)
/// );
/// ```
pub const fn as_hms_micro(self) -> (u8, u8, u8, u32) {
- self.0.as_hms_micro()
+ self.time().as_hms_micro()
}
/// Get the clock hour, minute, second, and nanosecond.
///
/// ```rust
/// # use time_macros::datetime;
/// assert_eq!(datetime!(2020-01-01 0:00:00).as_hms_nano(), (0, 0, 0, 0));
/// assert_eq!(
/// datetime!(2020-01-01 23:59:59.999_999_999).as_hms_nano(),
/// (23, 59, 59, 999_999_999)
/// );
/// ```
pub const fn as_hms_nano(self) -> (u8, u8, u8, u32) {
- self.0.as_hms_nano()
+ self.time().as_hms_nano()
}
/// Get the clock hour.
///
/// The returned value will always be in the range `0..24`.
///
/// ```rust
/// # use time_macros::datetime;
/// assert_eq!(datetime!(2019-01-01 0:00).hour(), 0);
/// assert_eq!(datetime!(2019-01-01 23:59:59).hour(), 23);
/// ```
pub const fn hour(self) -> u8 {
- self.0.hour()
+ self.time().hour()
}
/// Get the minute within the hour.
///
/// The returned value will always be in the range `0..60`.
///
/// ```rust
/// # use time_macros::datetime;
/// assert_eq!(datetime!(2019-01-01 0:00).minute(), 0);
/// assert_eq!(datetime!(2019-01-01 23:59:59).minute(), 59);
/// ```
pub const fn minute(self) -> u8 {
- self.0.minute()
+ self.time().minute()
}
/// Get the second within the minute.
///
/// The returned value will always be in the range `0..60`.
///
/// ```rust
/// # use time_macros::datetime;
/// assert_eq!(datetime!(2019-01-01 0:00).second(), 0);
/// assert_eq!(datetime!(2019-01-01 23:59:59).second(), 59);
/// ```
pub const fn second(self) -> u8 {
- self.0.second()
+ self.time().second()
}
/// Get the milliseconds within the second.
///
/// The returned value will always be in the range `0..1_000`.
///
/// ```rust
/// # use time_macros::datetime;
/// assert_eq!(datetime!(2019-01-01 0:00).millisecond(), 0);
/// assert_eq!(datetime!(2019-01-01 23:59:59.999).millisecond(), 999);
/// ```
pub const fn millisecond(self) -> u16 {
- self.0.millisecond()
+ self.time().millisecond()
}
/// Get the microseconds within the second.
///
/// The returned value will always be in the range `0..1_000_000`.
///
/// ```rust
/// # use time_macros::datetime;
/// assert_eq!(datetime!(2019-01-01 0:00).microsecond(), 0);
/// assert_eq!(
/// datetime!(2019-01-01 23:59:59.999_999).microsecond(),
/// 999_999
/// );
/// ```
pub const fn microsecond(self) -> u32 {
- self.0.microsecond()
+ self.time().microsecond()
}
/// Get the nanoseconds within the second.
///
/// The returned value will always be in the range `0..1_000_000_000`.
///
/// ```rust
/// # use time_macros::datetime;
/// assert_eq!(datetime!(2019-01-01 0:00).nanosecond(), 0);
/// assert_eq!(
/// datetime!(2019-01-01 23:59:59.999_999_999).nanosecond(),
/// 999_999_999,
/// );
/// ```
pub const fn nanosecond(self) -> u32 {
- self.0.nanosecond()
+ self.time().nanosecond()
}
// endregion time getters
// region: attach offset
/// Assuming that the existing `PrimitiveDateTime` represents a moment in the provided
/// [`UtcOffset`], return an [`OffsetDateTime`].
///
/// ```rust
/// # use time_macros::{datetime, offset};
/// assert_eq!(
@@ -456,35 +468,35 @@ impl PrimitiveDateTime {
/// 1_546_300_800,
/// );
/// assert_eq!(
/// datetime!(2019-01-01 0:00)
/// .assume_offset(offset!(-1))
/// .unix_timestamp(),
/// 1_546_304_400,
/// );
/// ```
pub const fn assume_offset(self, offset: UtcOffset) -> OffsetDateTime {
- OffsetDateTime(self.0.assume_offset(offset))
+ OffsetDateTime::new_in_offset(self.date, self.time, offset)
}
/// Assuming that the existing `PrimitiveDateTime` represents a moment in UTC, return an
/// [`OffsetDateTime`].
///
/// ```rust
/// # use time_macros::datetime;
/// assert_eq!(
/// datetime!(2019-01-01 0:00).assume_utc().unix_timestamp(),
/// 1_546_300_800,
/// );
/// ```
pub const fn assume_utc(self) -> OffsetDateTime {
- OffsetDateTime(self.0.assume_utc())
+ self.assume_offset(UtcOffset::UTC)
}
// endregion attach offset
// region: checked arithmetic
/// Computes `self + duration`, returning `None` if an overflow occurred.
///
/// ```
/// # use time::{Date, ext::NumericalDuration};
/// # use time_macros::datetime;
/// let datetime = Date::MIN.midnight();
@@ -492,41 +504,61 @@ impl PrimitiveDateTime {
///
/// let datetime = Date::MAX.midnight();
/// assert_eq!(datetime.checked_add(1.days()), None);
///
/// assert_eq!(
/// datetime!(2019 - 11 - 25 15:30).checked_add(27.hours()),
/// Some(datetime!(2019 - 11 - 26 18:30))
/// );
/// ```
pub const fn checked_add(self, duration: Duration) -> Option<Self> {
- Some(Self(const_try_opt!(self.0.checked_add(duration))))
+ let (date_adjustment, time) = self.time.adjusting_add(duration);
+ let date = const_try_opt!(self.date.checked_add(duration));
+
+ Some(Self {
+ date: match date_adjustment {
+ util::DateAdjustment::Previous => const_try_opt!(date.previous_day()),
+ util::DateAdjustment::Next => const_try_opt!(date.next_day()),
+ util::DateAdjustment::None => date,
+ },
+ time,
+ })
}
/// Computes `self - duration`, returning `None` if an overflow occurred.
///
/// ```
/// # use time::{Date, ext::NumericalDuration};
/// # use time_macros::datetime;
/// let datetime = Date::MIN.midnight();
/// assert_eq!(datetime.checked_sub(2.days()), None);
///
/// let datetime = Date::MAX.midnight();
/// assert_eq!(datetime.checked_sub((-1).days()), None);
///
/// assert_eq!(
/// datetime!(2019 - 11 - 25 15:30).checked_sub(27.hours()),
/// Some(datetime!(2019 - 11 - 24 12:30))
/// );
/// ```
pub const fn checked_sub(self, duration: Duration) -> Option<Self> {
- Some(Self(const_try_opt!(self.0.checked_sub(duration))))
+ let (date_adjustment, time) = self.time.adjusting_sub(duration);
+ let date = const_try_opt!(self.date.checked_sub(duration));
+
+ Some(Self {
+ date: match date_adjustment {
+ util::DateAdjustment::Previous => const_try_opt!(date.previous_day()),
+ util::DateAdjustment::Next => const_try_opt!(date.next_day()),
+ util::DateAdjustment::None => date,
+ },
+ time,
+ })
}
// endregion: checked arithmetic
// region: saturating arithmetic
/// Computes `self + duration`, saturating value on overflow.
///
/// ```
/// # use time::{PrimitiveDateTime, ext::NumericalDuration};
/// # use time_macros::datetime;
/// assert_eq!(
@@ -538,21 +570,27 @@ impl PrimitiveDateTime {
/// PrimitiveDateTime::MAX.saturating_add(2.days()),
/// PrimitiveDateTime::MAX
/// );
///
/// assert_eq!(
/// datetime!(2019 - 11 - 25 15:30).saturating_add(27.hours()),
/// datetime!(2019 - 11 - 26 18:30)
/// );
/// ```
pub const fn saturating_add(self, duration: Duration) -> Self {
- Self(self.0.saturating_add(duration))
+ if let Some(datetime) = self.checked_add(duration) {
+ datetime
+ } else if duration.is_negative() {
+ Self::MIN
+ } else {
+ Self::MAX
+ }
}
/// Computes `self - duration`, saturating value on overflow.
///
/// ```
/// # use time::{PrimitiveDateTime, ext::NumericalDuration};
/// # use time_macros::datetime;
/// assert_eq!(
/// PrimitiveDateTime::MIN.saturating_sub(2.days()),
/// PrimitiveDateTime::MIN
@@ -562,230 +600,285 @@ impl PrimitiveDateTime {
/// PrimitiveDateTime::MAX.saturating_sub((-2).days()),
/// PrimitiveDateTime::MAX
/// );
///
/// assert_eq!(
/// datetime!(2019 - 11 - 25 15:30).saturating_sub(27.hours()),
/// datetime!(2019 - 11 - 24 12:30)
/// );
/// ```
pub const fn saturating_sub(self, duration: Duration) -> Self {
- Self(self.0.saturating_sub(duration))
+ if let Some(datetime) = self.checked_sub(duration) {
+ datetime
+ } else if duration.is_negative() {
+ Self::MAX
+ } else {
+ Self::MIN
+ }
}
// endregion: saturating arithmetic
}
// region: replacement
/// Methods that replace part of the `PrimitiveDateTime`.
impl PrimitiveDateTime {
/// Replace the time, preserving the date.
///
/// ```rust
/// # use time_macros::{datetime, time};
/// assert_eq!(
/// datetime!(2020-01-01 17:00).replace_time(time!(5:00)),
/// datetime!(2020-01-01 5:00)
/// );
/// ```
#[must_use = "This method does not mutate the original `PrimitiveDateTime`."]
pub const fn replace_time(self, time: Time) -> Self {
- Self(self.0.replace_time(time))
+ Self {
+ date: self.date,
+ time,
+ }
}
/// Replace the date, preserving the time.
///
/// ```rust
/// # use time_macros::{datetime, date};
/// assert_eq!(
/// datetime!(2020-01-01 12:00).replace_date(date!(2020-01-30)),
/// datetime!(2020-01-30 12:00)
/// );
/// ```
#[must_use = "This method does not mutate the original `PrimitiveDateTime`."]
pub const fn replace_date(self, date: Date) -> Self {
- Self(self.0.replace_date(date))
+ Self {
+ date,
+ time: self.time,
+ }
}
/// Replace the year. The month and day will be unchanged.
///
/// ```rust
/// # use time_macros::datetime;
/// assert_eq!(
/// datetime!(2022 - 02 - 18 12:00).replace_year(2019),
/// Ok(datetime!(2019 - 02 - 18 12:00))
/// );
/// assert!(datetime!(2022 - 02 - 18 12:00).replace_year(-1_000_000_000).is_err()); // -1_000_000_000 isn't a valid year
/// assert!(datetime!(2022 - 02 - 18 12:00).replace_year(1_000_000_000).is_err()); // 1_000_000_000 isn't a valid year
/// ```
#[must_use = "This method does not mutate the original `PrimitiveDateTime`."]
pub const fn replace_year(self, year: i32) -> Result<Self, error::ComponentRange> {
- Ok(Self(const_try!(self.0.replace_year(year))))
+ Ok(Self {
+ date: const_try!(self.date.replace_year(year)),
+ time: self.time,
+ })
}
/// Replace the month of the year.
///
/// ```rust
/// # use time_macros::datetime;
/// # use time::Month;
/// assert_eq!(
/// datetime!(2022 - 02 - 18 12:00).replace_month(Month::January),
/// Ok(datetime!(2022 - 01 - 18 12:00))
/// );
/// assert!(datetime!(2022 - 01 - 30 12:00).replace_month(Month::February).is_err()); // 30 isn't a valid day in February
/// ```
#[must_use = "This method does not mutate the original `PrimitiveDateTime`."]
pub const fn replace_month(self, month: Month) -> Result<Self, error::ComponentRange> {
- Ok(Self(const_try!(self.0.replace_month(month))))
+ Ok(Self {
+ date: const_try!(self.date.replace_month(month)),
+ time: self.time,
+ })
}
/// Replace the day of the month.
///
/// ```rust
/// # use time_macros::datetime;
/// assert_eq!(
/// datetime!(2022 - 02 - 18 12:00).replace_day(1),
/// Ok(datetime!(2022 - 02 - 01 12:00))
/// );
/// assert!(datetime!(2022 - 02 - 18 12:00).replace_day(0).is_err()); // 00 isn't a valid day
/// assert!(datetime!(2022 - 02 - 18 12:00).replace_day(30).is_err()); // 30 isn't a valid day in February
/// ```
#[must_use = "This method does not mutate the original `PrimitiveDateTime`."]
pub const fn replace_day(self, day: u8) -> Result<Self, error::ComponentRange> {
- Ok(Self(const_try!(self.0.replace_day(day))))
+ Ok(Self {
+ date: const_try!(self.date.replace_day(day)),
+ time: self.time,
+ })
+ }
+
+ /// Replace the day of the year.
+ ///
+ /// ```rust
+ /// # use time_macros::datetime;
+ /// assert_eq!(datetime!(2022-049 12:00).replace_ordinal(1), Ok(datetime!(2022-001 12:00)));
+ /// assert!(datetime!(2022-049 12:00).replace_ordinal(0).is_err()); // 0 isn't a valid ordinal
+ /// assert!(datetime!(2022-049 12:00).replace_ordinal(366).is_err()); // 2022 isn't a leap year
+ /// ````
+ #[must_use = "This method does not mutate the original `PrimitiveDateTime`."]
+ pub const fn replace_ordinal(self, ordinal: u16) -> Result<Self, error::ComponentRange> {
+ Ok(Self {
+ date: const_try!(self.date.replace_ordinal(ordinal)),
+ time: self.time,
+ })
}
/// Replace the clock hour.
///
/// ```rust
/// # use time_macros::datetime;
/// assert_eq!(
/// datetime!(2022 - 02 - 18 01:02:03.004_005_006).replace_hour(7),
/// Ok(datetime!(2022 - 02 - 18 07:02:03.004_005_006))
/// );
/// assert!(datetime!(2022 - 02 - 18 01:02:03.004_005_006).replace_hour(24).is_err()); // 24 isn't a valid hour
/// ```
#[must_use = "This method does not mutate the original `PrimitiveDateTime`."]
pub const fn replace_hour(self, hour: u8) -> Result<Self, error::ComponentRange> {
- Ok(Self(const_try!(self.0.replace_hour(hour))))
+ Ok(Self {
+ date: self.date,
+ time: const_try!(self.time.replace_hour(hour)),
+ })
}
/// Replace the minutes within the hour.
///
/// ```rust
/// # use time_macros::datetime;
/// assert_eq!(
/// datetime!(2022 - 02 - 18 01:02:03.004_005_006).replace_minute(7),
/// Ok(datetime!(2022 - 02 - 18 01:07:03.004_005_006))
/// );
/// assert!(datetime!(2022 - 02 - 18 01:02:03.004_005_006).replace_minute(60).is_err()); // 60 isn't a valid minute
/// ```
#[must_use = "This method does not mutate the original `PrimitiveDateTime`."]
pub const fn replace_minute(self, minute: u8) -> Result<Self, error::ComponentRange> {
- Ok(Self(const_try!(self.0.replace_minute(minute))))
+ Ok(Self {
+ date: self.date,
+ time: const_try!(self.time.replace_minute(minute)),
+ })
}
/// Replace the seconds within the minute.
///
/// ```rust
/// # use time_macros::datetime;
/// assert_eq!(
/// datetime!(2022 - 02 - 18 01:02:03.004_005_006).replace_second(7),
/// Ok(datetime!(2022 - 02 - 18 01:02:07.004_005_006))
/// );
/// assert!(datetime!(2022 - 02 - 18 01:02:03.004_005_006).replace_second(60).is_err()); // 60 isn't a valid second
/// ```
#[must_use = "This method does not mutate the original `PrimitiveDateTime`."]
pub const fn replace_second(self, second: u8) -> Result<Self, error::ComponentRange> {
- Ok(Self(const_try!(self.0.replace_second(second))))
+ Ok(Self {
+ date: self.date,
+ time: const_try!(self.time.replace_second(second)),
+ })
}
/// Replace the milliseconds within the second.
///
/// ```rust
/// # use time_macros::datetime;
/// assert_eq!(
/// datetime!(2022 - 02 - 18 01:02:03.004_005_006).replace_millisecond(7),
/// Ok(datetime!(2022 - 02 - 18 01:02:03.007))
/// );
/// assert!(datetime!(2022 - 02 - 18 01:02:03.004_005_006).replace_millisecond(1_000).is_err()); // 1_000 isn't a valid millisecond
/// ```
#[must_use = "This method does not mutate the original `PrimitiveDateTime`."]
pub const fn replace_millisecond(
self,
millisecond: u16,
) -> Result<Self, error::ComponentRange> {
- Ok(Self(const_try!(self.0.replace_millisecond(millisecond))))
+ Ok(Self {
+ date: self.date,
+ time: const_try!(self.time.replace_millisecond(millisecond)),
+ })
}
/// Replace the microseconds within the second.
///
/// ```rust
/// # use time_macros::datetime;
/// assert_eq!(
/// datetime!(2022 - 02 - 18 01:02:03.004_005_006).replace_microsecond(7_008),
/// Ok(datetime!(2022 - 02 - 18 01:02:03.007_008))
/// );
/// assert!(datetime!(2022 - 02 - 18 01:02:03.004_005_006).replace_microsecond(1_000_000).is_err()); // 1_000_000 isn't a valid microsecond
/// ```
#[must_use = "This method does not mutate the original `PrimitiveDateTime`."]
pub const fn replace_microsecond(
self,
microsecond: u32,
) -> Result<Self, error::ComponentRange> {
- Ok(Self(const_try!(self.0.replace_microsecond(microsecond))))
+ Ok(Self {
+ date: self.date,
+ time: const_try!(self.time.replace_microsecond(microsecond)),
+ })
}
/// Replace the nanoseconds within the second.
///
/// ```rust
/// # use time_macros::datetime;
/// assert_eq!(
/// datetime!(2022 - 02 - 18 01:02:03.004_005_006).replace_nanosecond(7_008_009),
/// Ok(datetime!(2022 - 02 - 18 01:02:03.007_008_009))
/// );
/// assert!(datetime!(2022 - 02 - 18 01:02:03.004_005_006).replace_nanosecond(1_000_000_000).is_err()); // 1_000_000_000 isn't a valid nanosecond
/// ```
#[must_use = "This method does not mutate the original `PrimitiveDateTime`."]
pub const fn replace_nanosecond(self, nanosecond: u32) -> Result<Self, error::ComponentRange> {
- Ok(Self(const_try!(self.0.replace_nanosecond(nanosecond))))
+ Ok(Self {
+ date: self.date,
+ time: const_try!(self.time.replace_nanosecond(nanosecond)),
+ })
}
}
// endregion replacement
// region: formatting & parsing
#[cfg(feature = "formatting")]
impl PrimitiveDateTime {
/// Format the `PrimitiveDateTime` using the provided [format
/// description](crate::format_description).
pub fn format_into(
self,
output: &mut impl io::Write,
format: &(impl Formattable + ?Sized),
) -> Result<usize, error::Format> {
- self.0.format_into(output, format)
+ format.format_into(output, Some(self.date), Some(self.time), None)
}
/// Format the `PrimitiveDateTime` using the provided [format
/// description](crate::format_description).
///
/// ```rust
/// # use time::format_description;
/// # use time_macros::datetime;
/// let format = format_description::parse("[year]-[month]-[day] [hour]:[minute]:[second]")?;
/// assert_eq!(
/// datetime!(2020-01-02 03:04:05).format(&format)?,
/// "2020-01-02 03:04:05"
/// );
/// # Ok::<_, time::Error>(())
/// ```
pub fn format(self, format: &(impl Formattable + ?Sized)) -> Result<String, error::Format> {
- self.0.format(format)
+ format.format(Some(self.date), Some(self.time), None)
}
}
#[cfg(feature = "parsing")]
impl PrimitiveDateTime {
/// Parse a `PrimitiveDateTime` from the input using the provided [format
/// description](crate::format_description).
///
/// ```rust
/// # use time::PrimitiveDateTime;
@@ -794,92 +887,163 @@ impl PrimitiveDateTime {
/// assert_eq!(
/// PrimitiveDateTime::parse("2020-01-02 03:04:05", &format)?,
/// datetime!(2020-01-02 03:04:05)
/// );
/// # Ok::<_, time::Error>(())
/// ```
pub fn parse(
input: &str,
description: &(impl Parsable + ?Sized),
) -> Result<Self, error::Parse> {
- Inner::parse(input, description).map(Self)
+ description.parse_primitive_date_time(input.as_bytes())
+ }
+}
+
+impl SmartDisplay for PrimitiveDateTime {
+ type Metadata = ();
+
+ fn metadata(&self, _: FormatterOptions) -> Metadata<Self> {
+ let width = smart_display::padded_width_of!(self.date, " ", self.time);
+ Metadata::new(width, self, ())
+ }
+
+ fn fmt_with_metadata(
+ &self,
+ f: &mut fmt::Formatter<'_>,
+ metadata: Metadata<Self>,
+ ) -> fmt::Result {
+ f.pad_with_width(
+ metadata.unpadded_width(),
+ format_args!("{} {}", self.date, self.time),
+ )
}
}
impl fmt::Display for PrimitiveDateTime {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- self.0.fmt(f)
+ SmartDisplay::fmt(self, f)
}
}
impl fmt::Debug for PrimitiveDateTime {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(self, f)
}
}
// endregion formatting & parsing
// region: trait impls
impl Add<Duration> for PrimitiveDateTime {
type Output = Self;
+ /// # Panics
+ ///
+ /// This may panic if an overflow occurs.
fn add(self, duration: Duration) -> Self::Output {
- Self(self.0.add(duration))
+ self.checked_add(duration)
+ .expect("resulting value is out of range")
}
}
impl Add<StdDuration> for PrimitiveDateTime {
type Output = Self;
+ /// # Panics
+ ///
+ /// This may panic if an overflow occurs.
fn add(self, duration: StdDuration) -> Self::Output {
- Self(self.0.add(duration))
+ let (is_next_day, time) = self.time.adjusting_add_std(duration);
+
+ Self {
+ date: if is_next_day {
+ (self.date + duration)
+ .next_day()
+ .expect("resulting value is out of range")
+ } else {
+ self.date + duration
+ },
+ time,
+ }
}
}
impl AddAssign<Duration> for PrimitiveDateTime {
+ /// # Panics
+ ///
+ /// This may panic if an overflow occurs.
fn add_assign(&mut self, duration: Duration) {
- self.0.add_assign(duration);
+ *self = *self + duration;
}
}
impl AddAssign<StdDuration> for PrimitiveDateTime {
+ /// # Panics
+ ///
+ /// This may panic if an overflow occurs.
fn add_assign(&mut self, duration: StdDuration) {
- self.0.add_assign(duration);
+ *self = *self + duration;
}
}
impl Sub<Duration> for PrimitiveDateTime {
type Output = Self;
+ /// # Panics
+ ///
+ /// This may panic if an overflow occurs.
fn sub(self, duration: Duration) -> Self::Output {
- Self(self.0.sub(duration))
+ self.checked_sub(duration)
+ .expect("resulting value is out of range")
}
}
impl Sub<StdDuration> for PrimitiveDateTime {
type Output = Self;
+ /// # Panics
+ ///
+ /// This may panic if an overflow occurs.
fn sub(self, duration: StdDuration) -> Self::Output {
- Self(self.0.sub(duration))
+ let (is_previous_day, time) = self.time.adjusting_sub_std(duration);
+
+ Self {
+ date: if is_previous_day {
+ (self.date - duration)
+ .previous_day()
+ .expect("resulting value is out of range")
+ } else {
+ self.date - duration
+ },
+ time,
+ }
}
}
impl SubAssign<Duration> for PrimitiveDateTime {
+ /// # Panics
+ ///
+ /// This may panic if an overflow occurs.
fn sub_assign(&mut self, duration: Duration) {
- self.0.sub_assign(duration);
+ *self = *self - duration;
}
}
impl SubAssign<StdDuration> for PrimitiveDateTime {
+ /// # Panics
+ ///
+ /// This may panic if an overflow occurs.
fn sub_assign(&mut self, duration: StdDuration) {
- self.0.sub_assign(duration);
+ *self = *self - duration;
}
}
impl Sub for PrimitiveDateTime {
type Output = Duration;
+ /// # Panics
+ ///
+ /// This may panic if an overflow occurs.
fn sub(self, rhs: Self) -> Self::Output {
- self.0.sub(rhs.0)
+ (self.date - rhs.date) + (self.time - rhs.time)
}
}
// endregion trait impls
diff --git a/third_party/rust/time/src/quickcheck.rs b/third_party/rust/time/src/quickcheck.rs
index 4a788b517e..312a09e196 100644
--- a/third_party/rust/time/src/quickcheck.rs
+++ b/third_party/rust/time/src/quickcheck.rs
@@ -1,15 +1,15 @@
//! Implementations of the [`quickcheck::Arbitrary`](quickcheck::Arbitrary) trait.
//!
//! This enables users to write tests such as this, and have test values provided automatically:
//!
-//! ```
+//! ```ignore
//! # #![allow(dead_code)]
//! use quickcheck::quickcheck;
//! use time::Date;
//!
//! struct DateRange {
//! from: Date,
//! to: Date,
//! }
//!
//! impl DateRange {
@@ -31,22 +31,20 @@
//! ```
//!
//! An implementation for `Instant` is intentionally omitted since its values are only meaningful in
//! relation to a [`Duration`], and obtaining an `Instant` from a [`Duration`] is very simple
//! anyway.
use alloc::boxed::Box;
use quickcheck::{empty_shrinker, single_shrinker, Arbitrary, Gen};
-use crate::convert::*;
-use crate::date_time::{DateTime, MaybeOffset};
use crate::{Date, Duration, Month, OffsetDateTime, PrimitiveDateTime, Time, UtcOffset, Weekday};
/// Obtain an arbitrary value between the minimum and maximum inclusive.
macro_rules! arbitrary_between {
($type:ty; $gen:expr, $min:expr, $max:expr) => {{
let min = $min;
let max = $max;
let range = max - min;
<$type>::arbitrary($gen).rem_euclid(range + 1) + min
}};
@@ -66,119 +64,100 @@ impl Arbitrary for Date {
Box::new(
self.to_ordinal_date()
.shrink()
.flat_map(|(year, ordinal)| Self::from_ordinal_date(year, ordinal)),
)
}
}
impl Arbitrary for Duration {
fn arbitrary(g: &mut Gen) -> Self {
- Self::nanoseconds_i128(arbitrary_between!(
- i128;
- g,
- Self::MIN.whole_nanoseconds(),
- Self::MAX.whole_nanoseconds()
- ))
+ Self::new_ranged(<_>::arbitrary(g), <_>::arbitrary(g))
}
fn shrink(&self) -> Box<dyn Iterator<Item = Self>> {
Box::new(
- (self.subsec_nanoseconds(), self.whole_seconds())
+ (self.subsec_nanoseconds_ranged(), self.whole_seconds())
.shrink()
.map(|(mut nanoseconds, seconds)| {
// Coerce the sign if necessary.
- if (seconds > 0 && nanoseconds < 0) || (seconds < 0 && nanoseconds > 0) {
- nanoseconds *= -1;
+ if (seconds > 0 && nanoseconds.get() < 0)
+ || (seconds < 0 && nanoseconds.get() > 0)
+ {
+ nanoseconds = nanoseconds.neg();
}
- Self::new_unchecked(seconds, nanoseconds)
+ Self::new_ranged_unchecked(seconds, nanoseconds)
}),
)
}
}
impl Arbitrary for Time {
fn arbitrary(g: &mut Gen) -> Self {
- Self::__from_hms_nanos_unchecked(
- arbitrary_between!(u8; g, 0, Hour.per(Day) - 1),
- arbitrary_between!(u8; g, 0, Minute.per(Hour) - 1),
- arbitrary_between!(u8; g, 0, Second.per(Minute) - 1),
- arbitrary_between!(u32; g, 0, Nanosecond.per(Second) - 1),
+ Self::from_hms_nanos_ranged(
+ <_>::arbitrary(g),
+ <_>::arbitrary(g),
+ <_>::arbitrary(g),
+ <_>::arbitrary(g),
)
}
fn shrink(&self) -> Box<dyn Iterator<Item = Self>> {
Box::new(
- self.as_hms_nano()
+ self.as_hms_nano_ranged()
.shrink()
.map(|(hour, minute, second, nanosecond)| {
- Self::__from_hms_nanos_unchecked(hour, minute, second, nanosecond)
+ Self::from_hms_nanos_ranged(hour, minute, second, nanosecond)
}),
)
}
}
impl Arbitrary for PrimitiveDateTime {
fn arbitrary(g: &mut Gen) -> Self {
- Self(<_>::arbitrary(g))
+ Self::new(<_>::arbitrary(g), <_>::arbitrary(g))
}
fn shrink(&self) -> Box<dyn Iterator<Item = Self>> {
- Box::new(self.0.shrink().map(Self))
+ Box::new(
+ (self.date(), self.time())
+ .shrink()
+ .map(|(date, time)| Self::new(date, time)),
+ )
}
}
impl Arbitrary for UtcOffset {
fn arbitrary(g: &mut Gen) -> Self {
- let seconds =
- arbitrary_between!(i32; g, -(Second.per(Day) as i32 - 1), Second.per(Day) as i32 - 1);
- Self::__from_hms_unchecked(
- (seconds / Second.per(Hour) as i32) as _,
- ((seconds % Second.per(Hour) as i32) / Minute.per(Hour) as i32) as _,
- (seconds % Second.per(Minute) as i32) as _,
- )
+ Self::from_hms_ranged(<_>::arbitrary(g), <_>::arbitrary(g), <_>::arbitrary(g))
}
fn shrink(&self) -> Box<dyn Iterator<Item = Self>> {
Box::new(
- self.as_hms().shrink().map(|(hours, minutes, seconds)| {
- Self::__from_hms_unchecked(hours, minutes, seconds)
- }),
+ self.as_hms_ranged()
+ .shrink()
+ .map(|(hours, minutes, seconds)| Self::from_hms_ranged(hours, minutes, seconds)),
)
}
}
impl Arbitrary for OffsetDateTime {
fn arbitrary(g: &mut Gen) -> Self {
- Self(<_>::arbitrary(g))
- }
-
- fn shrink(&self) -> Box<dyn Iterator<Item = Self>> {
- Box::new(self.0.shrink().map(Self))
- }
-}
-
-impl<O: MaybeOffset + 'static> Arbitrary for DateTime<O> {
- fn arbitrary(g: &mut Gen) -> Self {
- Self {
- date: <_>::arbitrary(g),
- time: <_>::arbitrary(g),
- offset: <_>::arbitrary(g),
- }
+ Self::new_in_offset(<_>::arbitrary(g), <_>::arbitrary(g), <_>::arbitrary(g))
}
fn shrink(&self) -> Box<dyn Iterator<Item = Self>> {
Box::new(
- (self.date, self.time, self.offset)
+ (self.date(), self.time(), self.offset())
.shrink()
- .map(|(date, time, offset)| Self { date, time, offset }),
+ .map(|(date, time, offset)| Self::new_in_offset(date, time, offset)),
)
}
}
impl Arbitrary for Weekday {
fn arbitrary(g: &mut Gen) -> Self {
use Weekday::*;
match arbitrary_between!(u8; g, 0, 6) {
0 => Monday,
1 => Tuesday,
diff --git a/third_party/rust/time/src/rand.rs b/third_party/rust/time/src/rand.rs
index e5181f9bdd..eac6fe66d1 100644
--- a/third_party/rust/time/src/rand.rs
+++ b/third_party/rust/time/src/rand.rs
@@ -1,66 +1,53 @@
//! Implementation of [`Distribution`] for various structs.
use rand::distributions::{Distribution, Standard};
use rand::Rng;
-use crate::convert::*;
use crate::{Date, Duration, Month, OffsetDateTime, PrimitiveDateTime, Time, UtcOffset, Weekday};
impl Distribution<Time> for Standard {
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Time {
- Time::__from_hms_nanos_unchecked(
- rng.gen_range(0..Hour.per(Day)),
- rng.gen_range(0..Minute.per(Hour)),
- rng.gen_range(0..Second.per(Minute)),
- rng.gen_range(0..Nanosecond.per(Second)),
- )
+ Time::from_hms_nanos_ranged(rng.gen(), rng.gen(), rng.gen(), rng.gen())
}
}
impl Distribution<Date> for Standard {
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Date {
Date::from_julian_day_unchecked(
rng.gen_range(Date::MIN.to_julian_day()..=Date::MAX.to_julian_day()),
)
}
}
impl Distribution<UtcOffset> for Standard {
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> UtcOffset {
- let seconds = rng.gen_range(-(Second.per(Day) as i32 - 1)..=(Second.per(Day) as i32 - 1));
- UtcOffset::__from_hms_unchecked(
- (seconds / Second.per(Hour) as i32) as _,
- ((seconds % Second.per(Hour) as i32) / Minute.per(Hour) as i32) as _,
- (seconds % Second.per(Minute) as i32) as _,
- )
+ UtcOffset::from_hms_ranged(rng.gen(), rng.gen(), rng.gen())
}
}
impl Distribution<PrimitiveDateTime> for Standard {
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> PrimitiveDateTime {
PrimitiveDateTime::new(Self.sample(rng), Self.sample(rng))
}
}
impl Distribution<OffsetDateTime> for Standard {
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> OffsetDateTime {
let date_time: PrimitiveDateTime = Self.sample(rng);
date_time.assume_offset(Self.sample(rng))
}
}
impl Distribution<Duration> for Standard {
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Duration {
- Duration::nanoseconds_i128(
- rng.gen_range(Duration::MIN.whole_nanoseconds()..=Duration::MAX.whole_nanoseconds()),
- )
+ Duration::new_ranged(rng.gen(), rng.gen())
}
}
impl Distribution<Weekday> for Standard {
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Weekday {
use Weekday::*;
match rng.gen_range(0u8..7) {
0 => Monday,
1 => Tuesday,
diff --git a/third_party/rust/time/src/serde/mod.rs b/third_party/rust/time/src/serde/mod.rs
index 7e3403fb3b..fadc44306e 100644
--- a/third_party/rust/time/src/serde/mod.rs
+++ b/third_party/rust/time/src/serde/mod.rs
@@ -15,20 +15,22 @@ macro_rules! item {
#[cfg(any(feature = "formatting", feature = "parsing"))]
pub mod iso8601;
#[cfg(any(feature = "formatting", feature = "parsing"))]
pub mod rfc2822;
#[cfg(any(feature = "formatting", feature = "parsing"))]
pub mod rfc3339;
pub mod timestamp;
mod visitor;
+#[cfg(feature = "serde-human-readable")]
+use alloc::string::ToString;
use core::marker::PhantomData;
#[cfg(feature = "serde-human-readable")]
use serde::ser::Error as _;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
/// Generate a custom serializer and deserializer from a format string or an existing format.
///
/// The syntax accepted by this macro is the same as [`format_description::parse()`], which can
/// be found in [the book](https://time-rs.github.io/book/api/format-description.html).
///
@@ -110,23 +112,23 @@ use serde::{Deserialize, Deserializer, Serialize, Serializer};
)]
#[cfg_attr(
all(feature = "formatting", not(feature = "parsing")),
doc = "use ::serde::Serialize;"
)]
#[cfg_attr(
all(not(feature = "formatting"), feature = "parsing"),
doc = "use ::serde::Deserialize;"
)]
/// use time::serde;
-/// use time::format_description::FormatItem;
+/// use time::format_description::BorrowedFormatItem;
///
-/// const DATE_TIME_FORMAT: &[FormatItem<'_>] = time::macros::format_description!(
+/// const DATE_TIME_FORMAT: &[BorrowedFormatItem<'_>] = time::macros::format_description!(
/// "hour=[hour], minute=[minute]"
/// );
///
/// // Makes a module `mod my_format { ... }`.
/// serde::format_description!(my_format, OffsetDateTime, DATE_TIME_FORMAT);
///
/// # #[allow(dead_code)]
#[cfg_attr(
all(feature = "formatting", feature = "parsing"),
doc = "#[derive(Serialize, Deserialize)]"
@@ -199,32 +201,32 @@ use serde::{Deserialize, Deserializer, Serialize, Serializer};
/// }
/// # fn main() {}
/// ```
///
/// [`format_description::parse()`]: crate::format_description::parse()
#[cfg(all(feature = "macros", any(feature = "formatting", feature = "parsing"),))]
pub use time_macros::serde_format_description as format_description;
use self::visitor::Visitor;
#[cfg(feature = "parsing")]
-use crate::format_description::{modifier, Component, FormatItem};
+use crate::format_description::{modifier, BorrowedFormatItem, Component};
use crate::{Date, Duration, Month, OffsetDateTime, PrimitiveDateTime, Time, UtcOffset, Weekday};
// region: Date
/// The format used when serializing and deserializing a human-readable `Date`.
#[cfg(feature = "parsing")]
-const DATE_FORMAT: &[FormatItem<'_>] = &[
- FormatItem::Component(Component::Year(modifier::Year::default())),
- FormatItem::Literal(b"-"),
- FormatItem::Component(Component::Month(modifier::Month::default())),
- FormatItem::Literal(b"-"),
- FormatItem::Component(Component::Day(modifier::Day::default())),
+const DATE_FORMAT: &[BorrowedFormatItem<'_>] = &[
+ BorrowedFormatItem::Component(Component::Year(modifier::Year::default())),
+ BorrowedFormatItem::Literal(b"-"),
+ BorrowedFormatItem::Component(Component::Month(modifier::Month::default())),
+ BorrowedFormatItem::Literal(b"-"),
+ BorrowedFormatItem::Component(Component::Day(modifier::Day::default())),
];
impl Serialize for Date {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
#[cfg(feature = "serde-human-readable")]
if serializer.is_human_readable() {
let Ok(s) = self.format(&DATE_FORMAT) else {
return Err(S::Error::custom("failed formatting `Date`"));
};
return serializer.serialize_str(&s);
@@ -268,26 +270,26 @@ impl<'a> Deserialize<'a> for Duration {
} else {
deserializer.deserialize_tuple(2, Visitor::<Self>(PhantomData))
}
}
}
// endregion Duration
// region: OffsetDateTime
/// The format used when serializing and deserializing a human-readable `OffsetDateTime`.
#[cfg(feature = "parsing")]
-const OFFSET_DATE_TIME_FORMAT: &[FormatItem<'_>] = &[
- FormatItem::Compound(DATE_FORMAT),
- FormatItem::Literal(b" "),
- FormatItem::Compound(TIME_FORMAT),
- FormatItem::Literal(b" "),
- FormatItem::Compound(UTC_OFFSET_FORMAT),
+const OFFSET_DATE_TIME_FORMAT: &[BorrowedFormatItem<'_>] = &[
+ BorrowedFormatItem::Compound(DATE_FORMAT),
+ BorrowedFormatItem::Literal(b" "),
+ BorrowedFormatItem::Compound(TIME_FORMAT),
+ BorrowedFormatItem::Literal(b" "),
+ BorrowedFormatItem::Compound(UTC_OFFSET_FORMAT),
];
impl Serialize for OffsetDateTime {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
#[cfg(feature = "serde-human-readable")]
if serializer.is_human_readable() {
let Ok(s) = self.format(&OFFSET_DATE_TIME_FORMAT) else {
return Err(S::Error::custom("failed formatting `OffsetDateTime`"));
};
return serializer.serialize_str(&s);
@@ -315,24 +317,24 @@ impl<'a> Deserialize<'a> for OffsetDateTime {
} else {
deserializer.deserialize_tuple(9, Visitor::<Self>(PhantomData))
}
}
}
// endregion OffsetDateTime
// region: PrimitiveDateTime
/// The format used when serializing and deserializing a human-readable `PrimitiveDateTime`.
#[cfg(feature = "parsing")]
-const PRIMITIVE_DATE_TIME_FORMAT: &[FormatItem<'_>] = &[
- FormatItem::Compound(DATE_FORMAT),
- FormatItem::Literal(b" "),
- FormatItem::Compound(TIME_FORMAT),
+const PRIMITIVE_DATE_TIME_FORMAT: &[BorrowedFormatItem<'_>] = &[
+ BorrowedFormatItem::Compound(DATE_FORMAT),
+ BorrowedFormatItem::Literal(b" "),
+ BorrowedFormatItem::Compound(TIME_FORMAT),
];
impl Serialize for PrimitiveDateTime {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
#[cfg(feature = "serde-human-readable")]
if serializer.is_human_readable() {
let Ok(s) = self.format(&PRIMITIVE_DATE_TIME_FORMAT) else {
return Err(S::Error::custom("failed formatting `PrimitiveDateTime`"));
};
return serializer.serialize_str(&s);
@@ -357,28 +359,28 @@ impl<'a> Deserialize<'a> for PrimitiveDateTime {
} else {
deserializer.deserialize_tuple(6, Visitor::<Self>(PhantomData))
}
}
}
// endregion PrimitiveDateTime
// region: Time
/// The format used when serializing and deserializing a human-readable `Time`.
#[cfg(feature = "parsing")]
-const TIME_FORMAT: &[FormatItem<'_>] = &[
- FormatItem::Component(Component::Hour(<modifier::Hour>::default())),
- FormatItem::Literal(b":"),
- FormatItem::Component(Component::Minute(<modifier::Minute>::default())),
- FormatItem::Literal(b":"),
- FormatItem::Component(Component::Second(<modifier::Second>::default())),
- FormatItem::Literal(b"."),
- FormatItem::Component(Component::Subsecond(<modifier::Subsecond>::default())),
+const TIME_FORMAT: &[BorrowedFormatItem<'_>] = &[
+ BorrowedFormatItem::Component(Component::Hour(modifier::Hour::default())),
+ BorrowedFormatItem::Literal(b":"),
+ BorrowedFormatItem::Component(Component::Minute(modifier::Minute::default())),
+ BorrowedFormatItem::Literal(b":"),
+ BorrowedFormatItem::Component(Component::Second(modifier::Second::default())),
+ BorrowedFormatItem::Literal(b"."),
+ BorrowedFormatItem::Component(Component::Subsecond(modifier::Subsecond::default())),
];
impl Serialize for Time {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
#[cfg(feature = "serde-human-readable")]
if serializer.is_human_readable() {
let Ok(s) = self.format(&TIME_FORMAT) else {
return Err(S::Error::custom("failed formatting `Time`"));
};
return serializer.serialize_str(&s);
@@ -395,28 +397,34 @@ impl<'a> Deserialize<'a> for Time {
} else {
deserializer.deserialize_tuple(4, Visitor::<Self>(PhantomData))
}
}
}
// endregion Time
// region: UtcOffset
/// The format used when serializing and deserializing a human-readable `UtcOffset`.
#[cfg(feature = "parsing")]
-const UTC_OFFSET_FORMAT: &[FormatItem<'_>] = &[
- FormatItem::Component(Component::OffsetHour(modifier::OffsetHour::default())),
- FormatItem::Optional(&FormatItem::Compound(&[
- FormatItem::Literal(b":"),
- FormatItem::Component(Component::OffsetMinute(modifier::OffsetMinute::default())),
- FormatItem::Optional(&FormatItem::Compound(&[
- FormatItem::Literal(b":"),
- FormatItem::Component(Component::OffsetSecond(modifier::OffsetSecond::default())),
+const UTC_OFFSET_FORMAT: &[BorrowedFormatItem<'_>] = &[
+ BorrowedFormatItem::Component(Component::OffsetHour({
+ let mut m = modifier::OffsetHour::default();
+ m.sign_is_mandatory = true;
+ m
+ })),
+ BorrowedFormatItem::Optional(&BorrowedFormatItem::Compound(&[
+ BorrowedFormatItem::Literal(b":"),
+ BorrowedFormatItem::Component(Component::OffsetMinute(modifier::OffsetMinute::default())),
+ BorrowedFormatItem::Optional(&BorrowedFormatItem::Compound(&[
+ BorrowedFormatItem::Literal(b":"),
+ BorrowedFormatItem::Component(Component::OffsetSecond(
+ modifier::OffsetSecond::default(),
+ )),
])),
])),
];
impl Serialize for UtcOffset {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
#[cfg(feature = "serde-human-readable")]
if serializer.is_human_readable() {
let Ok(s) = self.format(&UTC_OFFSET_FORMAT) else {
return Err(S::Error::custom("failed formatting `UtcOffset`"));
@@ -472,21 +480,21 @@ impl<'a> Deserialize<'a> for Weekday {
// region: Month
impl Serialize for Month {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
#[cfg(feature = "serde-human-readable")]
if serializer.is_human_readable() {
#[cfg(not(feature = "std"))]
use alloc::string::String;
return self.to_string().serialize(serializer);
}
- (*self as u8).serialize(serializer)
+ u8::from(*self).serialize(serializer)
}
}
impl<'a> Deserialize<'a> for Month {
fn deserialize<D: Deserializer<'a>>(deserializer: D) -> Result<Self, D::Error> {
if cfg!(feature = "serde-human-readable") && deserializer.is_human_readable() {
deserializer.deserialize_any(Visitor::<Self>(PhantomData))
} else {
deserializer.deserialize_u8(Visitor::<Self>(PhantomData))
}
diff --git a/third_party/rust/time/src/serde/timestamp/microseconds.rs b/third_party/rust/time/src/serde/timestamp/microseconds.rs
new file mode 100644
index 0000000000..65c603ec35
--- /dev/null
+++ b/third_party/rust/time/src/serde/timestamp/microseconds.rs
@@ -0,0 +1,63 @@
+//! Treat an [`OffsetDateTime`] as a [Unix timestamp] with microseconds for
+//! the purposes of serde.
+//!
+//! Use this module in combination with serde's [`#[with]`][with] attribute.
+//!
+//! When deserializing, the offset is assumed to be UTC.
+//!
+//! [Unix timestamp]: https://en.wikipedia.org/wiki/Unix_time
+//! [with]: https://serde.rs/field-attrs.html#with
+
+use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
+
+use crate::OffsetDateTime;
+
+/// Serialize an `OffsetDateTime` as its Unix timestamp with microseconds
+pub fn serialize<S: Serializer>(
+ datetime: &OffsetDateTime,
+ serializer: S,
+) -> Result<S::Ok, S::Error> {
+ let timestamp = datetime.unix_timestamp_nanos() / 1_000;
+ timestamp.serialize(serializer)
+}
+
+/// Deserialize an `OffsetDateTime` from its Unix timestamp with microseconds
+pub fn deserialize<'a, D: Deserializer<'a>>(deserializer: D) -> Result<OffsetDateTime, D::Error> {
+ let value: i128 = <_>::deserialize(deserializer)?;
+ OffsetDateTime::from_unix_timestamp_nanos(value * 1_000)
+ .map_err(|err| de::Error::invalid_value(de::Unexpected::Signed(err.value), &err))
+}
+
+/// Treat an `Option<OffsetDateTime>` as a [Unix timestamp] with microseconds
+/// for the purposes of serde.
+///
+/// Use this module in combination with serde's [`#[with]`][with] attribute.
+///
+/// When deserializing, the offset is assumed to be UTC.
+///
+/// [Unix timestamp]: https://en.wikipedia.org/wiki/Unix_time
+/// [with]: https://serde.rs/field-attrs.html#with
+pub mod option {
+ #[allow(clippy::wildcard_imports)]
+ use super::*;
+
+ /// Serialize an `Option<OffsetDateTime>` as its Unix timestamp with microseconds
+ pub fn serialize<S: Serializer>(
+ option: &Option<OffsetDateTime>,
+ serializer: S,
+ ) -> Result<S::Ok, S::Error> {
+ option
+ .map(|timestamp| timestamp.unix_timestamp_nanos() / 1_000)
+ .serialize(serializer)
+ }
+
+ /// Deserialize an `Option<OffsetDateTime>` from its Unix timestamp with microseconds
+ pub fn deserialize<'a, D: Deserializer<'a>>(
+ deserializer: D,
+ ) -> Result<Option<OffsetDateTime>, D::Error> {
+ Option::deserialize(deserializer)?
+ .map(|value: i128| OffsetDateTime::from_unix_timestamp_nanos(value * 1_000))
+ .transpose()
+ .map_err(|err| de::Error::invalid_value(de::Unexpected::Signed(err.value), &err))
+ }
+}
diff --git a/third_party/rust/time/src/serde/timestamp/milliseconds.rs b/third_party/rust/time/src/serde/timestamp/milliseconds.rs
new file mode 100644
index 0000000000..e571b6c9e2
--- /dev/null
+++ b/third_party/rust/time/src/serde/timestamp/milliseconds.rs
@@ -0,0 +1,63 @@
+//! Treat an [`OffsetDateTime`] as a [Unix timestamp] with milliseconds for
+//! the purposes of serde.
+//!
+//! Use this module in combination with serde's [`#[with]`][with] attribute.
+//!
+//! When deserializing, the offset is assumed to be UTC.
+//!
+//! [Unix timestamp]: https://en.wikipedia.org/wiki/Unix_time
+//! [with]: https://serde.rs/field-attrs.html#with
+
+use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
+
+use crate::OffsetDateTime;
+
+/// Serialize an `OffsetDateTime` as its Unix timestamp with milliseconds
+pub fn serialize<S: Serializer>(
+ datetime: &OffsetDateTime,
+ serializer: S,
+) -> Result<S::Ok, S::Error> {
+ let timestamp = datetime.unix_timestamp_nanos() / 1_000_000;
+ timestamp.serialize(serializer)
+}
+
+/// Deserialize an `OffsetDateTime` from its Unix timestamp with milliseconds
+pub fn deserialize<'a, D: Deserializer<'a>>(deserializer: D) -> Result<OffsetDateTime, D::Error> {
+ let value: i128 = <_>::deserialize(deserializer)?;
+ OffsetDateTime::from_unix_timestamp_nanos(value * 1_000_000)
+ .map_err(|err| de::Error::invalid_value(de::Unexpected::Signed(err.value), &err))
+}
+
+/// Treat an `Option<OffsetDateTime>` as a [Unix timestamp] with milliseconds
+/// for the purposes of serde.
+///
+/// Use this module in combination with serde's [`#[with]`][with] attribute.
+///
+/// When deserializing, the offset is assumed to be UTC.
+///
+/// [Unix timestamp]: https://en.wikipedia.org/wiki/Unix_time
+/// [with]: https://serde.rs/field-attrs.html#with
+pub mod option {
+ #[allow(clippy::wildcard_imports)]
+ use super::*;
+
+ /// Serialize an `Option<OffsetDateTime>` as its Unix timestamp with milliseconds
+ pub fn serialize<S: Serializer>(
+ option: &Option<OffsetDateTime>,
+ serializer: S,
+ ) -> Result<S::Ok, S::Error> {
+ option
+ .map(|timestamp| timestamp.unix_timestamp_nanos() / 1_000_000)
+ .serialize(serializer)
+ }
+
+ /// Deserialize an `Option<OffsetDateTime>` from its Unix timestamp with milliseconds
+ pub fn deserialize<'a, D: Deserializer<'a>>(
+ deserializer: D,
+ ) -> Result<Option<OffsetDateTime>, D::Error> {
+ Option::deserialize(deserializer)?
+ .map(|value: i128| OffsetDateTime::from_unix_timestamp_nanos(value * 1_000_000))
+ .transpose()
+ .map_err(|err| de::Error::invalid_value(de::Unexpected::Signed(err.value), &err))
+ }
+}
diff --git a/third_party/rust/time/src/serde/timestamp.rs b/third_party/rust/time/src/serde/timestamp/mod.rs
similarity index 97%
rename from third_party/rust/time/src/serde/timestamp.rs
rename to third_party/rust/time/src/serde/timestamp/mod.rs
index d86e6b9336..6dd0db03cd 100644
--- a/third_party/rust/time/src/serde/timestamp.rs
+++ b/third_party/rust/time/src/serde/timestamp/mod.rs
@@ -1,19 +1,23 @@
//! Treat an [`OffsetDateTime`] as a [Unix timestamp] for the purposes of serde.
//!
//! Use this module in combination with serde's [`#[with]`][with] attribute.
//!
//! When deserializing, the offset is assumed to be UTC.
//!
//! [Unix timestamp]: https://en.wikipedia.org/wiki/Unix_time
//! [with]: https://serde.rs/field-attrs.html#with
+pub mod microseconds;
+pub mod milliseconds;
+pub mod nanoseconds;
+
use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
use crate::OffsetDateTime;
/// Serialize an `OffsetDateTime` as its Unix timestamp
pub fn serialize<S: Serializer>(
datetime: &OffsetDateTime,
serializer: S,
) -> Result<S::Ok, S::Error> {
datetime.unix_timestamp().serialize(serializer)
diff --git a/third_party/rust/time/src/serde/timestamp/nanoseconds.rs b/third_party/rust/time/src/serde/timestamp/nanoseconds.rs
new file mode 100644
index 0000000000..c71d1e7cdb
--- /dev/null
+++ b/third_party/rust/time/src/serde/timestamp/nanoseconds.rs
@@ -0,0 +1,61 @@
+//! Treat an [`OffsetDateTime`] as a [Unix timestamp] with nanoseconds for
+//! the purposes of serde.
+//!
+//! Use this module in combination with serde's [`#[with]`][with] attribute.
+//!
+//! When deserializing, the offset is assumed to be UTC.
+//!
+//! [Unix timestamp]: https://en.wikipedia.org/wiki/Unix_time
+//! [with]: https://serde.rs/field-attrs.html#with
+
+use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
+
+use crate::OffsetDateTime;
+
+/// Serialize an `OffsetDateTime` as its Unix timestamp with nanoseconds
+pub fn serialize<S: Serializer>(
+ datetime: &OffsetDateTime,
+ serializer: S,
+) -> Result<S::Ok, S::Error> {
+ datetime.unix_timestamp_nanos().serialize(serializer)
+}
+
+/// Deserialize an `OffsetDateTime` from its Unix timestamp with nanoseconds
+pub fn deserialize<'a, D: Deserializer<'a>>(deserializer: D) -> Result<OffsetDateTime, D::Error> {
+ OffsetDateTime::from_unix_timestamp_nanos(<_>::deserialize(deserializer)?)
+ .map_err(|err| de::Error::invalid_value(de::Unexpected::Signed(err.value), &err))
+}
+
+/// Treat an `Option<OffsetDateTime>` as a [Unix timestamp] with nanoseconds
+/// for the purposes of serde.
+///
+/// Use this module in combination with serde's [`#[with]`][with] attribute.
+///
+/// When deserializing, the offset is assumed to be UTC.
+///
+/// [Unix timestamp]: https://en.wikipedia.org/wiki/Unix_time
+/// [with]: https://serde.rs/field-attrs.html#with
+pub mod option {
+ #[allow(clippy::wildcard_imports)]
+ use super::*;
+
+ /// Serialize an `Option<OffsetDateTime>` as its Unix timestamp with nanoseconds
+ pub fn serialize<S: Serializer>(
+ option: &Option<OffsetDateTime>,
+ serializer: S,
+ ) -> Result<S::Ok, S::Error> {
+ option
+ .map(OffsetDateTime::unix_timestamp_nanos)
+ .serialize(serializer)
+ }
+
+ /// Deserialize an `Option<OffsetDateTime>` from its Unix timestamp with nanoseconds
+ pub fn deserialize<'a, D: Deserializer<'a>>(
+ deserializer: D,
+ ) -> Result<Option<OffsetDateTime>, D::Error> {
+ Option::deserialize(deserializer)?
+ .map(OffsetDateTime::from_unix_timestamp_nanos)
+ .transpose()
+ .map_err(|err| de::Error::invalid_value(de::Unexpected::Signed(err.value), &err))
+ }
+}
diff --git a/third_party/rust/time/src/serde/visitor.rs b/third_party/rust/time/src/serde/visitor.rs
index fcf718bb45..3a4311ecb2 100644
--- a/third_party/rust/time/src/serde/visitor.rs
+++ b/third_party/rust/time/src/serde/visitor.rs
@@ -174,21 +174,21 @@ impl<'a> de::Visitor<'a> for Visitor<UtcOffset> {
minutes = min;
if let Ok(Some(sec)) = seq.next_element() {
seconds = sec;
}
};
UtcOffset::from_hms(hours, minutes, seconds).map_err(ComponentRange::into_de_error)
}
}
-impl<'a> de::Visitor<'a> for Visitor<Weekday> {
+impl de::Visitor<'_> for Visitor<Weekday> {
type Value = Weekday;
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str("a `Weekday`")
}
fn visit_str<E: de::Error>(self, value: &str) -> Result<Weekday, E> {
match value {
"Monday" => Ok(Weekday::Monday),
"Tuesday" => Ok(Weekday::Tuesday),
@@ -211,21 +211,21 @@ impl<'a> de::Visitor<'a> for Visitor<Weekday> {
6 => Ok(Weekday::Saturday),
7 => Ok(Weekday::Sunday),
_ => Err(E::invalid_value(
de::Unexpected::Unsigned(value),
&"a value in the range 1..=7",
)),
}
}
}
-impl<'a> de::Visitor<'a> for Visitor<Month> {
+impl de::Visitor<'_> for Visitor<Month> {
type Value = Month;
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str("a `Month`")
}
fn visit_str<E: de::Error>(self, value: &str) -> Result<Month, E> {
match value {
"January" => Ok(Month::January),
"February" => Ok(Month::February),
@@ -262,21 +262,21 @@ impl<'a> de::Visitor<'a> for Visitor<Month> {
&"a value in the range 1..=12",
)),
}
}
}
/// Implement a visitor for a well-known format.
macro_rules! well_known {
($article:literal, $name:literal, $($ty:tt)+) => {
#[cfg(feature = "parsing")]
- impl<'a> de::Visitor<'a> for Visitor<$($ty)+> {
+ impl de::Visitor<'_> for Visitor<$($ty)+> {
type Value = OffsetDateTime;
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str(concat!($article, " ", $name, "-formatted `OffsetDateTime`"))
}
fn visit_str<E: de::Error>(self, value: &str) -> Result<OffsetDateTime, E> {
OffsetDateTime::parse(value, &$($ty)+).map_err(E::custom)
}
}
diff --git a/third_party/rust/time/src/sys/local_offset_at/unix.rs b/third_party/rust/time/src/sys/local_offset_at/unix.rs
index f4a8089328..eddd8e2a95 100644
--- a/third_party/rust/time/src/sys/local_offset_at/unix.rs
+++ b/third_party/rust/time/src/sys/local_offset_at/unix.rs
@@ -8,23 +8,20 @@ use crate::{OffsetDateTime, UtcOffset};
/// Whether the operating system has a thread-safe environment. This allows bypassing the check for
/// if the process is multi-threaded.
// This is the same value as `cfg!(target_os = "x")`.
// Use byte-strings to work around current limitations of const eval.
const OS_HAS_THREAD_SAFE_ENVIRONMENT: bool = match std::env::consts::OS.as_bytes() {
// https://github.com/illumos/illumos-gate/blob/0fb96ba1f1ce26ff8b286f8f928769a6afcb00a6/usr/src/lib/libc/port/gen/getenv.c
b"illumos"
// https://github.com/NetBSD/src/blob/f45028636a44111bc4af44d460924958a4460844/lib/libc/stdlib/getenv.c
// https://github.com/NetBSD/src/blob/f45028636a44111bc4af44d460924958a4460844/lib/libc/stdlib/setenv.c
| b"netbsd"
- // https://github.com/apple-oss-distributions/Libc/blob/d526593760f0f79dfaeb8b96c3c8a42c791156ff/stdlib/FreeBSD/getenv.c
- // https://github.com/apple-oss-distributions/Libc/blob/d526593760f0f79dfaeb8b96c3c8a42c791156ff/stdlib/FreeBSD/setenv.c
- | b"macos"
=> true,
_ => false,
};
/// Convert the given Unix timestamp to a `libc::tm`. Returns `None` on any error.
///
/// # Safety
///
/// This method must only be called when the process is single-threaded.
///
diff --git a/third_party/rust/time/src/sys/local_offset_at/wasm_js.rs b/third_party/rust/time/src/sys/local_offset_at/wasm_js.rs
index dfbe063a2a..a985e7c484 100644
--- a/third_party/rust/time/src/sys/local_offset_at/wasm_js.rs
+++ b/third_party/rust/time/src/sys/local_offset_at/wasm_js.rs
@@ -1,13 +1,16 @@
+use num_conv::prelude::*;
+
use crate::convert::*;
use crate::{OffsetDateTime, UtcOffset};
/// Obtain the system's UTC offset.
pub(super) fn local_offset_at(datetime: OffsetDateTime) -> Option<UtcOffset> {
let js_date: js_sys::Date = datetime.into();
// The number of minutes returned by getTimezoneOffset() is positive if the local time zone
// is behind UTC, and negative if the local time zone is ahead of UTC. For example,
// for UTC+10, -600 will be returned.
- let timezone_offset = (js_date.get_timezone_offset() as i32) * -(Minute.per(Hour) as i32);
+ let timezone_offset =
+ (js_date.get_timezone_offset() as i32) * -Minute::per(Hour).cast_signed().extend::<i32>();
UtcOffset::from_whole_seconds(timezone_offset).ok()
}
diff --git a/third_party/rust/time/src/sys/local_offset_at/windows.rs b/third_party/rust/time/src/sys/local_offset_at/windows.rs
index a4d5882d67..d144292092 100644
--- a/third_party/rust/time/src/sys/local_offset_at/windows.rs
+++ b/third_party/rust/time/src/sys/local_offset_at/windows.rs
@@ -1,14 +1,16 @@
//! Get the system's UTC offset on Windows.
use core::mem::MaybeUninit;
+use num_conv::prelude::*;
+
use crate::convert::*;
use crate::{OffsetDateTime, UtcOffset};
// ffi: WINAPI FILETIME struct
#[repr(C)]
#[allow(non_snake_case, clippy::missing_docs_in_private_items)]
struct FileTime {
dwLowDateTime: u32,
dwHighDateTime: u32,
}
@@ -50,35 +52,36 @@ fn systemtime_to_filetime(systime: &SystemTime) -> Option<FileTime> {
None
} else {
// Safety: The call succeeded.
Some(unsafe { ft.assume_init() })
}
}
/// Convert a `FILETIME` to an `i64`, representing a number of seconds.
fn filetime_to_secs(filetime: &FileTime) -> i64 {
/// FILETIME represents 100-nanosecond intervals
- const FT_TO_SECS: i64 = Nanosecond.per(Second) as i64 / 100;
- ((filetime.dwHighDateTime as i64) << 32 | filetime.dwLowDateTime as i64) / FT_TO_SECS
+ const FT_TO_SECS: u64 = Nanosecond::per(Second) as u64 / 100;
+ ((filetime.dwHighDateTime.extend::<u64>() << 32 | filetime.dwLowDateTime.extend::<u64>())
+ / FT_TO_SECS) as i64
}
/// Convert an [`OffsetDateTime`] to a `SYSTEMTIME`.
fn offset_to_systemtime(datetime: OffsetDateTime) -> SystemTime {
let (_, month, day_of_month) = datetime.to_offset(UtcOffset::UTC).date().to_calendar_date();
SystemTime {
- wYear: datetime.year() as _,
- wMonth: month as _,
- wDay: day_of_month as _,
+ wYear: datetime.year().cast_unsigned().truncate(),
+ wMonth: u8::from(month).extend(),
+ wDay: day_of_month.extend(),
wDayOfWeek: 0, // ignored
- wHour: datetime.hour() as _,
- wMinute: datetime.minute() as _,
- wSecond: datetime.second() as _,
+ wHour: datetime.hour().extend(),
+ wMinute: datetime.minute().extend(),
+ wSecond: datetime.second().extend(),
wMilliseconds: datetime.millisecond(),
}
}
/// Obtain the system's UTC offset.
pub(super) fn local_offset_at(datetime: OffsetDateTime) -> Option<UtcOffset> {
// This function falls back to UTC if any system call fails.
let systime_utc = offset_to_systemtime(datetime.to_offset(UtcOffset::UTC));
// Safety: `local_time` is only read if it is properly initialized, and
diff --git a/third_party/rust/time/src/tests.rs b/third_party/rust/time/src/tests.rs
index 030c473bed..4ccd87b180 100644
--- a/third_party/rust/time/src/tests.rs
+++ b/third_party/rust/time/src/tests.rs
@@ -18,23 +18,24 @@
clippy::clone_on_copy,
clippy::cognitive_complexity,
clippy::std_instead_of_core
)]
//! Tests for internal details.
//!
//! This module should only be used when it is not possible to test the implementation in a
//! reasonable manner externally.
+use std::format;
use std::num::NonZeroU8;
-use crate::formatting::DigitCount;
+use crate::ext::DigitCount;
use crate::parsing::combinator::rfc::iso8601;
use crate::parsing::shim::Integer;
use crate::{duration, parsing};
#[test]
fn digit_count() {
assert_eq!(1_u8.num_digits(), 1);
assert_eq!(9_u8.num_digits(), 1);
assert_eq!(10_u8.num_digits(), 2);
assert_eq!(99_u8.num_digits(), 2);
@@ -64,28 +65,20 @@ fn digit_count() {
assert_eq!(999_999_u32.num_digits(), 6);
assert_eq!(1_000_000_u32.num_digits(), 7);
assert_eq!(9_999_999_u32.num_digits(), 7);
assert_eq!(10_000_000_u32.num_digits(), 8);
assert_eq!(99_999_999_u32.num_digits(), 8);
assert_eq!(100_000_000_u32.num_digits(), 9);
assert_eq!(999_999_999_u32.num_digits(), 9);
assert_eq!(1_000_000_000_u32.num_digits(), 10);
}
-#[test]
-fn default() {
- assert_eq!(
- duration::Padding::Optimize.clone(),
- duration::Padding::default()
- );
-}
-
#[test]
fn debug() {
let _ = format!("{:?}", duration::Padding::Optimize);
let _ = format!("{:?}", parsing::ParsedItem(b"", 0));
let _ = format!("{:?}", parsing::component::Period::Am);
let _ = format!("{:?}", iso8601::ExtendedKind::Basic);
}
#[test]
fn clone() {
diff --git a/third_party/rust/time/src/time.rs b/third_party/rust/time/src/time.rs
index 87b465bb9a..2bd2fefe8e 100644
--- a/third_party/rust/time/src/time.rs
+++ b/third_party/rust/time/src/time.rs
@@ -1,116 +1,239 @@
//! The [`Time`] struct and its associated `impl`s.
+#[cfg(feature = "formatting")]
+use alloc::string::String;
use core::fmt;
use core::ops::{Add, Sub};
use core::time::Duration as StdDuration;
#[cfg(feature = "formatting")]
use std::io;
+use deranged::{RangedU32, RangedU8};
+use num_conv::prelude::*;
+use powerfmt::ext::FormatterExt;
+use powerfmt::smart_display::{self, FormatterOptions, Metadata, SmartDisplay};
+
use crate::convert::*;
#[cfg(feature = "formatting")]
use crate::formatting::Formattable;
+use crate::internal_macros::{cascade, ensure_ranged, impl_add_assign, impl_sub_assign};
#[cfg(feature = "parsing")]
use crate::parsing::Parsable;
use crate::util::DateAdjustment;
use crate::{error, Duration};
/// By explicitly inserting this enum where padding is expected, the compiler is able to better
/// perform niche value optimization.
#[repr(u8)]
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub(crate) enum Padding {
#[allow(clippy::missing_docs_in_private_items)]
Optimize,
}
+/// The type of the `hour` field of `Time`.
+type Hours = RangedU8<0, { Hour::per(Day) - 1 }>;
+/// The type of the `minute` field of `Time`.
+type Minutes = RangedU8<0, { Minute::per(Hour) - 1 }>;
+/// The type of the `second` field of `Time`.
+type Seconds = RangedU8<0, { Second::per(Minute) - 1 }>;
+/// The type of the `nanosecond` field of `Time`.
+type Nanoseconds = RangedU32<0, { Nanosecond::per(Second) - 1 }>;
+
/// The clock time within a given date. Nanosecond precision.
///
/// All minutes are assumed to have exactly 60 seconds; no attempt is made to handle leap seconds
/// (either positive or negative).
///
/// When comparing two `Time`s, they are assumed to be in the same calendar date.
-#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
+#[derive(Clone, Copy, Eq)]
+#[repr(C)]
pub struct Time {
+ // The order of this struct's fields matter!
+ // Do not change them.
+
+ // Little endian version
+ #[cfg(target_endian = "little")]
+ #[allow(clippy::missing_docs_in_private_items)]
+ nanosecond: Nanoseconds,
+ #[cfg(target_endian = "little")]
#[allow(clippy::missing_docs_in_private_items)]
- hour: u8,
+ second: Seconds,
+ #[cfg(target_endian = "little")]
#[allow(clippy::missing_docs_in_private_items)]
- minute: u8,
+ minute: Minutes,
+ #[cfg(target_endian = "little")]
#[allow(clippy::missing_docs_in_private_items)]
- second: u8,
+ hour: Hours,
+ #[cfg(target_endian = "little")]
#[allow(clippy::missing_docs_in_private_items)]
- nanosecond: u32,
+ padding: Padding,
+
+ // Big endian version
+ #[cfg(target_endian = "big")]
#[allow(clippy::missing_docs_in_private_items)]
padding: Padding,
+ #[cfg(target_endian = "big")]
+ #[allow(clippy::missing_docs_in_private_items)]
+ hour: Hours,
+ #[cfg(target_endian = "big")]
+ #[allow(clippy::missing_docs_in_private_items)]
+ minute: Minutes,
+ #[cfg(target_endian = "big")]
+ #[allow(clippy::missing_docs_in_private_items)]
+ second: Seconds,
+ #[cfg(target_endian = "big")]
+ #[allow(clippy::missing_docs_in_private_items)]
+ nanosecond: Nanoseconds,
+}
+
+impl core::hash::Hash for Time {
+ fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
+ self.as_u64().hash(state)
+ }
+}
+
+impl PartialEq for Time {
+ fn eq(&self, other: &Self) -> bool {
+ self.as_u64().eq(&other.as_u64())
+ }
+}
+
+impl PartialOrd for Time {
+ fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
+ Some(self.cmp(other))
+ }
+}
+
+impl Ord for Time {
+ fn cmp(&self, other: &Self) -> core::cmp::Ordering {
+ self.as_u64().cmp(&other.as_u64())
+ }
}
impl Time {
+ /// Provides an u64 based representation **of the correct endianness**
+ ///
+ /// This representation can be used to do comparisons equality testing or hashing.
+ const fn as_u64(self) -> u64 {
+ let nano_bytes = self.nanosecond.get().to_ne_bytes();
+
+ #[cfg(target_endian = "big")]
+ return u64::from_be_bytes([
+ self.padding as u8,
+ self.hour.get(),
+ self.minute.get(),
+ self.second.get(),
+ nano_bytes[0],
+ nano_bytes[1],
+ nano_bytes[2],
+ nano_bytes[3],
+ ]);
+
+ #[cfg(target_endian = "little")]
+ return u64::from_le_bytes([
+ nano_bytes[0],
+ nano_bytes[1],
+ nano_bytes[2],
+ nano_bytes[3],
+ self.second.get(),
+ self.minute.get(),
+ self.hour.get(),
+ self.padding as u8,
+ ]);
+ }
+
/// Create a `Time` that is exactly midnight.
///
/// ```rust
/// # use time::Time;
/// # use time_macros::time;
/// assert_eq!(Time::MIDNIGHT, time!(0:00));
/// ```
- pub const MIDNIGHT: Self = Self::__from_hms_nanos_unchecked(0, 0, 0, 0);
+ pub const MIDNIGHT: Self = Self::MIN;
/// The smallest value that can be represented by `Time`.
///
/// `00:00:00.0`
- pub(crate) const MIN: Self = Self::__from_hms_nanos_unchecked(0, 0, 0, 0);
+ pub(crate) const MIN: Self =
+ Self::from_hms_nanos_ranged(Hours::MIN, Minutes::MIN, Seconds::MIN, Nanoseconds::MIN);
/// The largest value that can be represented by `Time`.
///
/// `23:59:59.999_999_999`
- pub(crate) const MAX: Self = Self::__from_hms_nanos_unchecked(23, 59, 59, 999_999_999);
+ pub(crate) const MAX: Self =
+ Self::from_hms_nanos_ranged(Hours::MAX, Minutes::MAX, Seconds::MAX, Nanoseconds::MAX);
// region: constructors
/// Create a `Time` from its components.
+ ///
+ /// # Safety
+ ///
+ /// - `hours` must be in the range `0..=23`.
+ /// - `minutes` must be in the range `0..=59`.
+ /// - `seconds` must be in the range `0..=59`.
+ /// - `nanoseconds` must be in the range `0..=999_999_999`.
#[doc(hidden)]
- pub const fn __from_hms_nanos_unchecked(
+ pub const unsafe fn __from_hms_nanos_unchecked(
hour: u8,
minute: u8,
second: u8,
nanosecond: u32,
) -> Self {
- debug_assert!(hour < Hour.per(Day));
- debug_assert!(minute < Minute.per(Hour));
- debug_assert!(second < Second.per(Minute));
- debug_assert!(nanosecond < Nanosecond.per(Second));
-
- Self {
- hour,
- minute,
- second,
- nanosecond,
- padding: Padding::Optimize,
+ // Safety: The caller must uphold the safety invariants.
+ unsafe {
+ Self::from_hms_nanos_ranged(
+ Hours::new_unchecked(hour),
+ Minutes::new_unchecked(minute),
+ Seconds::new_unchecked(second),
+ Nanoseconds::new_unchecked(nanosecond),
+ )
}
}
/// Attempt to create a `Time` from the hour, minute, and second.
///
/// ```rust
/// # use time::Time;
/// assert!(Time::from_hms(1, 2, 3).is_ok());
/// ```
///
/// ```rust
/// # use time::Time;
/// assert!(Time::from_hms(24, 0, 0).is_err()); // 24 isn't a valid hour.
/// assert!(Time::from_hms(0, 60, 0).is_err()); // 60 isn't a valid minute.
/// assert!(Time::from_hms(0, 0, 60).is_err()); // 60 isn't a valid second.
/// ```
pub const fn from_hms(hour: u8, minute: u8, second: u8) -> Result<Self, error::ComponentRange> {
- ensure_value_in_range!(hour in 0 => Hour.per(Day) - 1);
- ensure_value_in_range!(minute in 0 => Minute.per(Hour) - 1);
- ensure_value_in_range!(second in 0 => Second.per(Minute) - 1);
- Ok(Self::__from_hms_nanos_unchecked(hour, minute, second, 0))
+ Ok(Self::from_hms_nanos_ranged(
+ ensure_ranged!(Hours: hour),
+ ensure_ranged!(Minutes: minute),
+ ensure_ranged!(Seconds: second),
+ Nanoseconds::MIN,
+ ))
+ }
+
+ /// Create a `Time` from the hour, minute, second, and nanosecond.
+ pub(crate) const fn from_hms_nanos_ranged(
+ hour: Hours,
+ minute: Minutes,
+ second: Seconds,
+ nanosecond: Nanoseconds,
+ ) -> Self {
+ Self {
+ hour,
+ minute,
+ second,
+ nanosecond,
+ padding: Padding::Optimize,
+ }
}
/// Attempt to create a `Time` from the hour, minute, second, and millisecond.
///
/// ```rust
/// # use time::Time;
/// assert!(Time::from_hms_milli(1, 2, 3, 4).is_ok());
/// ```
///
/// ```rust
@@ -119,29 +242,25 @@ impl Time {
/// assert!(Time::from_hms_milli(0, 60, 0, 0).is_err()); // 60 isn't a valid minute.
/// assert!(Time::from_hms_milli(0, 0, 60, 0).is_err()); // 60 isn't a valid second.
/// assert!(Time::from_hms_milli(0, 0, 0, 1_000).is_err()); // 1_000 isn't a valid millisecond.
/// ```
pub const fn from_hms_milli(
hour: u8,
minute: u8,
second: u8,
millisecond: u16,
) -> Result<Self, error::ComponentRange> {
- ensure_value_in_range!(hour in 0 => Hour.per(Day) - 1);
- ensure_value_in_range!(minute in 0 => Minute.per(Hour) - 1);
- ensure_value_in_range!(second in 0 => Second.per(Minute) - 1);
- ensure_value_in_range!(millisecond in 0 => Millisecond.per(Second) - 1);
- Ok(Self::__from_hms_nanos_unchecked(
- hour,
- minute,
- second,
- millisecond as u32 * Nanosecond.per(Millisecond),
+ Ok(Self::from_hms_nanos_ranged(
+ ensure_ranged!(Hours: hour),
+ ensure_ranged!(Minutes: minute),
+ ensure_ranged!(Seconds: second),
+ ensure_ranged!(Nanoseconds: millisecond as u32 * Nanosecond::per(Millisecond)),
))
}
/// Attempt to create a `Time` from the hour, minute, second, and microsecond.
///
/// ```rust
/// # use time::Time;
/// assert!(Time::from_hms_micro(1, 2, 3, 4).is_ok());
/// ```
///
@@ -151,29 +270,25 @@ impl Time {
/// assert!(Time::from_hms_micro(0, 60, 0, 0).is_err()); // 60 isn't a valid minute.
/// assert!(Time::from_hms_micro(0, 0, 60, 0).is_err()); // 60 isn't a valid second.
/// assert!(Time::from_hms_micro(0, 0, 0, 1_000_000).is_err()); // 1_000_000 isn't a valid microsecond.
/// ```
pub const fn from_hms_micro(
hour: u8,
minute: u8,
second: u8,
microsecond: u32,
) -> Result<Self, error::ComponentRange> {
- ensure_value_in_range!(hour in 0 => Hour.per(Day) - 1);
- ensure_value_in_range!(minute in 0 => Minute.per(Hour) - 1);
- ensure_value_in_range!(second in 0 => Second.per(Minute) - 1);
- ensure_value_in_range!(microsecond in 0 => Microsecond.per(Second) - 1);
- Ok(Self::__from_hms_nanos_unchecked(
- hour,
- minute,
- second,
- microsecond * Nanosecond.per(Microsecond) as u32,
+ Ok(Self::from_hms_nanos_ranged(
+ ensure_ranged!(Hours: hour),
+ ensure_ranged!(Minutes: minute),
+ ensure_ranged!(Seconds: second),
+ ensure_ranged!(Nanoseconds: microsecond * Nanosecond::per(Microsecond) as u32),
))
}
/// Attempt to create a `Time` from the hour, minute, second, and nanosecond.
///
/// ```rust
/// # use time::Time;
/// assert!(Time::from_hms_nano(1, 2, 3, 4).is_ok());
/// ```
///
@@ -183,448 +298,449 @@ impl Time {
/// assert!(Time::from_hms_nano(0, 60, 0, 0).is_err()); // 60 isn't a valid minute.
/// assert!(Time::from_hms_nano(0, 0, 60, 0).is_err()); // 60 isn't a valid second.
/// assert!(Time::from_hms_nano(0, 0, 0, 1_000_000_000).is_err()); // 1_000_000_000 isn't a valid nanosecond.
/// ```
pub const fn from_hms_nano(
hour: u8,
minute: u8,
second: u8,
nanosecond: u32,
) -> Result<Self, error::ComponentRange> {
- ensure_value_in_range!(hour in 0 => Hour.per(Day) - 1);
- ensure_value_in_range!(minute in 0 => Minute.per(Hour) - 1);
- ensure_value_in_range!(second in 0 => Second.per(Minute) - 1);
- ensure_value_in_range!(nanosecond in 0 => Nanosecond.per(Second) - 1);
- Ok(Self::__from_hms_nanos_unchecked(
- hour, minute, second, nanosecond,
+ Ok(Self::from_hms_nanos_ranged(
+ ensure_ranged!(Hours: hour),
+ ensure_ranged!(Minutes: minute),
+ ensure_ranged!(Seconds: second),
+ ensure_ranged!(Nanoseconds: nanosecond),
))
}
// endregion constructors
// region: getters
/// Get the clock hour, minute, and second.
///
/// ```rust
/// # use time_macros::time;
/// assert_eq!(time!(0:00:00).as_hms(), (0, 0, 0));
/// assert_eq!(time!(23:59:59).as_hms(), (23, 59, 59));
/// ```
pub const fn as_hms(self) -> (u8, u8, u8) {
- (self.hour, self.minute, self.second)
+ (self.hour.get(), self.minute.get(), self.second.get())
}
/// Get the clock hour, minute, second, and millisecond.
///
/// ```rust
/// # use time_macros::time;
/// assert_eq!(time!(0:00:00).as_hms_milli(), (0, 0, 0, 0));
/// assert_eq!(time!(23:59:59.999).as_hms_milli(), (23, 59, 59, 999));
/// ```
pub const fn as_hms_milli(self) -> (u8, u8, u8, u16) {
(
- self.hour,
- self.minute,
- self.second,
- (self.nanosecond / Nanosecond.per(Millisecond)) as u16,
+ self.hour.get(),
+ self.minute.get(),
+ self.second.get(),
+ (self.nanosecond.get() / Nanosecond::per(Millisecond)) as u16,
)
}
/// Get the clock hour, minute, second, and microsecond.
///
/// ```rust
/// # use time_macros::time;
/// assert_eq!(time!(0:00:00).as_hms_micro(), (0, 0, 0, 0));
/// assert_eq!(
/// time!(23:59:59.999_999).as_hms_micro(),
/// (23, 59, 59, 999_999)
/// );
/// ```
pub const fn as_hms_micro(self) -> (u8, u8, u8, u32) {
(
- self.hour,
- self.minute,
- self.second,
- self.nanosecond / Nanosecond.per(Microsecond) as u32,
+ self.hour.get(),
+ self.minute.get(),
+ self.second.get(),
+ self.nanosecond.get() / Nanosecond::per(Microsecond) as u32,
)
}
/// Get the clock hour, minute, second, and nanosecond.
///
/// ```rust
/// # use time_macros::time;
/// assert_eq!(time!(0:00:00).as_hms_nano(), (0, 0, 0, 0));
/// assert_eq!(
/// time!(23:59:59.999_999_999).as_hms_nano(),
/// (23, 59, 59, 999_999_999)
/// );
/// ```
pub const fn as_hms_nano(self) -> (u8, u8, u8, u32) {
+ (
+ self.hour.get(),
+ self.minute.get(),
+ self.second.get(),
+ self.nanosecond.get(),
+ )
+ }
+
+ /// Get the clock hour, minute, second, and nanosecond.
+ #[cfg(feature = "quickcheck")]
+ pub(crate) const fn as_hms_nano_ranged(self) -> (Hours, Minutes, Seconds, Nanoseconds) {
(self.hour, self.minute, self.second, self.nanosecond)
}
/// Get the clock hour.
///
/// The returned value will always be in the range `0..24`.
///
/// ```rust
/// # use time_macros::time;
/// assert_eq!(time!(0:00:00).hour(), 0);
/// assert_eq!(time!(23:59:59).hour(), 23);
/// ```
pub const fn hour(self) -> u8 {
- self.hour
+ self.hour.get()
}
/// Get the minute within the hour.
///
/// The returned value will always be in the range `0..60`.
///
/// ```rust
/// # use time_macros::time;
/// assert_eq!(time!(0:00:00).minute(), 0);
/// assert_eq!(time!(23:59:59).minute(), 59);
/// ```
pub const fn minute(self) -> u8 {
- self.minute
+ self.minute.get()
}
/// Get the second within the minute.
///
/// The returned value will always be in the range `0..60`.
///
/// ```rust
/// # use time_macros::time;
/// assert_eq!(time!(0:00:00).second(), 0);
/// assert_eq!(time!(23:59:59).second(), 59);
/// ```
pub const fn second(self) -> u8 {
- self.second
+ self.second.get()
}
/// Get the milliseconds within the second.
///
/// The returned value will always be in the range `0..1_000`.
///
/// ```rust
/// # use time_macros::time;
/// assert_eq!(time!(0:00).millisecond(), 0);
/// assert_eq!(time!(23:59:59.999).millisecond(), 999);
/// ```
pub const fn millisecond(self) -> u16 {
- (self.nanosecond / Nanosecond.per(Millisecond)) as _
+ (self.nanosecond.get() / Nanosecond::per(Millisecond)) as _
}
/// Get the microseconds within the second.
///
/// The returned value will always be in the range `0..1_000_000`.
///
/// ```rust
/// # use time_macros::time;
/// assert_eq!(time!(0:00).microsecond(), 0);
/// assert_eq!(time!(23:59:59.999_999).microsecond(), 999_999);
/// ```
pub const fn microsecond(self) -> u32 {
- self.nanosecond / Nanosecond.per(Microsecond) as u32
+ self.nanosecond.get() / Nanosecond::per(Microsecond) as u32
}
/// Get the nanoseconds within the second.
///
/// The returned value will always be in the range `0..1_000_000_000`.
///
/// ```rust
/// # use time_macros::time;
/// assert_eq!(time!(0:00).nanosecond(), 0);
/// assert_eq!(time!(23:59:59.999_999_999).nanosecond(), 999_999_999);
/// ```
pub const fn nanosecond(self) -> u32 {
- self.nanosecond
+ self.nanosecond.get()
}
// endregion getters
// region: arithmetic helpers
/// Add the sub-day time of the [`Duration`] to the `Time`. Wraps on overflow, returning whether
/// the date is different.
pub(crate) const fn adjusting_add(self, duration: Duration) -> (DateAdjustment, Self) {
- let mut nanoseconds = self.nanosecond as i32 + duration.subsec_nanoseconds();
+ let mut nanoseconds = self.nanosecond.get() as i32 + duration.subsec_nanoseconds();
let mut seconds =
- self.second as i8 + (duration.whole_seconds() % Second.per(Minute) as i64) as i8;
+ self.second.get() as i8 + (duration.whole_seconds() % Second::per(Minute) as i64) as i8;
let mut minutes =
- self.minute as i8 + (duration.whole_minutes() % Minute.per(Hour) as i64) as i8;
- let mut hours = self.hour as i8 + (duration.whole_hours() % Hour.per(Day) as i64) as i8;
+ self.minute.get() as i8 + (duration.whole_minutes() % Minute::per(Hour) as i64) as i8;
+ let mut hours =
+ self.hour.get() as i8 + (duration.whole_hours() % Hour::per(Day) as i64) as i8;
let mut date_adjustment = DateAdjustment::None;
- cascade!(nanoseconds in 0..Nanosecond.per(Second) as _ => seconds);
- cascade!(seconds in 0..Second.per(Minute) as _ => minutes);
- cascade!(minutes in 0..Minute.per(Hour) as _ => hours);
- if hours >= Hour.per(Day) as _ {
- hours -= Hour.per(Day) as i8;
+ cascade!(nanoseconds in 0..Nanosecond::per(Second) as _ => seconds);
+ cascade!(seconds in 0..Second::per(Minute) as _ => minutes);
+ cascade!(minutes in 0..Minute::per(Hour) as _ => hours);
+ if hours >= Hour::per(Day) as _ {
+ hours -= Hour::per(Day) as i8;
date_adjustment = DateAdjustment::Next;
} else if hours < 0 {
- hours += Hour.per(Day) as i8;
+ hours += Hour::per(Day) as i8;
date_adjustment = DateAdjustment::Previous;
}
(
date_adjustment,
- Self::__from_hms_nanos_unchecked(
- hours as _,
- minutes as _,
- seconds as _,
- nanoseconds as _,
- ),
+ // Safety: The cascades above ensure the values are in range.
+ unsafe {
+ Self::__from_hms_nanos_unchecked(
+ hours as _,
+ minutes as _,
+ seconds as _,
+ nanoseconds as _,
+ )
+ },
)
}
/// Subtract the sub-day time of the [`Duration`] to the `Time`. Wraps on overflow, returning
/// whether the date is different.
pub(crate) const fn adjusting_sub(self, duration: Duration) -> (DateAdjustment, Self) {
- let mut nanoseconds = self.nanosecond as i32 - duration.subsec_nanoseconds();
+ let mut nanoseconds = self.nanosecond.get() as i32 - duration.subsec_nanoseconds();
let mut seconds =
- self.second as i8 - (duration.whole_seconds() % Second.per(Minute) as i64) as i8;
+ self.second.get() as i8 - (duration.whole_seconds() % Second::per(Minute) as i64) as i8;
let mut minutes =
- self.minute as i8 - (duration.whole_minutes() % Minute.per(Hour) as i64) as i8;
- let mut hours = self.hour as i8 - (duration.whole_hours() % Hour.per(Day) as i64) as i8;
+ self.minute.get() as i8 - (duration.whole_minutes() % Minute::per(Hour) as i64) as i8;
+ let mut hours =
+ self.hour.get() as i8 - (duration.whole_hours() % Hour::per(Day) as i64) as i8;
let mut date_adjustment = DateAdjustment::None;
- cascade!(nanoseconds in 0..Nanosecond.per(Second) as _ => seconds);
- cascade!(seconds in 0..Second.per(Minute) as _ => minutes);
- cascade!(minutes in 0..Minute.per(Hour) as _ => hours);
- if hours >= Hour.per(Day) as _ {
- hours -= Hour.per(Day) as i8;
+ cascade!(nanoseconds in 0..Nanosecond::per(Second) as _ => seconds);
+ cascade!(seconds in 0..Second::per(Minute) as _ => minutes);
+ cascade!(minutes in 0..Minute::per(Hour) as _ => hours);
+ if hours >= Hour::per(Day) as _ {
+ hours -= Hour::per(Day) as i8;
date_adjustment = DateAdjustment::Next;
} else if hours < 0 {
- hours += Hour.per(Day) as i8;
+ hours += Hour::per(Day) as i8;
date_adjustment = DateAdjustment::Previous;
}
(
date_adjustment,
- Self::__from_hms_nanos_unchecked(
- hours as _,
- minutes as _,
- seconds as _,
- nanoseconds as _,
- ),
+ // Safety: The cascades above ensure the values are in range.
+ unsafe {
+ Self::__from_hms_nanos_unchecked(
+ hours as _,
+ minutes as _,
+ seconds as _,
+ nanoseconds as _,
+ )
+ },
)
}
/// Add the sub-day time of the [`std::time::Duration`] to the `Time`. Wraps on overflow,
/// returning whether the date is the previous date as the first element of the tuple.
pub(crate) const fn adjusting_add_std(self, duration: StdDuration) -> (bool, Self) {
- let mut nanosecond = self.nanosecond + duration.subsec_nanos();
- let mut second = self.second + (duration.as_secs() % Second.per(Minute) as u64) as u8;
- let mut minute = self.minute
- + ((duration.as_secs() / Second.per(Minute) as u64) % Minute.per(Hour) as u64) as u8;
- let mut hour = self.hour
- + ((duration.as_secs() / Second.per(Hour) as u64) % Hour.per(Day) as u64) as u8;
+ let mut nanosecond = self.nanosecond.get() + duration.subsec_nanos();
+ let mut second =
+ self.second.get() + (duration.as_secs() % Second::per(Minute) as u64) as u8;
+ let mut minute = self.minute.get()
+ + ((duration.as_secs() / Second::per(Minute) as u64) % Minute::per(Hour) as u64) as u8;
+ let mut hour = self.hour.get()
+ + ((duration.as_secs() / Second::per(Hour) as u64) % Hour::per(Day) as u64) as u8;
let mut is_next_day = false;
- cascade!(nanosecond in 0..Nanosecond.per(Second) => second);
- cascade!(second in 0..Second.per(Minute) => minute);
- cascade!(minute in 0..Minute.per(Hour) => hour);
- if hour >= Hour.per(Day) {
- hour -= Hour.per(Day);
+ cascade!(nanosecond in 0..Nanosecond::per(Second) => second);
+ cascade!(second in 0..Second::per(Minute) => minute);
+ cascade!(minute in 0..Minute::per(Hour) => hour);
+ if hour >= Hour::per(Day) {
+ hour -= Hour::per(Day);
is_next_day = true;
}
(
is_next_day,
- Self::__from_hms_nanos_unchecked(hour, minute, second, nanosecond),
+ // Safety: The cascades above ensure the values are in range.
+ unsafe { Self::__from_hms_nanos_unchecked(hour, minute, second, nanosecond) },
)
}
/// Subtract the sub-day time of the [`std::time::Duration`] to the `Time`. Wraps on overflow,
/// returning whether the date is the previous date as the first element of the tuple.
pub(crate) const fn adjusting_sub_std(self, duration: StdDuration) -> (bool, Self) {
- let mut nanosecond = self.nanosecond as i32 - duration.subsec_nanos() as i32;
- let mut second = self.second as i8 - (duration.as_secs() % Second.per(Minute) as u64) as i8;
- let mut minute = self.minute as i8
- - ((duration.as_secs() / Second.per(Minute) as u64) % Minute.per(Hour) as u64) as i8;
- let mut hour = self.hour as i8
- - ((duration.as_secs() / Second.per(Hour) as u64) % Hour.per(Day) as u64) as i8;
+ let mut nanosecond = self.nanosecond.get() as i32 - duration.subsec_nanos() as i32;
+ let mut second =
+ self.second.get() as i8 - (duration.as_secs() % Second::per(Minute) as u64) as i8;
+ let mut minute = self.minute.get() as i8
+ - ((duration.as_secs() / Second::per(Minute) as u64) % Minute::per(Hour) as u64) as i8;
+ let mut hour = self.hour.get() as i8
+ - ((duration.as_secs() / Second::per(Hour) as u64) % Hour::per(Day) as u64) as i8;
let mut is_previous_day = false;
- cascade!(nanosecond in 0..Nanosecond.per(Second) as _ => second);
- cascade!(second in 0..Second.per(Minute) as _ => minute);
- cascade!(minute in 0..Minute.per(Hour) as _ => hour);
+ cascade!(nanosecond in 0..Nanosecond::per(Second) as _ => second);
+ cascade!(second in 0..Second::per(Minute) as _ => minute);
+ cascade!(minute in 0..Minute::per(Hour) as _ => hour);
if hour < 0 {
- hour += Hour.per(Day) as i8;
+ hour += Hour::per(Day) as i8;
is_previous_day = true;
}
(
is_previous_day,
- Self::__from_hms_nanos_unchecked(hour as _, minute as _, second as _, nanosecond as _),
+ // Safety: The cascades above ensure the values are in range.
+ unsafe {
+ Self::__from_hms_nanos_unchecked(
+ hour as _,
+ minute as _,
+ second as _,
+ nanosecond as _,
+ )
+ },
)
}
// endregion arithmetic helpers
// region: replacement
/// Replace the clock hour.
///
/// ```rust
/// # use time_macros::time;
/// assert_eq!(
/// time!(01:02:03.004_005_006).replace_hour(7),
/// Ok(time!(07:02:03.004_005_006))
/// );
/// assert!(time!(01:02:03.004_005_006).replace_hour(24).is_err()); // 24 isn't a valid hour
/// ```
#[must_use = "This method does not mutate the original `Time`."]
- pub const fn replace_hour(self, hour: u8) -> Result<Self, error::ComponentRange> {
- ensure_value_in_range!(hour in 0 => Hour.per(Day) - 1);
- Ok(Self::__from_hms_nanos_unchecked(
- hour,
- self.minute,
- self.second,
- self.nanosecond,
- ))
+ pub const fn replace_hour(mut self, hour: u8) -> Result<Self, error::ComponentRange> {
+ self.hour = ensure_ranged!(Hours: hour);
+ Ok(self)
}
/// Replace the minutes within the hour.
///
/// ```rust
/// # use time_macros::time;
/// assert_eq!(
/// time!(01:02:03.004_005_006).replace_minute(7),
/// Ok(time!(01:07:03.004_005_006))
/// );
/// assert!(time!(01:02:03.004_005_006).replace_minute(60).is_err()); // 60 isn't a valid minute
/// ```
#[must_use = "This method does not mutate the original `Time`."]
- pub const fn replace_minute(self, minute: u8) -> Result<Self, error::ComponentRange> {
- ensure_value_in_range!(minute in 0 => Minute.per(Hour) - 1);
- Ok(Self::__from_hms_nanos_unchecked(
- self.hour,
- minute,
- self.second,
- self.nanosecond,
- ))
+ pub const fn replace_minute(mut self, minute: u8) -> Result<Self, error::ComponentRange> {
+ self.minute = ensure_ranged!(Minutes: minute);
+ Ok(self)
}
/// Replace the seconds within the minute.
///
/// ```rust
/// # use time_macros::time;
/// assert_eq!(
/// time!(01:02:03.004_005_006).replace_second(7),
/// Ok(time!(01:02:07.004_005_006))
/// );
/// assert!(time!(01:02:03.004_005_006).replace_second(60).is_err()); // 60 isn't a valid second
/// ```
#[must_use = "This method does not mutate the original `Time`."]
- pub const fn replace_second(self, second: u8) -> Result<Self, error::ComponentRange> {
- ensure_value_in_range!(second in 0 => Second.per(Minute) - 1);
- Ok(Self::__from_hms_nanos_unchecked(
- self.hour,
- self.minute,
- second,
- self.nanosecond,
- ))
+ pub const fn replace_second(mut self, second: u8) -> Result<Self, error::ComponentRange> {
+ self.second = ensure_ranged!(Seconds: second);
+ Ok(self)
}
/// Replace the milliseconds within the second.
///
/// ```rust
/// # use time_macros::time;
/// assert_eq!(
/// time!(01:02:03.004_005_006).replace_millisecond(7),
/// Ok(time!(01:02:03.007))
/// );
/// assert!(time!(01:02:03.004_005_006).replace_millisecond(1_000).is_err()); // 1_000 isn't a valid millisecond
/// ```
#[must_use = "This method does not mutate the original `Time`."]
pub const fn replace_millisecond(
- self,
+ mut self,
millisecond: u16,
) -> Result<Self, error::ComponentRange> {
- ensure_value_in_range!(millisecond in 0 => Millisecond.per(Second) - 1);
- Ok(Self::__from_hms_nanos_unchecked(
- self.hour,
- self.minute,
- self.second,
- millisecond as u32 * Nanosecond.per(Millisecond),
- ))
+ self.nanosecond =
+ ensure_ranged!(Nanoseconds: millisecond as u32 * Nanosecond::per(Millisecond));
+ Ok(self)
}
/// Replace the microseconds within the second.
///
/// ```rust
/// # use time_macros::time;
/// assert_eq!(
/// time!(01:02:03.004_005_006).replace_microsecond(7_008),
/// Ok(time!(01:02:03.007_008))
/// );
/// assert!(time!(01:02:03.004_005_006).replace_microsecond(1_000_000).is_err()); // 1_000_000 isn't a valid microsecond
/// ```
#[must_use = "This method does not mutate the original `Time`."]
pub const fn replace_microsecond(
- self,
+ mut self,
microsecond: u32,
) -> Result<Self, error::ComponentRange> {
- ensure_value_in_range!(microsecond in 0 => Microsecond.per(Second) - 1);
- Ok(Self::__from_hms_nanos_unchecked(
- self.hour,
- self.minute,
- self.second,
- microsecond * Nanosecond.per(Microsecond) as u32,
- ))
+ self.nanosecond =
+ ensure_ranged!(Nanoseconds: microsecond * Nanosecond::per(Microsecond) as u32);
+ Ok(self)
}
/// Replace the nanoseconds within the second.
///
/// ```rust
/// # use time_macros::time;
/// assert_eq!(
/// time!(01:02:03.004_005_006).replace_nanosecond(7_008_009),
/// Ok(time!(01:02:03.007_008_009))
/// );
/// assert!(time!(01:02:03.004_005_006).replace_nanosecond(1_000_000_000).is_err()); // 1_000_000_000 isn't a valid nanosecond
/// ```
#[must_use = "This method does not mutate the original `Time`."]
- pub const fn replace_nanosecond(self, nanosecond: u32) -> Result<Self, error::ComponentRange> {
- ensure_value_in_range!(nanosecond in 0 => Nanosecond.per(Second) - 1);
- Ok(Self::__from_hms_nanos_unchecked(
- self.hour,
- self.minute,
- self.second,
- nanosecond,
- ))
+ pub const fn replace_nanosecond(
+ mut self,
+ nanosecond: u32,
+ ) -> Result<Self, error::ComponentRange> {
+ self.nanosecond = ensure_ranged!(Nanoseconds: nanosecond);
+ Ok(self)
}
// endregion replacement
}
// region: formatting & parsing
#[cfg(feature = "formatting")]
impl Time {
/// Format the `Time` using the provided [format description](crate::format_description).
pub fn format_into(
self,
output: &mut impl io::Write,
format: &(impl Formattable + ?Sized),
- ) -> Result<usize, crate::error::Format> {
+ ) -> Result<usize, error::Format> {
format.format_into(output, None, Some(self), None)
}
/// Format the `Time` using the provided [format description](crate::format_description).
///
/// ```rust
/// # use time::format_description;
/// # use time_macros::time;
/// let format = format_description::parse("[hour]:[minute]:[second]")?;
/// assert_eq!(time!(12:00).format(&format)?, "12:00:00");
/// # Ok::<_, time::Error>(())
/// ```
- pub fn format(
- self,
- format: &(impl Formattable + ?Sized),
- ) -> Result<String, crate::error::Format> {
+ pub fn format(self, format: &(impl Formattable + ?Sized)) -> Result<String, error::Format> {
format.format(None, Some(self), None)
}
}
#[cfg(feature = "parsing")]
impl Time {
/// Parse a `Time` from the input using the provided [format
/// description](crate::format_description).
///
/// ```rust
@@ -635,41 +751,92 @@ impl Time {
/// # Ok::<_, time::Error>(())
/// ```
pub fn parse(
input: &str,
description: &(impl Parsable + ?Sized),
) -> Result<Self, error::Parse> {
description.parse_time(input.as_bytes())
}
}
-impl fmt::Display for Time {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- let (value, width) = match self.nanosecond() {
+mod private {
+ #[non_exhaustive]
+ #[derive(Debug, Clone, Copy)]
+ pub struct TimeMetadata {
+ /// How many characters wide the formatted subsecond is.
+ pub(super) subsecond_width: u8,
+ /// The value to use when formatting the subsecond. Leading zeroes will be added as
+ /// necessary.
+ pub(super) subsecond_value: u32,
+ }
+}
+use private::TimeMetadata;
+
+impl SmartDisplay for Time {
+ type Metadata = TimeMetadata;
+
+ fn metadata(&self, _: FormatterOptions) -> Metadata<Self> {
+ let (subsecond_value, subsecond_width) = match self.nanosecond() {
nanos if nanos % 10 != 0 => (nanos, 9),
nanos if (nanos / 10) % 10 != 0 => (nanos / 10, 8),
nanos if (nanos / 100) % 10 != 0 => (nanos / 100, 7),
nanos if (nanos / 1_000) % 10 != 0 => (nanos / 1_000, 6),
nanos if (nanos / 10_000) % 10 != 0 => (nanos / 10_000, 5),
nanos if (nanos / 100_000) % 10 != 0 => (nanos / 100_000, 4),
nanos if (nanos / 1_000_000) % 10 != 0 => (nanos / 1_000_000, 3),
nanos if (nanos / 10_000_000) % 10 != 0 => (nanos / 10_000_000, 2),
nanos => (nanos / 100_000_000, 1),
};
- write!(
- f,
- "{}:{:02}:{:02}.{value:0width$}",
- self.hour, self.minute, self.second,
+
+ let formatted_width = smart_display::padded_width_of!(
+ self.hour.get(),
+ ":",
+ self.minute.get() => width(2) fill('0'),
+ ":",
+ self.second.get() => width(2) fill('0'),
+ ".",
+ ) + subsecond_width;
+
+ Metadata::new(
+ formatted_width,
+ self,
+ TimeMetadata {
+ subsecond_width: subsecond_width.truncate(),
+ subsecond_value,
+ },
+ )
+ }
+
+ fn fmt_with_metadata(
+ &self,
+ f: &mut fmt::Formatter<'_>,
+ metadata: Metadata<Self>,
+ ) -> fmt::Result {
+ let subsecond_width = metadata.subsecond_width.extend();
+ let subsecond_value = metadata.subsecond_value;
+
+ f.pad_with_width(
+ metadata.unpadded_width(),
+ format_args!(
+ "{}:{:02}:{:02}.{subsecond_value:0subsecond_width$}",
+ self.hour, self.minute, self.second
+ ),
)
}
}
+impl fmt::Display for Time {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ SmartDisplay::fmt(self, f)
+ }
+}
+
impl fmt::Debug for Time {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(self, f)
}
}
// endregion formatting & parsing
// region: trait impls
impl Add<Duration> for Time {
type Output = Self;
@@ -747,31 +914,39 @@ impl Sub for Time {
///
/// ```rust
/// # use time::ext::NumericalDuration;
/// # use time_macros::time;
/// assert_eq!(time!(0:00) - time!(0:00), 0.seconds());
/// assert_eq!(time!(1:00) - time!(0:00), 1.hours());
/// assert_eq!(time!(0:00) - time!(1:00), (-1).hours());
/// assert_eq!(time!(0:00) - time!(23:00), (-23).hours());
/// ```
fn sub(self, rhs: Self) -> Self::Output {
- let hour_diff = (self.hour as i8) - (rhs.hour as i8);
- let minute_diff = (self.minute as i8) - (rhs.minute as i8);
- let second_diff = (self.second as i8) - (rhs.second as i8);
- let nanosecond_diff = (self.nanosecond as i32) - (rhs.nanosecond as i32);
+ let hour_diff = self.hour.get().cast_signed() - rhs.hour.get().cast_signed();
+ let minute_diff = self.minute.get().cast_signed() - rhs.minute.get().cast_signed();
+ let second_diff = self.second.get().cast_signed() - rhs.second.get().cast_signed();
+ let nanosecond_diff =
+ self.nanosecond.get().cast_signed() - rhs.nanosecond.get().cast_signed();
- let seconds = hour_diff as i64 * Second.per(Hour) as i64
- + minute_diff as i64 * Second.per(Minute) as i64
- + second_diff as i64;
+ let seconds = hour_diff.extend::<i64>() * Second::per(Hour).cast_signed().extend::<i64>()
+ + minute_diff.extend::<i64>() * Second::per(Minute).cast_signed().extend::<i64>()
+ + second_diff.extend::<i64>();
let (seconds, nanoseconds) = if seconds > 0 && nanosecond_diff < 0 {
- (seconds - 1, nanosecond_diff + Nanosecond.per(Second) as i32)
+ (
+ seconds - 1,
+ nanosecond_diff + Nanosecond::per(Second).cast_signed(),
+ )
} else if seconds < 0 && nanosecond_diff > 0 {
- (seconds + 1, nanosecond_diff - Nanosecond.per(Second) as i32)
+ (
+ seconds + 1,
+ nanosecond_diff - Nanosecond::per(Second).cast_signed(),
+ )
} else {
(seconds, nanosecond_diff)
};
- Duration::new_unchecked(seconds, nanoseconds)
+ // Safety: `nanoseconds` is in range due to the overflow handling.
+ unsafe { Duration::new_unchecked(seconds, nanoseconds) }
}
}
// endregion trait impls
diff --git a/third_party/rust/time/src/utc_offset.rs b/third_party/rust/time/src/utc_offset.rs
index af7fd1bf79..7cf24d141c 100644
--- a/third_party/rust/time/src/utc_offset.rs
+++ b/third_party/rust/time/src/utc_offset.rs
@@ -1,255 +1,343 @@
//! The [`UtcOffset`] struct and its associated `impl`s.
+#[cfg(feature = "formatting")]
+use alloc::string::String;
use core::fmt;
use core::ops::Neg;
#[cfg(feature = "formatting")]
use std::io;
+use deranged::{RangedI32, RangedI8};
+use powerfmt::ext::FormatterExt;
+use powerfmt::smart_display::{self, FormatterOptions, Metadata, SmartDisplay};
+
use crate::convert::*;
use crate::error;
#[cfg(feature = "formatting")]
use crate::formatting::Formattable;
+use crate::internal_macros::ensure_ranged;
#[cfg(feature = "parsing")]
use crate::parsing::Parsable;
#[cfg(feature = "local-offset")]
use crate::sys::local_offset_at;
#[cfg(feature = "local-offset")]
use crate::OffsetDateTime;
+/// The type of the `hours` field of `UtcOffset`.
+type Hours = RangedI8<-25, 25>;
+/// The type of the `minutes` field of `UtcOffset`.
+type Minutes = RangedI8<{ -(Minute::per(Hour) as i8 - 1) }, { Minute::per(Hour) as i8 - 1 }>;
+/// The type of the `seconds` field of `UtcOffset`.
+type Seconds = RangedI8<{ -(Second::per(Minute) as i8 - 1) }, { Second::per(Minute) as i8 - 1 }>;
+/// The type capable of storing the range of whole seconds that a `UtcOffset` can encompass.
+type WholeSeconds = RangedI32<
+ {
+ Hours::MIN.get() as i32 * Second::per(Hour) as i32
+ + Minutes::MIN.get() as i32 * Second::per(Minute) as i32
+ + Seconds::MIN.get() as i32
+ },
+ {
+ Hours::MAX.get() as i32 * Second::per(Hour) as i32
+ + Minutes::MAX.get() as i32 * Second::per(Minute) as i32
+ + Seconds::MAX.get() as i32
+ },
+>;
+
/// An offset from UTC.
///
-/// This struct can store values up to ±23:59:59. If you need support outside this range, please
+/// This struct can store values up to ±25:59:59. If you need support outside this range, please
/// file an issue with your use case.
// All three components _must_ have the same sign.
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct UtcOffset {
#[allow(clippy::missing_docs_in_private_items)]
- hours: i8,
+ hours: Hours,
#[allow(clippy::missing_docs_in_private_items)]
- minutes: i8,
+ minutes: Minutes,
#[allow(clippy::missing_docs_in_private_items)]
- seconds: i8,
+ seconds: Seconds,
}
impl UtcOffset {
/// A `UtcOffset` that is UTC.
///
/// ```rust
/// # use time::UtcOffset;
/// # use time_macros::offset;
/// assert_eq!(UtcOffset::UTC, offset!(UTC));
/// ```
- pub const UTC: Self = Self::__from_hms_unchecked(0, 0, 0);
+ pub const UTC: Self = Self::from_whole_seconds_ranged(WholeSeconds::new_static::<0>());
// region: constructors
/// Create a `UtcOffset` representing an offset of the hours, minutes, and seconds provided, the
/// validity of which must be guaranteed by the caller. All three parameters must have the same
/// sign.
+ ///
+ /// # Safety
+ ///
+ /// - Hours must be in the range `-25..=25`.
+ /// - Minutes must be in the range `-59..=59`.
+ /// - Seconds must be in the range `-59..=59`.
+ ///
+ /// While the signs of the parameters are required to match to avoid bugs, this is not a safety
+ /// invariant.
#[doc(hidden)]
- pub const fn __from_hms_unchecked(hours: i8, minutes: i8, seconds: i8) -> Self {
- if hours < 0 {
- debug_assert!(minutes <= 0);
- debug_assert!(seconds <= 0);
- } else if hours > 0 {
- debug_assert!(minutes >= 0);
- debug_assert!(seconds >= 0);
- }
- if minutes < 0 {
- debug_assert!(seconds <= 0);
- } else if minutes > 0 {
- debug_assert!(seconds >= 0);
- }
- debug_assert!(hours.unsigned_abs() < 24);
- debug_assert!(minutes.unsigned_abs() < Minute.per(Hour));
- debug_assert!(seconds.unsigned_abs() < Second.per(Minute));
-
- Self {
- hours,
- minutes,
- seconds,
+ pub const unsafe fn __from_hms_unchecked(hours: i8, minutes: i8, seconds: i8) -> Self {
+ // Safety: The caller must uphold the safety invariants.
+ unsafe {
+ Self::from_hms_ranged_unchecked(
+ Hours::new_unchecked(hours),
+ Minutes::new_unchecked(minutes),
+ Seconds::new_unchecked(seconds),
+ )
}
}
/// Create a `UtcOffset` representing an offset by the number of hours, minutes, and seconds
/// provided.
///
/// The sign of all three components should match. If they do not, all smaller components will
/// have their signs flipped.
///
/// ```rust
/// # use time::UtcOffset;
/// assert_eq!(UtcOffset::from_hms(1, 2, 3)?.as_hms(), (1, 2, 3));
/// assert_eq!(UtcOffset::from_hms(1, -2, -3)?.as_hms(), (1, 2, 3));
/// # Ok::<_, time::Error>(())
/// ```
pub const fn from_hms(
hours: i8,
- mut minutes: i8,
- mut seconds: i8,
+ minutes: i8,
+ seconds: i8,
) -> Result<Self, error::ComponentRange> {
- ensure_value_in_range!(hours in -23 => 23);
- ensure_value_in_range!(
- minutes in -(Minute.per(Hour) as i8 - 1) => Minute.per(Hour) as i8 - 1
- );
- ensure_value_in_range!(
- seconds in -(Second.per(Minute) as i8 - 1) => Second.per(Minute) as i8 - 1
- );
+ Ok(Self::from_hms_ranged(
+ ensure_ranged!(Hours: hours),
+ ensure_ranged!(Minutes: minutes),
+ ensure_ranged!(Seconds: seconds),
+ ))
+ }
+
+ /// Create a `UtcOffset` representing an offset of the hours, minutes, and seconds provided. All
+ /// three parameters must have the same sign.
+ ///
+ /// While the signs of the parameters are required to match, this is not a safety invariant.
+ pub(crate) const fn from_hms_ranged_unchecked(
+ hours: Hours,
+ minutes: Minutes,
+ seconds: Seconds,
+ ) -> Self {
+ if hours.get() < 0 {
+ debug_assert!(minutes.get() <= 0);
+ debug_assert!(seconds.get() <= 0);
+ } else if hours.get() > 0 {
+ debug_assert!(minutes.get() >= 0);
+ debug_assert!(seconds.get() >= 0);
+ }
+ if minutes.get() < 0 {
+ debug_assert!(seconds.get() <= 0);
+ } else if minutes.get() > 0 {
+ debug_assert!(seconds.get() >= 0);
+ }
- if (hours > 0 && minutes < 0) || (hours < 0 && minutes > 0) {
- minutes *= -1;
+ Self {
+ hours,
+ minutes,
+ seconds,
+ }
+ }
+
+ /// Create a `UtcOffset` representing an offset by the number of hours, minutes, and seconds
+ /// provided.
+ ///
+ /// The sign of all three components should match. If they do not, all smaller components will
+ /// have their signs flipped.
+ pub(crate) const fn from_hms_ranged(
+ hours: Hours,
+ mut minutes: Minutes,
+ mut seconds: Seconds,
+ ) -> Self {
+ if (hours.get() > 0 && minutes.get() < 0) || (hours.get() < 0 && minutes.get() > 0) {
+ minutes = minutes.neg();
}
- if (hours > 0 && seconds < 0)
- || (hours < 0 && seconds > 0)
- || (minutes > 0 && seconds < 0)
- || (minutes < 0 && seconds > 0)
+ if (hours.get() > 0 && seconds.get() < 0)
+ || (hours.get() < 0 && seconds.get() > 0)
+ || (minutes.get() > 0 && seconds.get() < 0)
+ || (minutes.get() < 0 && seconds.get() > 0)
{
- seconds *= -1;
+ seconds = seconds.neg();
}
- Ok(Self::__from_hms_unchecked(hours, minutes, seconds))
+ Self {
+ hours,
+ minutes,
+ seconds,
+ }
}
/// Create a `UtcOffset` representing an offset by the number of seconds provided.
///
/// ```rust
/// # use time::UtcOffset;
/// assert_eq!(UtcOffset::from_whole_seconds(3_723)?.as_hms(), (1, 2, 3));
/// # Ok::<_, time::Error>(())
/// ```
pub const fn from_whole_seconds(seconds: i32) -> Result<Self, error::ComponentRange> {
- ensure_value_in_range!(
- seconds in -24 * Second.per(Hour) as i32 - 1 => 24 * Second.per(Hour) as i32 - 1
- );
-
- Ok(Self::__from_hms_unchecked(
- (seconds / Second.per(Hour) as i32) as _,
- ((seconds % Second.per(Hour) as i32) / Minute.per(Hour) as i32) as _,
- (seconds % Second.per(Minute) as i32) as _,
+ Ok(Self::from_whole_seconds_ranged(
+ ensure_ranged!(WholeSeconds: seconds),
))
}
+
+ /// Create a `UtcOffset` representing an offset by the number of seconds provided.
+ // ignore because the function is crate-private
+ /// ```rust,ignore
+ /// # use time::UtcOffset;
+ /// # use deranged::RangedI32;
+ /// assert_eq!(
+ /// UtcOffset::from_whole_seconds_ranged(RangedI32::new_static::<3_723>()).as_hms(),
+ /// (1, 2, 3)
+ /// );
+ /// # Ok::<_, time::Error>(())
+ /// ```
+ pub(crate) const fn from_whole_seconds_ranged(seconds: WholeSeconds) -> Self {
+ // Safety: The type of `seconds` guarantees that all values are in range.
+ unsafe {
+ Self::__from_hms_unchecked(
+ (seconds.get() / Second::per(Hour) as i32) as _,
+ ((seconds.get() % Second::per(Hour) as i32) / Minute::per(Hour) as i32) as _,
+ (seconds.get() % Second::per(Minute) as i32) as _,
+ )
+ }
+ }
// endregion constructors
// region: getters
/// Obtain the UTC offset as its hours, minutes, and seconds. The sign of all three components
/// will always match. A positive value indicates an offset to the east; a negative to the west.
///
/// ```rust
/// # use time_macros::offset;
/// assert_eq!(offset!(+1:02:03).as_hms(), (1, 2, 3));
/// assert_eq!(offset!(-1:02:03).as_hms(), (-1, -2, -3));
/// ```
pub const fn as_hms(self) -> (i8, i8, i8) {
+ (self.hours.get(), self.minutes.get(), self.seconds.get())
+ }
+
+ /// Obtain the UTC offset as its hours, minutes, and seconds. The sign of all three components
+ /// will always match. A positive value indicates an offset to the east; a negative to the west.
+ #[cfg(feature = "quickcheck")]
+ pub(crate) const fn as_hms_ranged(self) -> (Hours, Minutes, Seconds) {
(self.hours, self.minutes, self.seconds)
}
/// Obtain the number of whole hours the offset is from UTC. A positive value indicates an
/// offset to the east; a negative to the west.
///
/// ```rust
/// # use time_macros::offset;
/// assert_eq!(offset!(+1:02:03).whole_hours(), 1);
/// assert_eq!(offset!(-1:02:03).whole_hours(), -1);
/// ```
pub const fn whole_hours(self) -> i8 {
- self.hours
+ self.hours.get()
}
/// Obtain the number of whole minutes the offset is from UTC. A positive value indicates an
/// offset to the east; a negative to the west.
///
/// ```rust
/// # use time_macros::offset;
/// assert_eq!(offset!(+1:02:03).whole_minutes(), 62);
/// assert_eq!(offset!(-1:02:03).whole_minutes(), -62);
/// ```
pub const fn whole_minutes(self) -> i16 {
- self.hours as i16 * Minute.per(Hour) as i16 + self.minutes as i16
+ self.hours.get() as i16 * Minute::per(Hour) as i16 + self.minutes.get() as i16
}
/// Obtain the number of minutes past the hour the offset is from UTC. A positive value
/// indicates an offset to the east; a negative to the west.
///
/// ```rust
/// # use time_macros::offset;
/// assert_eq!(offset!(+1:02:03).minutes_past_hour(), 2);
/// assert_eq!(offset!(-1:02:03).minutes_past_hour(), -2);
/// ```
pub const fn minutes_past_hour(self) -> i8 {
- self.minutes
+ self.minutes.get()
}
/// Obtain the number of whole seconds the offset is from UTC. A positive value indicates an
/// offset to the east; a negative to the west.
///
/// ```rust
/// # use time_macros::offset;
/// assert_eq!(offset!(+1:02:03).whole_seconds(), 3723);
/// assert_eq!(offset!(-1:02:03).whole_seconds(), -3723);
/// ```
// This may be useful for anyone manually implementing arithmetic, as it
// would let them construct a `Duration` directly.
pub const fn whole_seconds(self) -> i32 {
- self.hours as i32 * Second.per(Hour) as i32
- + self.minutes as i32 * Second.per(Minute) as i32
- + self.seconds as i32
+ self.hours.get() as i32 * Second::per(Hour) as i32
+ + self.minutes.get() as i32 * Second::per(Minute) as i32
+ + self.seconds.get() as i32
}
/// Obtain the number of seconds past the minute the offset is from UTC. A positive value
/// indicates an offset to the east; a negative to the west.
///
/// ```rust
/// # use time_macros::offset;
/// assert_eq!(offset!(+1:02:03).seconds_past_minute(), 3);
/// assert_eq!(offset!(-1:02:03).seconds_past_minute(), -3);
/// ```
pub const fn seconds_past_minute(self) -> i8 {
- self.seconds
+ self.seconds.get()
}
// endregion getters
// region: is_{sign}
/// Check if the offset is exactly UTC.
///
///
/// ```rust
/// # use time_macros::offset;
/// assert!(!offset!(+1:02:03).is_utc());
/// assert!(!offset!(-1:02:03).is_utc());
/// assert!(offset!(UTC).is_utc());
/// ```
pub const fn is_utc(self) -> bool {
- self.hours == 0 && self.minutes == 0 && self.seconds == 0
+ self.hours.get() == 0 && self.minutes.get() == 0 && self.seconds.get() == 0
}
/// Check if the offset is positive, or east of UTC.
///
/// ```rust
/// # use time_macros::offset;
/// assert!(offset!(+1:02:03).is_positive());
/// assert!(!offset!(-1:02:03).is_positive());
/// assert!(!offset!(UTC).is_positive());
/// ```
pub const fn is_positive(self) -> bool {
- self.hours > 0 || self.minutes > 0 || self.seconds > 0
+ self.hours.get() > 0 || self.minutes.get() > 0 || self.seconds.get() > 0
}
/// Check if the offset is negative, or west of UTC.
///
/// ```rust
/// # use time_macros::offset;
/// assert!(!offset!(+1:02:03).is_negative());
/// assert!(offset!(-1:02:03).is_negative());
/// assert!(!offset!(UTC).is_negative());
/// ```
pub const fn is_negative(self) -> bool {
- self.hours < 0 || self.minutes < 0 || self.seconds < 0
+ self.hours.get() < 0 || self.minutes.get() < 0 || self.seconds.get() < 0
}
// endregion is_{sign}
// region: local offset
/// Attempt to obtain the system's UTC offset at a known moment in time. If the offset cannot be
/// determined, an error is returned.
///
/// ```rust
/// # use time::{UtcOffset, OffsetDateTime};
/// let local_offset = UtcOffset::local_offset_at(OffsetDateTime::UNIX_EPOCH);
@@ -319,37 +407,71 @@ impl UtcOffset {
/// # Ok::<_, time::Error>(())
/// ```
pub fn parse(
input: &str,
description: &(impl Parsable + ?Sized),
) -> Result<Self, error::Parse> {
description.parse_offset(input.as_bytes())
}
}
+mod private {
+ #[non_exhaustive]
+ #[derive(Debug, Clone, Copy)]
+ pub struct UtcOffsetMetadata;
+}
+use private::UtcOffsetMetadata;
+
+impl SmartDisplay for UtcOffset {
+ type Metadata = UtcOffsetMetadata;
+
+ fn metadata(&self, _: FormatterOptions) -> Metadata<Self> {
+ let sign = if self.is_negative() { '-' } else { '+' };
+ let width = smart_display::padded_width_of!(
+ sign,
+ self.hours.abs() => width(2),
+ ":",
+ self.minutes.abs() => width(2),
+ ":",
+ self.seconds.abs() => width(2),
+ );
+ Metadata::new(width, self, UtcOffsetMetadata)
+ }
+
+ fn fmt_with_metadata(
+ &self,
+ f: &mut fmt::Formatter<'_>,
+ metadata: Metadata<Self>,
+ ) -> fmt::Result {
+ f.pad_with_width(
+ metadata.unpadded_width(),
+ format_args!(
+ "{}{:02}:{:02}:{:02}",
+ if self.is_negative() { '-' } else { '+' },
+ self.hours.abs(),
+ self.minutes.abs(),
+ self.seconds.abs(),
+ ),
+ )
+ }
+}
+
impl fmt::Display for UtcOffset {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- write!(
- f,
- "{}{:02}:{:02}:{:02}",
- if self.is_negative() { '-' } else { '+' },
- self.hours.abs(),
- self.minutes.abs(),
- self.seconds.abs()
- )
+ SmartDisplay::fmt(self, f)
}
}
impl fmt::Debug for UtcOffset {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(self, f)
}
}
// endregion formatting & parsing
impl Neg for UtcOffset {
type Output = Self;
fn neg(self) -> Self::Output {
- Self::__from_hms_unchecked(-self.hours, -self.minutes, -self.seconds)
+ Self::from_hms_ranged(self.hours.neg(), self.minutes.neg(), self.seconds.neg())
}
}
diff --git a/third_party/rust/time/src/util.rs b/third_party/rust/time/src/util.rs
index 857f5f20b0..9bb9b0404d 100644
--- a/third_party/rust/time/src/util.rs
+++ b/third_party/rust/time/src/util.rs
@@ -78,22 +78,22 @@ pub mod local_offset {
/// If using this method is absolutely necessary, it is recommended to keep the time between
/// setting the soundness to [`Soundness::Unsound`] and setting it back to [`Soundness::Sound`]
/// as short as possible.
///
/// The following methods currently obtain the local UTC offset:
///
/// - [`OffsetDateTime::now_local`](crate::OffsetDateTime::now_local)
/// - [`UtcOffset::local_offset_at`](crate::UtcOffset::local_offset_at)
/// - [`UtcOffset::current_local_offset`](crate::UtcOffset::current_local_offset)
pub unsafe fn set_soundness(soundness: Soundness) {
- LOCAL_OFFSET_IS_SOUND.store(soundness == Soundness::Sound, Ordering::SeqCst);
+ LOCAL_OFFSET_IS_SOUND.store(soundness == Soundness::Sound, Ordering::Release);
}
/// Obtains the soundness of obtaining the local UTC offset. If it is [`Soundness::Unsound`],
/// it is allowed to invoke undefined behavior when obtaining the local UTC offset.
pub fn get_soundness() -> Soundness {
- match LOCAL_OFFSET_IS_SOUND.load(Ordering::SeqCst) {
+ match LOCAL_OFFSET_IS_SOUND.load(Ordering::Acquire) {
false => Soundness::Unsound,
true => Soundness::Sound,
}
}
}
diff --git a/third_party/rust/time/src/weekday.rs b/third_party/rust/time/src/weekday.rs
index 761107e694..543ecb22cc 100644
--- a/third_party/rust/time/src/weekday.rs
+++ b/third_party/rust/time/src/weekday.rs
@@ -1,38 +1,39 @@
//! Days of the week.
-use core::fmt::{self, Display};
+use core::fmt;
use core::str::FromStr;
-use Weekday::*;
+use powerfmt::smart_display::{FormatterOptions, Metadata, SmartDisplay};
+use self::Weekday::*;
use crate::error;
/// Days of the week.
///
/// As order is dependent on context (Sunday could be either two days after or five days before
/// Friday), this type does not implement `PartialOrd` or `Ord`.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Weekday {
- #[allow(clippy::missing_docs_in_private_items)]
+ #[allow(missing_docs)]
Monday,
- #[allow(clippy::missing_docs_in_private_items)]
+ #[allow(missing_docs)]
Tuesday,
- #[allow(clippy::missing_docs_in_private_items)]
+ #[allow(missing_docs)]
Wednesday,
- #[allow(clippy::missing_docs_in_private_items)]
+ #[allow(missing_docs)]
Thursday,
- #[allow(clippy::missing_docs_in_private_items)]
+ #[allow(missing_docs)]
Friday,
- #[allow(clippy::missing_docs_in_private_items)]
+ #[allow(missing_docs)]
Saturday,
- #[allow(clippy::missing_docs_in_private_items)]
+ #[allow(missing_docs)]
Sunday,
}
impl Weekday {
/// Get the previous weekday.
///
/// ```rust
/// # use time::Weekday;
/// assert_eq!(Weekday::Tuesday.previous(), Weekday::Monday);
/// ```
@@ -153,34 +154,61 @@ impl Weekday {
Tuesday => 2,
Wednesday => 3,
Thursday => 4,
Friday => 5,
Saturday => 6,
Sunday => 0,
}
}
}
-impl Display for Weekday {
+mod private {
+ #[non_exhaustive]
+ #[derive(Debug, Clone, Copy)]
+ pub struct WeekdayMetadata;
+}
+use private::WeekdayMetadata;
+
+impl SmartDisplay for Weekday {
+ type Metadata = WeekdayMetadata;
+
+ fn metadata(&self, _: FormatterOptions) -> Metadata<'_, Self> {
+ match self {
+ Monday => Metadata::new(6, self, WeekdayMetadata),
+ Tuesday => Metadata::new(7, self, WeekdayMetadata),
+ Wednesday => Metadata::new(9, self, WeekdayMetadata),
+ Thursday => Metadata::new(8, self, WeekdayMetadata),
+ Friday => Metadata::new(6, self, WeekdayMetadata),
+ Saturday => Metadata::new(8, self, WeekdayMetadata),
+ Sunday => Metadata::new(6, self, WeekdayMetadata),
+ }
+ }
+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- f.write_str(match self {
+ f.pad(match self {
Monday => "Monday",
Tuesday => "Tuesday",
Wednesday => "Wednesday",
Thursday => "Thursday",
Friday => "Friday",
Saturday => "Saturday",
Sunday => "Sunday",
})
}
}
+impl fmt::Display for Weekday {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ SmartDisplay::fmt(self, f)
+ }
+}
+
impl FromStr for Weekday {
type Err = error::InvalidVariant;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"Monday" => Ok(Monday),
"Tuesday" => Ok(Tuesday),
"Wednesday" => Ok(Wednesday),
"Thursday" => Ok(Thursday),
"Friday" => Ok(Friday),