mirror of
https://forge.fsky.io/oneflux/omegafox.git
synced 2026-02-10 16:02:04 -08:00
21484 lines
808 KiB
Text
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`
|
|
+
|
|
+[](https://www.whatrustisit.com)
|
|
+[](https://crates.io/crates/powerfmt)
|
|
+[](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
|
|
|
|
-[](https://www.whatrustisit.com)
|
|
+[](https://www.whatrustisit.com)
|
|
[](https://crates.io/crates/time)
|
|
[](https://github.com/time-rs/time/actions)
|
|
[](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(×tamp.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(×tamp.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),
|