Bug 1710421 - [webdriver] Update time and cookie dependencies. r=webdriver-reviewers,glandium,whimboo

Differential Revision: https://phabricator.services.mozilla.com/D147000
This commit is contained in:
Mike Hommey 2022-06-01 06:44:06 +00:00
Родитель 5ff09263c0
Коммит 2edc1ca2ed
118 изменённых файлов: 19143 добавлений и 2511 удалений

56
Cargo.lock сгенерированный
Просмотреть файл

@ -607,7 +607,7 @@ dependencies = [
"storage_variant",
"tempfile",
"thin-vec",
"time",
"time 0.1.43",
"wr_malloc_size_of",
"xpcom",
]
@ -675,7 +675,7 @@ dependencies = [
"num-integer",
"num-traits",
"serde",
"time",
"time 0.1.43",
"winapi",
]
@ -745,11 +745,12 @@ dependencies = [
[[package]]
name = "cookie"
version = "0.12.0"
version = "0.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "888604f00b3db336d2af898ec3c1d5d0ddf5e6d462220f2ededc33a87ac4bbd5"
checksum = "94d4706de1b0fa5b132270cddffa8585166037822e260a944fe161acd137ca05"
dependencies = [
"time",
"time 0.3.9",
"version_check",
]
[[package]]
@ -2125,7 +2126,7 @@ dependencies = [
"serde",
"serde_json",
"thiserror",
"time",
"time 0.1.43",
"uuid",
"whatsys",
]
@ -2145,7 +2146,7 @@ dependencies = [
"rkv",
"serde",
"serde_json",
"time",
"time 0.1.43",
"uuid",
"zeitstempel",
]
@ -2467,7 +2468,7 @@ dependencies = [
"log",
"pin-project",
"socket2",
"time",
"time 0.1.43",
"tokio 0.2.25",
"tower-service",
"want",
@ -3425,7 +3426,7 @@ version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aad9dfe950c057b1bfe9c1f2aa51583a8468ef2a5baba2ebbe06d775efeb7729"
dependencies = [
"time",
"time 0.1.43",
"winapi",
]
@ -3685,6 +3686,15 @@ dependencies = [
"libc",
]
[[package]]
name = "num_threads"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44"
dependencies = [
"libc",
]
[[package]]
name = "objc"
version = "0.2.7"
@ -4871,7 +4881,7 @@ dependencies = [
"static_prefs",
"style_derive",
"style_traits",
"time",
"time 0.1.43",
"to_shmem",
"to_shmem_derive",
"toml",
@ -5074,6 +5084,24 @@ dependencies = [
"winapi",
]
[[package]]
name = "time"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2702e08a7a860f005826c6815dcac101b19b5eb330c27fe4a5928fec1d20ddd"
dependencies = [
"itoa 1.0.2",
"libc",
"num_threads",
"time-macros",
]
[[package]]
name = "time-macros"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42657b1a6f4d817cda8e7a0ace261fe0cc946cf3a80314390b22cc61ae080792"
[[package]]
name = "tinystr"
version = "0.3.4"
@ -5675,7 +5703,7 @@ dependencies = [
"serde",
"serde_derive",
"serde_json",
"time",
"time 0.3.9",
"tokio 0.2.25",
"unicode-segmentation",
"url",
@ -5758,7 +5786,7 @@ dependencies = [
"smallvec",
"svg_fmt",
"swgl",
"time",
"time 0.1.43",
"topological-sort",
"tracy-rs",
"webrender_api",
@ -5781,7 +5809,7 @@ dependencies = [
"serde",
"serde_bytes",
"serde_derive",
"time",
"time 0.1.43",
"wr_malloc_size_of",
]
@ -6090,5 +6118,5 @@ dependencies = [
"flate2",
"msdos_time",
"podio",
"time",
"time 0.1.43",
]

Просмотреть файл

@ -83,6 +83,10 @@ TOLERATED_DUPES = {
"memoffset": 2,
"mio": 2,
"pin-project-lite": 2,
# Transition from time 0.1 to 0.3 underway, but chrono is stuck on 0.1
# and hasn't been updated in 1.5 years (an hypothetical update is
# expected to remove the dependency on time altogether).
"time": 2,
"tokio": 3,
}

Просмотреть файл

@ -17,13 +17,13 @@ server = ["tokio", "warp"]
[dependencies]
base64 = "0.12"
bytes = "0.5"
cookie = { version = "0.12", default-features = false }
cookie = { version = "0.16", default-features = false }
http = "0.2"
log = "0.4"
serde = "1.0"
serde_json = "1.0"
serde_derive = "1.0"
time = "0.1"
time = "0.3"
tokio = { version = "0.2", features = ["rt-core"], optional = true}
unicode-segmentation = "1.2"
url = "2.0"

Просмотреть файл

@ -1 +1 @@
{"files":{"Cargo.toml":"2c65ccbf56c2640abee1927d35423ade48e20588630ac31e094d434595ee80d8","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"378f5840b258e2779c39418f3f2d7b2ba96f1c7917dd6be0713f88305dbda397","README.md":"3fcac69759e004d729a868ed5ef248c84d86fb0f3ab5c7a93abb547b91a0eb4e","src/builder.rs":"4200963d44d1a59f1268965b77407ba977eb5a777875cb76ea927ddc829be3d8","src/delta.rs":"510fc3dbf0a70d635d0488c5a5a32a2ba8e1490ce05bee39d944ea8c02189bbc","src/draft.rs":"950b43b3f6e1c4c13b1e90220c71defe02713170807b41e5ffde9a1327688f48","src/jar.rs":"8cc6531203e2a9422bfe1b2a00aeb96beb57d4676fa147a66f28f2d7c3129b57","src/lib.rs":"6a267d63ad90998f4a463c726be6a93fc33979eb8a72bfb53cae9f5b7a13fae0","src/parse.rs":"549844993601f20f5de3f5d5f8bea0fce3fe4f09d72e343aff9e433948a4ec5c","src/secure/key.rs":"734f35ef4b0d6b63174befdcb970f0304ac63f0895871b7c2f267fefdd43b648","src/secure/macros.rs":"83d770e5c4eb7fbd3c3d86973b69042e9e2bb9fafb72a4456598e2ae78638d5f","src/secure/mod.rs":"5d7fecb62295827d474ed1ce6b7628fe93d4a09eb14babfde036d64e8e4a04f8","src/secure/private.rs":"81d782cd4fa4b1415795710ad9e2e77eca3f4326e20ef96675093db9a378da32","src/secure/signed.rs":"26c46c2d561ea14d1d8d79f85342a98b4bd749df776677dde91dd9b928e91fbe"},"package":"888604f00b3db336d2af898ec3c1d5d0ddf5e6d462220f2ededc33a87ac4bbd5"}
{"files":{"CHANGELOG.md":"f28b8766c2e83ce8631e17bdeaea680c3a4306cc3a9a464e75c4e144bf7b48d4","Cargo.toml":"ea7621bda57463a85c1302db729ea460b19a6a97a36d50ac058c45ef4d8ce7e7","LICENSE-APACHE":"2773e20df8f4c52a026b5b578c7f2457341f5aa3fb6612fd87e1d2e1bd8f48ad","LICENSE-MIT":"f28420c1906af38be726cd7842798f0194b27c41d6051c0d1e9ee348b8a4a0ea","README.md":"c40f9fb713d6e7d471a1725315cc45fccfb62c6690ac32095351a4722b2f0c84","build.rs":"42c12800b13ac5ad41021f04a1bdb94de9d782d853ff5f54d8734bef1491e9d1","scripts/test.sh":"a76191d56d96c32efcb6883e0983e86beb4c6842e6e5c5a8bfded4c8183ff6f6","src/builder.rs":"238884aebf7fa2e4ab940faee4a5a2f5d6add35d2214ac644c36ec0ec64d7829","src/delta.rs":"4232e3acf98a70b111329c92cd823ba0419d4a12c3894091a318ae823dfdb225","src/draft.rs":"4f39d7acf3d3e868c013bab9f97ac570b983eed94d64fd59dba39343b870e4e0","src/expiration.rs":"188be15a5dd20d3471daa483609377ab66d876eb77afad1fc44891c18f251efd","src/jar.rs":"e91ba170610cc5b73a5cce8de1f6d960b26303ee5dff49822728e86a5fc64218","src/lib.rs":"e5a4bd66cf864e529ce81b33b02fe05fa807ad9ec1b368a062a05ad131ffdbba","src/parse.rs":"b37bd10631edc373ac82d9c9e5968416b04b3b705630ea4d4f9b1d7db9e695f6","src/secure/key.rs":"a8154b55c5435acba18132b90ab47f6d8ef229eb81240180079a99ae7885d8ca","src/secure/macros.rs":"18377b3fffdb2a1ad754f98f65a69d40a31fb69185fed231a4808ed4319985e4","src/secure/mod.rs":"6b2cf8486244545d51ecc5bd880845ae3f8d681ed86592e93cc4706b34704dcc","src/secure/private.rs":"0bd1b986d41b23fbb9c38836aa957b3db1f534c41dc205085a36d38099ad2f36","src/secure/signed.rs":"2375c29ca816e093fbee1db5631630f0ea47d92b1d16db458b7be14f30765117"},"package":"94d4706de1b0fa5b132270cddffa8585166037822e260a944fe161acd137ca05"}

142
third_party/rust/cookie/CHANGELOG.md поставляемый Normal file
Просмотреть файл

@ -0,0 +1,142 @@
# Version 0.16
## Version 0.16.0 (Dec 28, 2021)
### Breaking Changes
* The MSRV is now `1.53`, up from `1.41` in `0.15`.
* `time` has been updated to `0.3` and is reexported from the crate root.
### General Changes
* `rust-crypto` dependencies were updated to their latest versions.
# Version 0.15
## Version 0.15.1 (Jul 14, 2021)
### Changes and Fixes
* A panic that could result from non-char boundary indexing was fixed.
* Stale doc references to version `0.14` were updated.
## Version 0.15.0 (Feb 25, 2021)
### Breaking Changes
* `Cookie::force_remove()` takes `&Cookie` instead of `Cookie`.
* Child jar methods split into immutable and mutable versions
(`Cookie::{private{_mut}, signed{_mut}}`).
* `Cookie::encoded()` returns a new `Display` struct.
* Dates with year `<= 99` are handled like Chrome: range `0..=68` maps to
`2000..=2068`, `69..=99` to `1969..=1999`.
* `Cookie::{set_}expires()` operates on a new `Expiration` enum.
### New Features
* Added `Cookie::make_removal()` to manually create expired cookies.
* Added `Cookie::stripped()` display variant to print only the `name` and
`value` of a cookie.
* `Key` implements a constant-time `PartialEq`.
* Added `Key::master()` to retrieve the full 512-bit master key.
* Added `PrivateJar::decrypt()` to manually decrypt an encrypted `Cookie`.
* Added `SignedJar::verify()` to manually verify a signed `Cookie`.
* `Cookie::expires()` returns an `Option<Expiration>` to allow distinguishing
between unset and `None` expirations.
* Added `Cookie::expires_datetime()` to retrieve the expiration as an
`OffsetDateTime`.
* Added `Cookie::unset_expires()` to unset expirations.
### General Changes and Fixes
* MSRV is 1.41.
# Version 0.14
## Version 0.14.3 (Nov 5, 2020)
### Changes and Fixes
* `rust-crypto` dependencies were updated to their latest versions.
## Version 0.14.2 (Jul 22, 2020)
### Changes and Fixes
* Documentation now builds on the stable channel.
* `rust-crypto` dependencies were updated to their latest versions.
* Fixed 'interator' -> 'iterator' documentation typo.
## Version 0.14.1 (Jun 5, 2020)
### Changes and Fixes
* Updated `base64` dependency to 0.12.
* Updated minimum `time` dependency to correct version: 0.2.11.
* Added `readme` key to `Cargo.toml`, updated `license` field.
## Version 0.14.0 (May 29, 2020)
### Breaking Changes
* The `Key::from_master()` method was deprecated in favor of the more aptly
named `Key::derive_from()`.
* The deprecated `CookieJar::clear()` method was removed.
### New Features
* Added `Key::from()` to create a `Key` structure from a full-length key.
* Signed and private cookie jars can be individually enabled via the new
`signed` and `private` features, respectively.
* Key derivation via key expansion can be individually enabled via the new
`key-expansion` feature.
### General Changes and Fixes
* `ring` is no longer a dependency: `RustCrypto`-based cryptography is used in
lieu of `ring`. Prior to their inclusion here, the `hmac` and `hkdf` crates
were audited.
* Quotes, if present, are stripped from cookie values when parsing.
# Version 0.13
## Version 0.13.3 (Feb 3, 2020)
### Changes
* The `time` dependency was unpinned from `0.2.4`, allowing any `0.2.x`
version of `time` where `x >= 6`.
## Version 0.13.2 (Jan 28, 2020)
### Changes
* The `time` dependency was pinned to `0.2.4` due to upstream breaking changes
in `0.2.5`.
## Version 0.13.1 (Jan 23, 2020)
### New Features
* Added the `CookieJar::reset_delta()` method, which reverts all _delta_
changes to a `CookieJar`.
## Version 0.13.0 (Jan 21, 2020)
### Breaking Changes
* `time` was updated from 0.1 to 0.2.
* `ring` was updated from 0.14 to 0.16.
* `SameSite::None` now writes `SameSite=None` to correspond with updated
`SameSite` draft. `SameSite` can be unset by passing `None` to
`Cookie::set_same_site()`.
* `CookieBuilder` gained a lifetime: `CookieBuilder<'c>`.
### General Changes and Fixes
* Added a CHANGELOG.
* `expires`, `max_age`, `path`, and `domain` can be unset by passing `None` to
the respective `Cookie::set_{field}()` method.
* The "Expires" field is limited to a date-time of Dec 31, 9999, 23:59:59.
* The `%` character is now properly encoded and decoded.
* Constructor methods on `CookieBuilder` allow non-static lifetimes.

58
third_party/rust/cookie/Cargo.toml поставляемый
Просмотреть файл

@ -11,30 +11,60 @@
# will likely look very different (and much more reasonable)
[package]
edition = "2018"
name = "cookie"
version = "0.12.0"
authors = ["Alex Crichton <alex@alexcrichton.com>", "Sergio Benitez <sb@sergio.bz>"]
description = "Crate for parsing HTTP cookie headers and managing a cookie jar. Supports signed\nand private (encrypted + signed) jars.\n"
version = "0.16.0"
authors = ["Sergio Benitez <sb@sergio.bz>", "Alex Crichton <alex@alexcrichton.com>"]
build = "build.rs"
description = "HTTP cookie parsing and cookie jar management. Supports signed and private\n(encrypted, authenticated) jars.\n"
documentation = "https://docs.rs/cookie"
license = "MIT/Apache-2.0"
repository = "https://github.com/alexcrichton/cookie-rs"
readme = "README.md"
license = "MIT OR Apache-2.0"
repository = "https://github.com/SergioBenitez/cookie-rs"
[package.metadata.docs.rs]
all-features = true
[dependencies.aes-gcm]
version = "0.9.0"
optional = true
[dependencies.base64]
version = "0.13"
optional = true
[dependencies.hkdf]
version = "0.12.0"
optional = true
[dependencies.hmac]
version = "0.12.0"
optional = true
[dependencies.percent-encoding]
version = "2.0"
optional = true
[dependencies.rand]
version = "0.8"
optional = true
[dependencies.sha2]
version = "0.10.0"
optional = true
[dependencies.ring]
version = "0.14.0"
[dependencies.subtle]
version = "2.3"
optional = true
[dependencies.time]
version = "0.1"
[dependencies.url]
version = "1.0"
optional = true
version = "0.3"
features = ["std", "parsing", "formatting", "macros"]
default-features = false
[build-dependencies.version_check]
version = "0.9"
[features]
percent-encode = ["url"]
secure = ["ring", "base64"]
key-expansion = ["sha2", "hkdf"]
percent-encode = ["percent-encoding"]
private = ["aes-gcm", "base64", "rand", "subtle"]
secure = ["private", "signed", "key-expansion"]
signed = ["hmac", "sha2", "base64", "rand", "subtle"]

3
third_party/rust/cookie/LICENSE-APACHE поставляемый
Просмотреть файл

@ -186,7 +186,8 @@ APPENDIX: How to apply the Apache License to your work.
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Copyright 2017 Sergio Benitez
Copyright 2014 Alex Chricton
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.

1
third_party/rust/cookie/LICENSE-MIT поставляемый
Просмотреть файл

@ -1,3 +1,4 @@
Copyright (c) 2017 Sergio Benitez
Copyright (c) 2014 Alex Crichton
Permission is hereby granted, free of charge, to any

15
third_party/rust/cookie/README.md поставляемый
Просмотреть файл

@ -1,9 +1,10 @@
# cookie-rs
# Cookie
[![Build Status](https://travis-ci.com/SergioBenitez/cookie-rs.svg?branch=master)](https://travis-ci.com/SergioBenitez/cookie-rs)
[![CI Status](https://github.com/SergioBenitez/cookie-rs/workflows/CI/badge.svg)](https://github.com/SergioBenitez/cookie-rs/actions)
[![Current Crates.io Version](https://img.shields.io/crates/v/cookie.svg)](https://crates.io/crates/cookie)
[![Documentation](https://docs.rs/cookie/badge.svg)](https://docs.rs/cookie)
A library for parsing HTTP cookies and managing cookie jars.
A Rust library for parsing HTTP cookies and managing cookie jars.
# Usage
@ -11,11 +12,17 @@ Add the following to your `Cargo.toml`:
```toml
[dependencies]
cookie = "0.12"
cookie = "0.16"
```
See the [documentation](http://docs.rs/cookie) for detailed usage information.
# MSRV
khe minimum supported `rustc` version for cookie `0.16` is `1.53`.
The minimum supported `rustc` version for cookie `0.15` is `1.41`.
# License
This project is licensed under either of

5
third_party/rust/cookie/build.rs поставляемый Normal file
Просмотреть файл

@ -0,0 +1,5 @@
fn main() {
if let Some(true) = version_check::is_feature_flaggable() {
println!("cargo:rustc-cfg=nightly");
}
}

19
third_party/rust/cookie/scripts/test.sh поставляемый Executable file
Просмотреть файл

@ -0,0 +1,19 @@
#!/bin/bash
set -e
cargo build --verbose
cargo test --verbose --features percent-encode
cargo test --verbose --features private
cargo test --verbose --features signed
cargo test --verbose --features secure
cargo test --verbose --features 'private,key-expansion'
cargo test --verbose --features 'signed,key-expansion'
cargo test --verbose --features 'secure,percent-encode'
cargo test --verbose
cargo test --verbose --no-default-features
cargo test --verbose --all-features
rustdoc --test README.md -L target

66
third_party/rust/cookie/src/builder.rs поставляемый
Просмотреть файл

@ -1,25 +1,21 @@
use std::borrow::Cow;
use time::{Tm, Duration};
use ::{Cookie, SameSite};
use crate::{Cookie, SameSite, Expiration};
/// Structure that follows the builder pattern for building `Cookie` structs.
///
/// To construct a cookie:
///
/// 1. Call [`Cookie::build`](struct.Cookie.html#method.build) to start building.
/// 1. Call [`Cookie::build`] to start building.
/// 2. Use any of the builder methods to set fields in the cookie.
/// 3. Call [finish](#method.finish) to retrieve the built cookie.
/// 3. Call [`CookieBuilder::finish()`] to retrieve the built cookie.
///
/// # Example
///
/// ```rust
/// # extern crate cookie;
/// extern crate time;
///
/// use cookie::Cookie;
/// use time::Duration;
/// use cookie::time::Duration;
///
/// # fn main() {
/// let cookie: Cookie = Cookie::build("name", "value")
@ -32,16 +28,15 @@ use ::{Cookie, SameSite};
/// # }
/// ```
#[derive(Debug, Clone)]
pub struct CookieBuilder {
pub struct CookieBuilder<'c> {
/// The cookie being built.
cookie: Cookie<'static>,
cookie: Cookie<'c>,
}
impl CookieBuilder {
impl<'c> CookieBuilder<'c> {
/// Creates a new `CookieBuilder` instance from the given name and value.
///
/// This method is typically called indirectly via
/// [Cookie::build](struct.Cookie.html#method.build).
/// This method is typically called indirectly via [`Cookie::build()`].
///
/// # Example
///
@ -51,9 +46,9 @@ impl CookieBuilder {
/// let c = Cookie::build("foo", "bar").finish();
/// assert_eq!(c.name_value(), ("foo", "bar"));
/// ```
pub fn new<N, V>(name: N, value: V) -> CookieBuilder
where N: Into<Cow<'static, str>>,
V: Into<Cow<'static, str>>
pub fn new<N, V>(name: N, value: V) -> Self
where N: Into<Cow<'c, str>>,
V: Into<Cow<'c, str>>
{
CookieBuilder { cookie: Cookie::new(name, value) }
}
@ -64,20 +59,25 @@ impl CookieBuilder {
///
/// ```rust
/// # extern crate cookie;
/// extern crate time;
///
/// use cookie::Cookie;
/// use cookie::{Cookie, Expiration};
/// use cookie::time::OffsetDateTime;
///
/// # fn main() {
/// let c = Cookie::build("foo", "bar")
/// .expires(time::now())
/// .expires(OffsetDateTime::now_utc())
/// .finish();
///
/// assert!(c.expires().is_some());
///
/// let c = Cookie::build("foo", "bar")
/// .expires(None)
/// .finish();
///
/// assert_eq!(c.expires(), Some(Expiration::Session));
/// # }
/// ```
#[inline]
pub fn expires(mut self, when: Tm) -> CookieBuilder {
pub fn expires<E: Into<Expiration>>(mut self, when: E) -> Self {
self.cookie.set_expires(when);
self
}
@ -88,10 +88,8 @@ impl CookieBuilder {
///
/// ```rust
/// # extern crate cookie;
/// extern crate time;
/// use time::Duration;
///
/// use cookie::Cookie;
/// use cookie::time::Duration;
///
/// # fn main() {
/// let c = Cookie::build("foo", "bar")
@ -102,7 +100,7 @@ impl CookieBuilder {
/// # }
/// ```
#[inline]
pub fn max_age(mut self, value: Duration) -> CookieBuilder {
pub fn max_age(mut self, value: time::Duration) -> Self {
self.cookie.set_max_age(value);
self
}
@ -120,7 +118,7 @@ impl CookieBuilder {
///
/// assert_eq!(c.domain(), Some("www.rust-lang.org"));
/// ```
pub fn domain<D: Into<Cow<'static, str>>>(mut self, value: D) -> CookieBuilder {
pub fn domain<D: Into<Cow<'c, str>>>(mut self, value: D) -> Self {
self.cookie.set_domain(value);
self
}
@ -138,7 +136,7 @@ impl CookieBuilder {
///
/// assert_eq!(c.path(), Some("/"));
/// ```
pub fn path<P: Into<Cow<'static, str>>>(mut self, path: P) -> CookieBuilder {
pub fn path<P: Into<Cow<'c, str>>>(mut self, path: P) -> Self {
self.cookie.set_path(path);
self
}
@ -157,7 +155,7 @@ impl CookieBuilder {
/// assert_eq!(c.secure(), Some(true));
/// ```
#[inline]
pub fn secure(mut self, value: bool) -> CookieBuilder {
pub fn secure(mut self, value: bool) -> Self {
self.cookie.set_secure(value);
self
}
@ -176,7 +174,7 @@ impl CookieBuilder {
/// assert_eq!(c.http_only(), Some(true));
/// ```
#[inline]
pub fn http_only(mut self, value: bool) -> CookieBuilder {
pub fn http_only(mut self, value: bool) -> Self {
self.cookie.set_http_only(value);
self
}
@ -195,7 +193,7 @@ impl CookieBuilder {
/// assert_eq!(c.same_site(), Some(SameSite::Strict));
/// ```
#[inline]
pub fn same_site(mut self, value: SameSite) -> CookieBuilder {
pub fn same_site(mut self, value: SameSite) -> Self {
self.cookie.set_same_site(value);
self
}
@ -207,10 +205,8 @@ impl CookieBuilder {
///
/// ```rust
/// # extern crate cookie;
/// extern crate time;
///
/// use cookie::Cookie;
/// use time::Duration;
/// use cookie::time::Duration;
///
/// # fn main() {
/// let c = Cookie::build("foo", "bar")
@ -222,7 +218,7 @@ impl CookieBuilder {
/// # }
/// ```
#[inline]
pub fn permanent(mut self) -> CookieBuilder {
pub fn permanent(mut self) -> Self {
self.cookie.make_permanent();
self
}
@ -244,7 +240,7 @@ impl CookieBuilder {
/// assert_eq!(c.path(), Some("/"));
/// ```
#[inline]
pub fn finish(self) -> Cookie<'static> {
pub fn finish(self) -> Cookie<'c> {
self.cookie
}
}

12
third_party/rust/cookie/src/delta.rs поставляемый
Просмотреть файл

@ -2,7 +2,7 @@ use std::ops::{Deref, DerefMut};
use std::hash::{Hash, Hasher};
use std::borrow::Borrow;
use Cookie;
use crate::Cookie;
/// A `DeltaCookie` is a helper structure used in a cookie jar. It wraps a
/// `Cookie` so that it can be hashed and compared purely by name. It further
@ -19,20 +19,14 @@ impl DeltaCookie {
/// Create a new `DeltaCookie` that is being added to a jar.
#[inline]
pub fn added(cookie: Cookie<'static>) -> DeltaCookie {
DeltaCookie {
cookie: cookie,
removed: false,
}
DeltaCookie { cookie, removed: false, }
}
/// Create a new `DeltaCookie` that is being removed from a jar. The
/// `cookie` should be a "removal" cookie.
#[inline]
pub fn removed(cookie: Cookie<'static>) -> DeltaCookie {
DeltaCookie {
cookie: cookie,
removed: true,
}
DeltaCookie { cookie, removed: true, }
}
}

22
third_party/rust/cookie/src/draft.rs поставляемый
Просмотреть файл

@ -10,18 +10,28 @@ use std::fmt;
/// attribute is "Strict", then the cookie is never sent in cross-site requests.
/// If the `SameSite` attribute is "Lax", the cookie is only sent in cross-site
/// requests with "safe" HTTP methods, i.e, `GET`, `HEAD`, `OPTIONS`, `TRACE`.
/// If the `SameSite` attribute is not present (made explicit via the
/// `SameSite::None` variant), then the cookie will be sent as normal.
/// If the `SameSite` attribute is "None", the cookie is sent in all cross-site
/// requests if the "Secure" flag is also set, otherwise the cookie is ignored.
/// This library automatically sets the "Secure" flag on cookies when
/// `same_site` is set to `SameSite::None` as long as `secure` is not explicitly
/// set to `false`.
///
/// **Note:** This cookie attribute is an HTTP draft! Its meaning and definition
/// are subject to change.
/// If the `SameSite` attribute is not present (by not setting `SameSite`
/// initally or passing `None` to [`Cookie::set_same_site()`]), then the cookie
/// will be sent as normal.
///
/// **Note:** This cookie attribute is an [HTTP draft]! Its meaning and
/// definition are subject to change.
///
/// [`Cookie::set_same_site()`]: crate::Cookie::set_same_site()
/// [HTTP draft]: https://tools.ietf.org/html/draft-west-cookie-incrementalism-00
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum SameSite {
/// The "Strict" `SameSite` attribute.
Strict,
/// The "Lax" `SameSite` attribute.
Lax,
/// No `SameSite` attribute.
/// The "None" `SameSite` attribute.
None
}
@ -92,7 +102,7 @@ impl fmt::Display for SameSite {
match *self {
SameSite::Strict => write!(f, "Strict"),
SameSite::Lax => write!(f, "Lax"),
SameSite::None => Ok(()),
SameSite::None => write!(f, "None"),
}
}
}

137
third_party/rust/cookie/src/expiration.rs поставляемый Normal file
Просмотреть файл

@ -0,0 +1,137 @@
use time::OffsetDateTime;
/// A cookie's expiration: either session or a date-time.
///
/// An `Expiration` is constructible via `Expiration::from()` with an
/// `Option<OffsetDateTime>` or an `OffsetDateTime`:
///
/// * `None` -> `Expiration::Session`
/// * `Some(OffsetDateTime)` -> `Expiration::DateTime`
/// * `OffsetDateTime` -> `Expiration::DateTime`
///
/// ```rust
/// use cookie::Expiration;
/// use time::OffsetDateTime;
///
/// let expires = Expiration::from(None);
/// assert_eq!(expires, Expiration::Session);
///
/// let now = OffsetDateTime::now_utc();
/// let expires = Expiration::from(now);
/// assert_eq!(expires, Expiration::DateTime(now));
///
/// let expires = Expiration::from(Some(now));
/// assert_eq!(expires, Expiration::DateTime(now));
/// ```
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Expiration {
/// Expiration for a "permanent" cookie at a specific date-time.
DateTime(OffsetDateTime),
/// Expiration for a "session" cookie. Browsers define the notion of a
/// "session" and will automatically expire session cookies when they deem
/// the "session" to be over. This is typically, but need not be, when the
/// browser is closed.
Session,
}
impl Expiration {
/// Returns `true` if `self` is an `Expiration::DateTime`.
///
/// # Example
///
/// ```rust
/// use cookie::Expiration;
/// use time::OffsetDateTime;
///
/// let expires = Expiration::from(None);
/// assert!(!expires.is_datetime());
///
/// let expires = Expiration::from(OffsetDateTime::now_utc());
/// assert!(expires.is_datetime());
/// ```
pub fn is_datetime(&self) -> bool {
match self {
Expiration::DateTime(_) => true,
Expiration::Session => false
}
}
/// Returns `true` if `self` is an `Expiration::Session`.
///
/// # Example
///
/// ```rust
/// use cookie::Expiration;
/// use time::OffsetDateTime;
///
/// let expires = Expiration::from(None);
/// assert!(expires.is_session());
///
/// let expires = Expiration::from(OffsetDateTime::now_utc());
/// assert!(!expires.is_session());
/// ```
pub fn is_session(&self) -> bool {
match self {
Expiration::DateTime(_) => false,
Expiration::Session => true
}
}
/// Returns the inner `OffsetDateTime` if `self` is a `DateTime`.
///
/// # Example
///
/// ```rust
/// use cookie::Expiration;
/// use time::OffsetDateTime;
///
/// let expires = Expiration::from(None);
/// assert!(expires.datetime().is_none());
///
/// let now = OffsetDateTime::now_utc();
/// let expires = Expiration::from(now);
/// assert_eq!(expires.datetime(), Some(now));
/// ```
pub fn datetime(self) -> Option<OffsetDateTime> {
match self {
Expiration::Session => None,
Expiration::DateTime(v) => Some(v)
}
}
/// Applied `f` to the inner `OffsetDateTime` if `self` is a `DateTime` and
/// returns the mapped `Expiration`.
///
/// # Example
///
/// ```rust
/// use cookie::Expiration;
/// use time::{OffsetDateTime, Duration};
///
/// let now = OffsetDateTime::now_utc();
/// let one_week = Duration::weeks(1);
///
/// let expires = Expiration::from(now);
/// assert_eq!(expires.map(|t| t + one_week).datetime(), Some(now + one_week));
///
/// let expires = Expiration::from(None);
/// assert_eq!(expires.map(|t| t + one_week).datetime(), None);
/// ```
pub fn map<F>(self, f: F) -> Self
where F: FnOnce(OffsetDateTime) -> OffsetDateTime
{
match self {
Expiration::Session => Expiration::Session,
Expiration::DateTime(v) => Expiration::DateTime(f(v)),
}
}
}
impl<T: Into<Option<OffsetDateTime>>> From<T> for Expiration {
fn from(option: T) -> Self {
match option.into() {
Some(value) => Expiration::DateTime(value),
None => Expiration::Session
}
}
}

185
third_party/rust/cookie/src/jar.rs поставляемый
Просмотреть файл

@ -1,18 +1,17 @@
use std::collections::HashSet;
use std::mem::replace;
use time::{self, Duration};
#[cfg(feature = "signed")] use crate::secure::SignedJar;
#[cfg(feature = "private")] use crate::secure::PrivateJar;
#[cfg(any(feature = "signed", feature = "private"))] use crate::secure::Key;
#[cfg(feature = "secure")]
use secure::{PrivateJar, SignedJar, Key};
use delta::DeltaCookie;
use Cookie;
use crate::delta::DeltaCookie;
use crate::Cookie;
/// A collection of cookies that tracks its modifications.
///
/// A `CookieJar` provides storage for any number of cookies. Any changes made
/// to the jar are tracked; the changes can be retrieved via the
/// [delta](#method.delta) method which returns an interator over the changes.
/// [delta](#method.delta) method which returns an iterator over the changes.
///
/// # Usage
///
@ -118,7 +117,7 @@ impl CookieJar {
self.delta_cookies
.get(name)
.or_else(|| self.original_cookies.get(name))
.and_then(|c| if !c.removed { Some(&c.cookie) } else { None })
.and_then(|c| if c.removed { None } else { Some(&c.cookie) })
}
/// Adds an "original" `cookie` to this jar. If an original cookie with the
@ -179,7 +178,7 @@ impl CookieJar {
///
/// A "removal" cookie is a cookie that has the same name as the original
/// cookie but has an empty value, a max-age of 0, and an expiration date
/// far in the past.
/// far in the past. See also [`Cookie::make_removal()`].
///
/// # Example
///
@ -187,10 +186,8 @@ impl CookieJar {
///
/// ```rust
/// # extern crate cookie;
/// extern crate time;
///
/// use cookie::{CookieJar, Cookie};
/// use time::Duration;
/// use cookie::time::Duration;
///
/// # fn main() {
/// let mut jar = CookieJar::new();
@ -209,7 +206,8 @@ impl CookieJar {
/// # }
/// ```
///
/// Removing a new cookie does not result in a _removal_ cookie:
/// Removing a new cookie does not result in a _removal_ cookie unless
/// there's an original cookie with the same name:
///
/// ```rust
/// use cookie::{CookieJar, Cookie};
@ -220,12 +218,17 @@ impl CookieJar {
///
/// jar.remove(Cookie::named("name"));
/// assert_eq!(jar.delta().count(), 0);
///
/// jar.add_original(Cookie::new("name", "value"));
/// jar.add(Cookie::new("name", "value"));
/// assert_eq!(jar.delta().count(), 1);
///
/// jar.remove(Cookie::named("name"));
/// assert_eq!(jar.delta().count(), 1);
/// ```
pub fn remove(&mut self, mut cookie: Cookie<'static>) {
if self.original_cookies.contains(cookie.name()) {
cookie.set_value("");
cookie.set_max_age(Duration::seconds(0));
cookie.set_expires(time::now() - Duration::days(365));
cookie.make_removal();
self.delta_cookies.replace(DeltaCookie::removed(cookie));
} else {
self.delta_cookies.remove(cookie.name());
@ -243,10 +246,8 @@ impl CookieJar {
///
/// ```rust
/// # extern crate cookie;
/// extern crate time;
///
/// use cookie::{CookieJar, Cookie};
/// use time::Duration;
/// use cookie::time::Duration;
///
/// # fn main() {
/// let mut jar = CookieJar::new();
@ -258,30 +259,56 @@ impl CookieJar {
/// assert_eq!(jar.iter().count(), 2);
///
/// // Now force remove the original cookie.
/// jar.force_remove(Cookie::new("name", "value"));
/// jar.force_remove(&Cookie::named("name"));
/// assert_eq!(jar.delta().count(), 1);
/// assert_eq!(jar.iter().count(), 1);
///
/// // Now force remove the new cookie.
/// jar.force_remove(Cookie::new("key", "value"));
/// jar.force_remove(&Cookie::named("key"));
/// assert_eq!(jar.delta().count(), 0);
/// assert_eq!(jar.iter().count(), 0);
/// # }
/// ```
pub fn force_remove<'a>(&mut self, cookie: Cookie<'a>) {
pub fn force_remove<'a>(&mut self, cookie: &Cookie<'a>) {
self.original_cookies.remove(cookie.name());
self.delta_cookies.remove(cookie.name());
}
/// Removes all cookies from this cookie jar.
#[deprecated(since = "0.7.0", note = "calling this method may not remove \
all cookies since the path and domain are not specified; use \
`remove` instead")]
pub fn clear(&mut self) {
self.delta_cookies.clear();
for delta in replace(&mut self.original_cookies, HashSet::new()) {
self.remove(delta.cookie);
}
/// Removes all delta cookies, i.e. all cookies not added via
/// [`CookieJar::add_original()`], from this `CookieJar`. This undoes any
/// changes from [`CookieJar::add()`] and [`CookieJar::remove()`]
/// operations.
///
/// # Example
///
/// ```rust
/// use cookie::{CookieJar, Cookie};
///
/// let mut jar = CookieJar::new();
///
/// // Only original cookies will remain after calling `reset_delta`.
/// jar.add_original(Cookie::new("name", "value"));
/// jar.add_original(Cookie::new("language", "Rust"));
///
/// // These operations, represented by delta cookies, will be reset.
/// jar.add(Cookie::new("language", "C++"));
/// jar.remove(Cookie::named("name"));
///
/// // All is normal.
/// assert_eq!(jar.get("name"), None);
/// assert_eq!(jar.get("language").map(Cookie::value), Some("C++"));
/// assert_eq!(jar.iter().count(), 1);
/// assert_eq!(jar.delta().count(), 2);
///
/// // Resetting undoes delta operations.
/// jar.reset_delta();
/// assert_eq!(jar.get("name").map(Cookie::value), Some("value"));
/// assert_eq!(jar.get("language").map(Cookie::value), Some("Rust"));
/// assert_eq!(jar.iter().count(), 2);
/// assert_eq!(jar.delta().count(), 0);
/// ```
pub fn reset_delta(&mut self) {
self.delta_cookies = HashSet::new();
}
/// Returns an iterator over cookies that represent the changes to this jar
@ -350,14 +377,9 @@ impl CookieJar {
}
}
/// Returns a `PrivateJar` with `self` as its parent jar using the key `key`
/// to sign/encrypt and verify/decrypt cookies added/retrieved from the
/// child jar.
///
/// Any modifications to the child jar will be reflected on the parent jar,
/// and any retrievals from the child jar will be made from the parent jar.
///
/// This method is only available when the `secure` feature is enabled.
/// Returns a read-only `PrivateJar` with `self` as its parent jar using the
/// key `key` to verify/decrypt cookies retrieved from the child jar. Any
/// retrievals from the child jar will be made from the parent jar.
///
/// # Example
///
@ -369,7 +391,7 @@ impl CookieJar {
///
/// // Add a private (signed + encrypted) cookie.
/// let mut jar = CookieJar::new();
/// jar.private(&key).add(Cookie::new("private", "text"));
/// jar.private_mut(&key).add(Cookie::new("private", "text"));
///
/// // The cookie's contents are encrypted.
/// assert_ne!(jar.get("private").unwrap().value(), "text");
@ -383,18 +405,43 @@ impl CookieJar {
/// assert!(jar.private(&key).get("private").is_none());
/// assert!(jar.get("private").is_some());
/// ```
#[cfg(feature = "secure")]
pub fn private(&mut self, key: &Key) -> PrivateJar {
#[cfg(feature = "private")]
#[cfg_attr(all(nightly, doc), doc(cfg(feature = "private")))]
pub fn private<'a>(&'a self, key: &Key) -> PrivateJar<&'a Self> {
PrivateJar::new(self, key)
}
/// Returns a `SignedJar` with `self` as its parent jar using the key `key`
/// to sign/verify cookies added/retrieved from the child jar.
/// Returns a read/write `PrivateJar` with `self` as its parent jar using
/// the key `key` to sign/encrypt and verify/decrypt cookies added/retrieved
/// from the child jar.
///
/// Any modifications to the child jar will be reflected on the parent jar,
/// and any retrievals from the child jar will be made from the parent jar.
///
/// This method is only available when the `secure` feature is enabled.
/// # Example
///
/// ```rust
/// use cookie::{Cookie, CookieJar, Key};
///
/// // Generate a secure key.
/// let key = Key::generate();
///
/// // Add a private (signed + encrypted) cookie.
/// let mut jar = CookieJar::new();
/// jar.private_mut(&key).add(Cookie::new("private", "text"));
///
/// // Remove a cookie using the child jar.
/// jar.private_mut(&key).remove(Cookie::named("private"));
/// ```
#[cfg(feature = "private")]
#[cfg_attr(all(nightly, doc), doc(cfg(feature = "private")))]
pub fn private_mut<'a>(&'a mut self, key: &Key) -> PrivateJar<&'a mut Self> {
PrivateJar::new(self, key)
}
/// Returns a read-only `SignedJar` with `self` as its parent jar using the
/// key `key` to verify cookies retrieved from the child jar. Any retrievals
/// from the child jar will be made from the parent jar.
///
/// # Example
///
@ -406,7 +453,7 @@ impl CookieJar {
///
/// // Add a signed cookie.
/// let mut jar = CookieJar::new();
/// jar.signed(&key).add(Cookie::new("signed", "text"));
/// jar.signed_mut(&key).add(Cookie::new("signed", "text"));
///
/// // The cookie's contents are signed but still in plaintext.
/// assert_ne!(jar.get("signed").unwrap().value(), "text");
@ -421,8 +468,36 @@ impl CookieJar {
/// assert!(jar.signed(&key).get("signed").is_none());
/// assert!(jar.get("signed").is_some());
/// ```
#[cfg(feature = "secure")]
pub fn signed(&mut self, key: &Key) -> SignedJar {
#[cfg(feature = "signed")]
#[cfg_attr(all(nightly, doc), doc(cfg(feature = "signed")))]
pub fn signed<'a>(&'a self, key: &Key) -> SignedJar<&'a Self> {
SignedJar::new(self, key)
}
/// Returns a read/write `SignedJar` with `self` as its parent jar using the
/// key `key` to sign/verify cookies added/retrieved from the child jar.
///
/// Any modifications to the child jar will be reflected on the parent jar,
/// and any retrievals from the child jar will be made from the parent jar.
///
/// # Example
///
/// ```rust
/// use cookie::{Cookie, CookieJar, Key};
///
/// // Generate a secure key.
/// let key = Key::generate();
///
/// // Add a signed cookie.
/// let mut jar = CookieJar::new();
/// jar.signed_mut(&key).add(Cookie::new("signed", "text"));
///
/// // Remove a cookie.
/// jar.signed_mut(&key).remove(Cookie::named("signed"));
/// ```
#[cfg(feature = "signed")]
#[cfg_attr(all(nightly, doc), doc(cfg(feature = "signed")))]
pub fn signed_mut<'a>(&'a mut self, key: &Key) -> SignedJar<&'a mut Self> {
SignedJar::new(self, key)
}
}
@ -468,7 +543,7 @@ impl<'a> Iterator for Iter<'a> {
#[cfg(test)]
mod test {
use super::CookieJar;
use Cookie;
use crate::Cookie;
#[test]
#[allow(deprecated)]
@ -483,7 +558,8 @@ mod test {
assert!(c.get("test2").is_some());
c.add(Cookie::new("test3", ""));
c.clear();
c.remove(Cookie::named("test2"));
c.remove(Cookie::named("test3"));
assert!(c.get("test").is_none());
assert!(c.get("test2").is_none());
@ -500,9 +576,9 @@ mod test {
}
#[test]
#[cfg(feature = "secure")]
#[cfg(all(feature = "signed", feature = "private"))]
fn iter() {
let key = ::Key::generate();
let key = crate::Key::generate();
let mut c = CookieJar::new();
c.add_original(Cookie::new("original", "original"));
@ -512,8 +588,8 @@ mod test {
c.add(Cookie::new("test3", "test3"));
assert_eq!(c.iter().count(), 4);
c.signed(&key).add(Cookie::new("signed", "signed"));
c.private(&key).add(Cookie::new("encrypted", "encrypted"));
c.signed_mut(&key).add(Cookie::new("signed", "signed"));
c.private_mut(&key).add(Cookie::new("encrypted", "encrypted"));
assert_eq!(c.iter().count(), 6);
c.remove(Cookie::named("test"));
@ -531,7 +607,6 @@ mod test {
}
#[test]
#[cfg(feature = "secure")]
fn delta() {
use std::collections::HashMap;
use time::Duration;

673
third_party/rust/cookie/src/lib.rs поставляемый

Разница между файлами не показана из-за своего большого размера Загрузить разницу

260
third_party/rust/cookie/src/parse.rs поставляемый
Просмотреть файл

@ -1,21 +1,29 @@
use std::borrow::Cow;
use std::cmp;
use std::error::Error;
use std::convert::{From, TryFrom};
use std::str::Utf8Error;
use std::fmt;
use std::convert::From;
#[allow(unused_imports, deprecated)]
use std::ascii::AsciiExt;
#[cfg(feature = "percent-encode")]
use url::percent_encoding::percent_decode;
use time::{self, Duration};
use percent_encoding::percent_decode;
use time::{PrimitiveDateTime, Duration, OffsetDateTime};
use time::{parsing::Parsable, macros::format_description, format_description::FormatItem};
use ::{Cookie, SameSite, CookieStr};
use crate::{Cookie, SameSite, CookieStr};
// The three formats spec'd in http://tools.ietf.org/html/rfc2616#section-3.3.1.
// Additional ones as encountered in the real world.
pub static FMT1: &[FormatItem<'_>] = format_description!("[weekday repr:short], [day] [month repr:short] [year padding:none] [hour]:[minute]:[second] GMT");
pub static FMT2: &[FormatItem<'_>] = format_description!("[weekday], [day]-[month repr:short]-[year repr:last_two] [hour]:[minute]:[second] GMT");
pub static FMT3: &[FormatItem<'_>] = format_description!("[weekday repr:short] [month repr:short] [day padding:space] [hour]:[minute]:[second] [year padding:none]");
pub static FMT4: &[FormatItem<'_>] = format_description!("[weekday repr:short], [day]-[month repr:short]-[year padding:none] [hour]:[minute]:[second] GMT");
/// Enum corresponding to a parsing error.
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[non_exhaustive]
pub enum ParseError {
/// The cookie did not contain a name/value pair.
MissingPair,
@ -23,10 +31,6 @@ pub enum ParseError {
EmptyName,
/// Decoding the cookie's name or value resulted in invalid UTF-8.
Utf8Error(Utf8Error),
/// It is discouraged to exhaustively match on this enum as its variants may
/// grow without a breaking-change bump in version numbers.
#[doc(hidden)]
__Nonexhasutive,
}
impl ParseError {
@ -38,7 +42,6 @@ impl ParseError {
ParseError::Utf8Error(_) => {
"decoding the cookie's name or value resulted in invalid UTF-8"
}
ParseError::__Nonexhasutive => unreachable!("__Nonexhasutive ParseError"),
}
}
}
@ -79,18 +82,39 @@ fn indexes_of(needle: &str, haystack: &str) -> Option<(usize, usize)> {
}
#[cfg(feature = "percent-encode")]
fn name_val_decoded(name: &str, val: &str) -> Result<(CookieStr, CookieStr), ParseError> {
fn name_val_decoded(
name: &str,
val: &str
) -> Result<Option<(CookieStr<'static>, CookieStr<'static>)>, ParseError> {
let decoded_name = percent_decode(name.as_bytes()).decode_utf8()?;
let decoded_value = percent_decode(val.as_bytes()).decode_utf8()?;
let name = CookieStr::Concrete(Cow::Owned(decoded_name.into_owned()));
let val = CookieStr::Concrete(Cow::Owned(decoded_value.into_owned()));
Ok((name, val))
if let (&Cow::Borrowed(_), &Cow::Borrowed(_)) = (&decoded_name, &decoded_value) {
Ok(None)
} else {
let name = CookieStr::Concrete(Cow::Owned(decoded_name.into()));
let val = CookieStr::Concrete(Cow::Owned(decoded_value.into()));
Ok(Some((name, val)))
}
}
#[cfg(not(feature = "percent-encode"))]
fn name_val_decoded(_: &str, _: &str) -> Result<(CookieStr, CookieStr), ParseError> {
unreachable!("This function should never be called when the feature is disabled!")
fn name_val_decoded(
_: &str,
_: &str
) -> Result<Option<(CookieStr<'static>, CookieStr<'static>)>, ParseError> {
unreachable!("This function should never be called with 'percent-encode' disabled!")
}
fn trim_quotes(s: &str) -> &str {
if s.len() < 2 {
return s;
}
match (s.chars().next(), s.chars().last()) {
(Some('"'), Some('"')) => &s[1..(s.len() - 1)],
_ => s
}
}
// This function does the real parsing but _does not_ set the `cookie_string` in
@ -99,14 +123,14 @@ fn name_val_decoded(_: &str, _: &str) -> Result<(CookieStr, CookieStr), ParseErr
// set in the outer `parse` function.
fn parse_inner<'c>(s: &str, decode: bool) -> Result<Cookie<'c>, ParseError> {
let mut attributes = s.split(';');
let key_value = match attributes.next() {
Some(s) => s,
_ => panic!(),
};
// Determine the name = val.
let key_value = attributes.next().expect("first str::split().next() returns Some");
let (name, value) = match key_value.find('=') {
Some(i) => (key_value[..i].trim(), key_value[(i + 1)..].trim()),
Some(i) => {
let (key, value) = (key_value[..i].trim(), key_value[(i + 1)..].trim());
(key, trim_quotes(value).trim())
},
None => return Err(ParseError::MissingPair)
};
@ -114,23 +138,29 @@ fn parse_inner<'c>(s: &str, decode: bool) -> Result<Cookie<'c>, ParseError> {
return Err(ParseError::EmptyName);
}
// Create a cookie with all of the defaults. We'll fill things in while we
// iterate through the parameters below.
let (name, value) = if decode {
name_val_decoded(name, value)?
} else {
// If there is nothing to decode, or we're not decoding, use indexes.
let indexed_names = |s, name, value| {
let name_indexes = indexes_of(name, s).expect("name sub");
let value_indexes = indexes_of(value, s).expect("value sub");
let name = CookieStr::Indexed(name_indexes.0, name_indexes.1);
let value = CookieStr::Indexed(value_indexes.0, value_indexes.1);
(name, value)
};
let mut cookie = Cookie {
// Create a cookie with all of the defaults. We'll fill things in while we
// iterate through the parameters below.
let (name, value) = if decode {
match name_val_decoded(name, value)? {
Some((name, value)) => (name, value),
None => indexed_names(s, name, value)
}
} else {
indexed_names(s, name, value)
};
let mut cookie: Cookie<'c> = Cookie {
name, value,
cookie_string: None,
name: name,
value: value,
expires: None,
max_age: None,
domain: None,
@ -149,21 +179,26 @@ fn parse_inner<'c>(s: &str, decode: bool) -> Result<Cookie<'c>, ParseError> {
match (&*key.to_ascii_lowercase(), value) {
("secure", _) => cookie.secure = Some(true),
("httponly", _) => cookie.http_only = Some(true),
("max-age", Some(v)) => {
// See RFC 6265 Section 5.2.2, negative values indicate that the
// earliest possible expiration time should be used, so set the
// max age as 0 seconds.
cookie.max_age = match v.parse() {
Ok(val) if val <= 0 => Some(Duration::zero()),
Ok(val) => {
// Don't panic if the max age seconds is greater than what's supported by
// `Duration`.
let val = cmp::min(val, Duration::max_value().num_seconds());
Some(Duration::seconds(val))
}
Err(_) => continue,
};
}
("max-age", Some(mut v)) => cookie.max_age = {
let is_negative = v.starts_with('-');
if is_negative {
v = &v[1..];
}
if !v.chars().all(|d| d.is_digit(10)) {
continue
}
// From RFC 6265 5.2.2: neg values indicate that the earliest
// expiration should be used, so set the max age to 0 seconds.
if is_negative {
Some(Duration::ZERO)
} else {
Some(v.parse::<i64>()
.map(Duration::seconds)
.unwrap_or_else(|_| Duration::seconds(i64::max_value())))
}
},
("domain", Some(mut domain)) if !domain.is_empty() => {
if domain.starts_with('.') {
domain = &domain[1..];
@ -181,6 +216,8 @@ fn parse_inner<'c>(s: &str, decode: bool) -> Result<Cookie<'c>, ParseError> {
cookie.same_site = Some(SameSite::Strict);
} else if v.eq_ignore_ascii_case("lax") {
cookie.same_site = Some(SameSite::Lax);
} else if v.eq_ignore_ascii_case("none") {
cookie.same_site = Some(SameSite::None);
} else {
// We do nothing here, for now. When/if the `SameSite`
// attribute becomes standard, the spec says that we should
@ -190,16 +227,14 @@ fn parse_inner<'c>(s: &str, decode: bool) -> Result<Cookie<'c>, ParseError> {
}
}
("expires", Some(v)) => {
// Try strptime with three date formats according to
// http://tools.ietf.org/html/rfc2616#section-3.3.1. Try
// additional ones as encountered in the real world.
let tm = time::strptime(v, "%a, %d %b %Y %H:%M:%S %Z")
.or_else(|_| time::strptime(v, "%A, %d-%b-%y %H:%M:%S %Z"))
.or_else(|_| time::strptime(v, "%a, %d-%b-%Y %H:%M:%S %Z"))
.or_else(|_| time::strptime(v, "%a %b %d %H:%M:%S %Y"));
let tm = parse_date(v, &FMT1)
.or_else(|_| parse_date(v, &FMT2))
.or_else(|_| parse_date(v, &FMT3))
.or_else(|_| parse_date(v, &FMT4));
// .or_else(|_| parse_date(v, &FMT5));
if let Ok(time) = tm {
cookie.expires = Some(time)
cookie.expires = Some(time.into())
}
}
_ => {
@ -214,7 +249,7 @@ fn parse_inner<'c>(s: &str, decode: bool) -> Result<Cookie<'c>, ParseError> {
Ok(cookie)
}
pub fn parse_cookie<'c, S>(cow: S, decode: bool) -> Result<Cookie<'c>, ParseError>
pub(crate) fn parse_cookie<'c, S>(cow: S, decode: bool) -> Result<Cookie<'c>, ParseError>
where S: Into<Cow<'c, str>>
{
let s = cow.into();
@ -223,10 +258,27 @@ pub fn parse_cookie<'c, S>(cow: S, decode: bool) -> Result<Cookie<'c>, ParseErro
Ok(cookie)
}
pub(crate) fn parse_date(s: &str, format: &impl Parsable) -> Result<OffsetDateTime, time::Error> {
// Parse. Handle "abbreviated" dates like Chromium. See cookie#162.
let mut date = format.parse(s.as_bytes())?;
if let Some(y) = date.year().or_else(|| date.year_last_two().map(|v| v as i32)) {
let offset = match y {
0..=68 => 2000,
69..=99 => 1900,
_ => 0,
};
date.set_year(y + offset);
}
Ok(PrimitiveDateTime::try_from(date)?.assume_utc())
}
#[cfg(test)]
mod tests {
use ::{Cookie, SameSite};
use ::time::{strptime, Duration};
use super::parse_date;
use crate::{Cookie, SameSite};
use time::Duration;
macro_rules! assert_eq_parse {
($string:expr, $expected:expr) => (
@ -271,6 +323,15 @@ mod tests {
assert_eq_parse!("foo=bar; SameSite=strict", expected);
assert_eq_parse!("foo=bar; SameSite=STrICT", expected);
assert_eq_parse!("foo=bar; SameSite=STRICT", expected);
let expected = Cookie::build("foo", "bar")
.same_site(SameSite::None)
.finish();
assert_eq_parse!("foo=bar; SameSite=None", expected);
assert_eq_parse!("foo=bar; SameSITE=none", expected);
assert_eq_parse!("foo=bar; SameSite=NOne", expected);
assert_eq_parse!("foo=bar; SameSite=nOne", expected);
}
#[test]
@ -283,10 +344,28 @@ mod tests {
let expected = Cookie::build("foo", "bar=baz").finish();
assert_eq_parse!("foo=bar=baz", expected);
let expected = Cookie::build("foo", "\"bar\"").finish();
assert_eq_parse!("foo=\"\"bar\"\"", expected);
let expected = Cookie::build("foo", "\"bar").finish();
assert_eq_parse!("foo= \"bar", expected);
assert_eq_parse!("foo=\"bar ", expected);
assert_eq_parse!("foo=\"\"bar\"", expected);
assert_eq_parse!("foo=\"\"bar \"", expected);
assert_eq_parse!("foo=\"\"bar \" ", expected);
let expected = Cookie::build("foo", "bar\"").finish();
assert_eq_parse!("foo=bar\"", expected);
assert_eq_parse!("foo=\"bar\"\"", expected);
assert_eq_parse!("foo=\" bar\"\"", expected);
assert_eq_parse!("foo=\" bar\" \" ", expected);
let mut expected = Cookie::build("foo", "bar").finish();
assert_eq_parse!("foo=bar", expected);
assert_eq_parse!("foo = bar", expected);
assert_eq_parse!("foo=\"bar\"", expected);
assert_eq_parse!(" foo=bar ", expected);
assert_eq_parse!(" foo=\"bar \" ", expected);
assert_eq_parse!(" foo=bar ;Domain=", expected);
assert_eq_parse!(" foo=bar ;Domain= ", expected);
assert_eq_parse!(" foo=bar ;Ignored", expected);
@ -316,7 +395,7 @@ mod tests {
assert_ne_parse!(" foo=bar ;HttpOnly; secure", unexpected);
assert_ne_parse!(" foo=bar ;HttpOnly; secure", unexpected);
expected.set_max_age(Duration::zero());
expected.set_max_age(Duration::ZERO);
assert_eq_parse!(" foo=bar ;HttpOnly; Secure; Max-Age=0", expected);
assert_eq_parse!(" foo=bar ;HttpOnly; Secure; Max-Age = 0 ", expected);
assert_eq_parse!(" foo=bar ;HttpOnly; Secure; Max-Age=-1", expected);
@ -371,18 +450,76 @@ mod tests {
Domain=FOO.COM", unexpected);
let time_str = "Wed, 21 Oct 2015 07:28:00 GMT";
let expires = strptime(time_str, "%a, %d %b %Y %H:%M:%S %Z").unwrap();
let expires = parse_date(time_str, &super::FMT1).unwrap();
expected.set_expires(expires);
assert_eq_parse!(" foo=bar ;HttpOnly; Secure; Max-Age=4; Path=/foo; \
Domain=foo.com; Expires=Wed, 21 Oct 2015 07:28:00 GMT", expected);
unexpected.set_domain("foo.com");
let bad_expires = strptime(time_str, "%a, %d %b %Y %H:%S:%M %Z").unwrap();
let bad_expires = parse_date(time_str, &super::FMT1).unwrap();
expected.set_expires(bad_expires);
assert_ne_parse!(" foo=bar ;HttpOnly; Secure; Max-Age=4; Path=/foo; \
Domain=foo.com; Expires=Wed, 21 Oct 2015 07:28:00 GMT", unexpected);
}
#[test]
fn parse_abbreviated_years() {
let cookie_str = "foo=bar; expires=Thu, 10-Sep-20 20:00:00 GMT";
let cookie = Cookie::parse(cookie_str).unwrap();
assert_eq!(cookie.expires_datetime().unwrap().year(), 2020);
let cookie_str = "foo=bar; expires=Thu, 10-Sep-68 20:00:00 GMT";
let cookie = Cookie::parse(cookie_str).unwrap();
assert_eq!(cookie.expires_datetime().unwrap().year(), 2068);
let cookie_str = "foo=bar; expires=Thu, 10-Sep-69 20:00:00 GMT";
let cookie = Cookie::parse(cookie_str).unwrap();
assert_eq!(cookie.expires_datetime().unwrap().year(), 1969);
let cookie_str = "foo=bar; expires=Thu, 10-Sep-99 20:00:00 GMT";
let cookie = Cookie::parse(cookie_str).unwrap();
assert_eq!(cookie.expires_datetime().unwrap().year(), 1999);
let cookie_str = "foo=bar; expires=Thu, 10-Sep-2069 20:00:00 GMT";
let cookie = Cookie::parse(cookie_str).unwrap();
assert_eq!(cookie.expires_datetime().unwrap().year(), 2069);
}
#[test]
fn parse_variant_date_fmts() {
let cookie_str = "foo=bar; expires=Sun, 06 Nov 1994 08:49:37 GMT";
Cookie::parse(cookie_str).unwrap().expires_datetime().unwrap();
let cookie_str = "foo=bar; expires=Sunday, 06-Nov-94 08:49:37 GMT";
Cookie::parse(cookie_str).unwrap().expires_datetime().unwrap();
let cookie_str = "foo=bar; expires=Sun Nov 6 08:49:37 1994";
Cookie::parse(cookie_str).unwrap().expires_datetime().unwrap();
}
#[test]
fn parse_very_large_max_ages() {
let mut expected = Cookie::build("foo", "bar")
.max_age(Duration::seconds(i64::max_value()))
.finish();
let string = format!("foo=bar; Max-Age={}", 1u128 << 100);
assert_eq_parse!(&string, expected);
expected.set_max_age(Duration::seconds(0));
assert_eq_parse!("foo=bar; Max-Age=-129", expected);
let string = format!("foo=bar; Max-Age=-{}", 1u128 << 100);
assert_eq_parse!(&string, expected);
let string = format!("foo=bar; Max-Age=-{}", i64::max_value());
assert_eq_parse!(&string, expected);
let string = format!("foo=bar; Max-Age={}", i64::max_value());
expected.set_max_age(Duration::seconds(i64::max_value()));
assert_eq_parse!(&string, expected);
}
#[test]
fn odd_characters() {
let expected = Cookie::new("foo", "b%2Fr");
@ -403,10 +540,11 @@ mod tests {
#[test]
fn do_not_panic_on_large_max_ages() {
let max_seconds = Duration::max_value().num_seconds();
let max_seconds = Duration::MAX.whole_seconds();
let expected = Cookie::build("foo", "bar")
.max_age(Duration::seconds(max_seconds))
.finish();
assert_eq_parse!(format!(" foo=bar; Max-Age={:?}", max_seconds + 1), expected);
let too_many_seconds = (max_seconds as u64) + 1;
assert_eq_parse!(format!(" foo=bar; Max-Age={:?}", too_many_seconds), expected);
}
}

164
third_party/rust/cookie/src/secure/key.rs поставляемый
Просмотреть файл

@ -1,31 +1,68 @@
use secure::ring::hkdf::expand;
use secure::ring::digest::{SHA256, Algorithm};
use secure::ring::hmac::SigningKey;
use secure::ring::rand::{SecureRandom, SystemRandom};
const SIGNING_KEY_LEN: usize = 32;
const ENCRYPTION_KEY_LEN: usize = 32;
const COMBINED_KEY_LENGTH: usize = SIGNING_KEY_LEN + ENCRYPTION_KEY_LEN;
use secure::private::KEY_LEN as PRIVATE_KEY_LEN;
use secure::signed::KEY_LEN as SIGNED_KEY_LEN;
static HKDF_DIGEST: &'static Algorithm = &SHA256;
const KEYS_INFO: &'static str = "COOKIE;SIGNED:HMAC-SHA256;PRIVATE:AEAD-AES-256-GCM";
// Statically ensure the numbers above are in-sync.
#[cfg(feature = "signed")]
const_assert!(crate::secure::signed::KEY_LEN == SIGNING_KEY_LEN);
#[cfg(feature = "private")]
const_assert!(crate::secure::private::KEY_LEN == ENCRYPTION_KEY_LEN);
/// A cryptographic master key for use with `Signed` and/or `Private` jars.
///
/// This structure encapsulates secure, cryptographic keys for use with both
/// [PrivateJar](struct.PrivateJar.html) and [SignedJar](struct.SignedJar.html).
/// It can be derived from a single master key via
/// [from_master](#method.from_master) or generated from a secure random source
/// via [generate](#method.generate). A single instance of `Key` can be used for
/// both a `PrivateJar` and a `SignedJar`.
///
/// This type is only available when the `secure` feature is enabled.
/// [`PrivateJar`](crate::PrivateJar) and [`SignedJar`](crate::SignedJar). A
/// single instance of a `Key` can be used for both a `PrivateJar` and a
/// `SignedJar` simultaneously with no notable security implications.
#[cfg_attr(all(nightly, doc), doc(cfg(any(feature = "private", feature = "signed"))))]
#[derive(Clone)]
pub struct Key {
signing_key: [u8; SIGNED_KEY_LEN],
encryption_key: [u8; PRIVATE_KEY_LEN]
pub struct Key([u8; COMBINED_KEY_LENGTH /* SIGNING | ENCRYPTION */]);
impl PartialEq for Key {
fn eq(&self, other: &Self) -> bool {
use subtle::ConstantTimeEq;
self.0.ct_eq(&other.0).into()
}
}
impl Key {
// An empty key structure, to be filled.
const fn zero() -> Self {
Key([0; COMBINED_KEY_LENGTH])
}
/// Creates a new `Key` from a 512-bit cryptographically random string.
///
/// The supplied key must be at least 512-bits (64 bytes). For security, the
/// master key _must_ be cryptographically random.
///
/// # Panics
///
/// Panics if `key` is less than 64 bytes in length.
///
/// # Example
///
/// ```rust
/// use cookie::Key;
///
/// # /*
/// let key = { /* a cryptographically random key >= 64 bytes */ };
/// # */
/// # let key: &Vec<u8> = &(0..64).collect();
///
/// let key = Key::from(key);
/// ```
pub fn from(key: &[u8]) -> Key {
if key.len() < 64 {
panic!("bad key length: expected >= 64 bytes, found {}", key.len());
}
let mut output = Key::zero();
output.0.copy_from_slice(&key[..COMBINED_KEY_LENGTH]);
output
}
/// Derives new signing/encryption keys from a master key.
///
/// The master key must be at least 256-bits (32 bytes). For security, the
@ -46,28 +83,21 @@ impl Key {
/// # */
/// # let master_key: &Vec<u8> = &(0..32).collect();
///
/// let key = Key::from_master(master_key);
/// let key = Key::derive_from(master_key);
/// ```
pub fn from_master(key: &[u8]) -> Key {
if key.len() < 32 {
panic!("bad master key length: expected at least 32 bytes, found {}", key.len());
#[cfg(feature = "key-expansion")]
#[cfg_attr(all(nightly, doc), doc(cfg(feature = "key-expansion")))]
pub fn derive_from(master_key: &[u8]) -> Self {
if master_key.len() < 32 {
panic!("bad master key length: expected >= 32 bytes, found {}", master_key.len());
}
// Expand the user's key into two.
let prk = SigningKey::new(HKDF_DIGEST, key);
let mut both_keys = [0; SIGNED_KEY_LEN + PRIVATE_KEY_LEN];
expand(&prk, KEYS_INFO.as_bytes(), &mut both_keys);
// Copy the keys into their respective arrays.
let mut signing_key = [0; SIGNED_KEY_LEN];
let mut encryption_key = [0; PRIVATE_KEY_LEN];
signing_key.copy_from_slice(&both_keys[..SIGNED_KEY_LEN]);
encryption_key.copy_from_slice(&both_keys[SIGNED_KEY_LEN..]);
Key {
signing_key: signing_key,
encryption_key: encryption_key
}
// Expand the master key into two HKDF generated keys.
const KEYS_INFO: &[u8] = b"COOKIE;SIGNED:HMAC-SHA256;PRIVATE:AEAD-AES-256-GCM";
let mut both_keys = [0; COMBINED_KEY_LENGTH];
let hk = hkdf::Hkdf::<sha2::Sha256>::from_prk(master_key).expect("key length prechecked");
hk.expand(KEYS_INFO, &mut both_keys).expect("expand into keys");
Key::from(&both_keys)
}
/// Generates signing/encryption keys from a secure, random source. Keys are
@ -76,7 +106,7 @@ impl Key {
/// # Panics
///
/// Panics if randomness cannot be retrieved from the operating system. See
/// [try_generate](#method.try_generate) for a non-panicking version.
/// [`Key::try_generate()`] for a non-panicking version.
///
/// # Example
///
@ -101,18 +131,16 @@ impl Key {
/// let key = Key::try_generate();
/// ```
pub fn try_generate() -> Option<Key> {
let mut sign_key = [0; SIGNED_KEY_LEN];
let mut enc_key = [0; PRIVATE_KEY_LEN];
use crate::secure::rand::RngCore;
let rng = SystemRandom::new();
if rng.fill(&mut sign_key).is_err() || rng.fill(&mut enc_key).is_err() {
return None
}
Some(Key { signing_key: sign_key, encryption_key: enc_key })
let mut rng = crate::secure::rand::thread_rng();
let mut key = Key::zero();
rng.try_fill_bytes(&mut key.0).ok()?;
Some(key)
}
/// Returns the raw bytes of a key suitable for signing cookies.
/// Returns the raw bytes of a key suitable for signing cookies. Guaranteed
/// to be at least 32 bytes.
///
/// # Example
///
@ -123,10 +151,11 @@ impl Key {
/// let signing_key = key.signing();
/// ```
pub fn signing(&self) -> &[u8] {
&self.signing_key[..]
&self.0[..SIGNING_KEY_LEN]
}
/// Returns the raw bytes of a key suitable for encrypting cookies.
/// Guaranteed to be at least 32 bytes.
///
/// # Example
///
@ -137,7 +166,22 @@ impl Key {
/// let encryption_key = key.encryption();
/// ```
pub fn encryption(&self) -> &[u8] {
&self.encryption_key[..]
&self.0[SIGNING_KEY_LEN..]
}
/// Returns the raw bytes of the master key. Guaranteed to be at least 64
/// bytes.
///
/// # Example
///
/// ```rust
/// use cookie::Key;
///
/// let key = Key::generate();
/// let master_key = key.master();
/// ```
pub fn master(&self) -> &[u8] {
&self.0
}
}
@ -146,18 +190,30 @@ mod test {
use super::Key;
#[test]
fn deterministic_from_master() {
fn from_works() {
let key = Key::from(&(0..64).collect::<Vec<_>>());
let signing: Vec<u8> = (0..32).collect();
assert_eq!(key.signing(), &*signing);
let encryption: Vec<u8> = (32..64).collect();
assert_eq!(key.encryption(), &*encryption);
}
#[test]
#[cfg(feature = "key-expansion")]
fn deterministic_derive() {
let master_key: Vec<u8> = (0..32).collect();
let key_a = Key::from_master(&master_key);
let key_b = Key::from_master(&master_key);
let key_a = Key::derive_from(&master_key);
let key_b = Key::derive_from(&master_key);
assert_eq!(key_a.signing(), key_b.signing());
assert_eq!(key_a.encryption(), key_b.encryption());
assert_ne!(key_a.encryption(), key_a.signing());
let master_key_2: Vec<u8> = (32..64).collect();
let key_2 = Key::from_master(&master_key_2);
let key_2 = Key::derive_from(&master_key_2);
assert_ne!(key_2.signing(), key_a.signing());
assert_ne!(key_2.encryption(), key_a.encryption());

Просмотреть файл

@ -39,3 +39,11 @@ macro_rules! assert_secure_behaviour {
})
}
// This is courtesty of `static_assertions`. That library is Copyright (c) 2017
// Nikolai Vazquez. See https://github.com/nvzqz/static-assertions-rs for more.
macro_rules! const_assert {
($x:expr $(,)?) => {
#[allow(unknown_lints, clippy::eq_op)]
const _: [(); 0 - !{ const ASSERT: bool = $x; ASSERT } as usize] = [];
};
}

12
third_party/rust/cookie/src/secure/mod.rs поставляемый
Просмотреть файл

@ -1,12 +1,14 @@
extern crate ring;
extern crate rand;
extern crate base64;
#[macro_use]
mod macros;
mod private;
mod signed;
mod key;
pub use self::private::*;
pub use self::signed::*;
pub use self::key::*;
#[cfg(feature = "private")] mod private;
#[cfg(feature = "private")] pub use self::private::*;
#[cfg(feature = "signed")] mod signed;
#[cfg(feature = "signed")] pub use self::signed::*;

227
third_party/rust/cookie/src/secure/private.rs поставляемый
Просмотреть файл

@ -1,15 +1,20 @@
use secure::ring::aead::{seal_in_place, open_in_place, Aad, Algorithm, Nonce, AES_256_GCM};
use secure::ring::aead::{OpeningKey, SealingKey};
use secure::ring::rand::{SecureRandom, SystemRandom};
use secure::{base64, Key};
extern crate aes_gcm;
use {Cookie, CookieJar};
use std::convert::TryInto;
use std::borrow::{Borrow, BorrowMut};
use crate::secure::{base64, rand, Key};
use crate::{Cookie, CookieJar};
use self::aes_gcm::Aes256Gcm;
use self::aes_gcm::aead::{Aead, AeadInPlace, NewAead, generic_array::GenericArray, Payload};
use self::rand::RngCore;
// Keep these in sync, and keep the key len synced with the `private` docs as
// well as the `KEYS_INFO` const in secure::Key.
static ALGO: &'static Algorithm = &AES_256_GCM;
const NONCE_LEN: usize = 12;
pub const KEY_LEN: usize = 32;
pub(crate) const NONCE_LEN: usize = 12;
pub(crate) const TAG_LEN: usize = 16;
pub(crate) const KEY_LEN: usize = 32;
/// A child cookie jar that provides authenticated encryption for its cookies.
///
@ -18,22 +23,49 @@ pub const KEY_LEN: usize = 32;
/// `PrivateJar` are simultaneously assured confidentiality, integrity, and
/// authenticity. In other words, clients cannot discover nor tamper with the
/// contents of a cookie, nor can they fabricate cookie data.
///
/// This type is only available when the `secure` feature is enabled.
pub struct PrivateJar<'a> {
parent: &'a mut CookieJar,
#[cfg_attr(all(nightly, doc), doc(cfg(feature = "private")))]
pub struct PrivateJar<J> {
parent: J,
key: [u8; KEY_LEN]
}
impl<'a> PrivateJar<'a> {
impl<J> PrivateJar<J> {
/// Creates a new child `PrivateJar` with parent `parent` and key `key`.
/// This method is typically called indirectly via the `signed` method of
/// `CookieJar`.
#[doc(hidden)]
pub fn new(parent: &'a mut CookieJar, key: &Key) -> PrivateJar<'a> {
let mut key_array = [0u8; KEY_LEN];
key_array.copy_from_slice(key.encryption());
PrivateJar { parent: parent, key: key_array }
pub(crate) fn new(parent: J, key: &Key) -> PrivateJar<J> {
PrivateJar { parent, key: key.encryption().try_into().expect("enc key len") }
}
/// Encrypts the cookie's value with authenticated encryption providing
/// confidentiality, integrity, and authenticity.
fn encrypt_cookie(&self, cookie: &mut Cookie) {
// Create a vec to hold the [nonce | cookie value | tag].
let cookie_val = cookie.value().as_bytes();
let mut data = vec![0; NONCE_LEN + cookie_val.len() + TAG_LEN];
// Split data into three: nonce, input/output, tag. Copy input.
let (nonce, in_out) = data.split_at_mut(NONCE_LEN);
let (in_out, tag) = in_out.split_at_mut(cookie_val.len());
in_out.copy_from_slice(cookie_val);
// Fill nonce piece with random data.
let mut rng = self::rand::thread_rng();
rng.try_fill_bytes(nonce).expect("couldn't random fill nonce");
let nonce = GenericArray::clone_from_slice(nonce);
// Perform the actual sealing operation, using the cookie's name as
// associated data to prevent value swapping.
let aad = cookie.name().as_bytes();
let aead = Aes256Gcm::new(GenericArray::from_slice(&self.key));
let aad_tag = aead.encrypt_in_place_detached(&nonce, aad, in_out)
.expect("encryption failure!");
// Copy the tag into the tag piece.
tag.copy_from_slice(&aad_tag);
// Base64 encode [nonce | encrypted value | tag].
cookie.set_value(base64::encode(&data));
}
/// Given a sealed value `str` and a key name `name`, where the nonce is
@ -41,24 +73,56 @@ impl<'a> PrivateJar<'a> {
/// verifies and decrypts the sealed value and returns it. If there's a
/// problem, returns an `Err` with a string describing the issue.
fn unseal(&self, name: &str, value: &str) -> Result<String, &'static str> {
let mut data = base64::decode(value).map_err(|_| "bad base64 value")?;
let data = base64::decode(value).map_err(|_| "bad base64 value")?;
if data.len() <= NONCE_LEN {
return Err("length of decoded data is <= NONCE_LEN");
}
let ad = Aad::from(name.as_bytes());
let key = OpeningKey::new(ALGO, &self.key).expect("opening key");
let (nonce, sealed) = data.split_at_mut(NONCE_LEN);
let nonce = Nonce::try_assume_unique_for_key(nonce)
.expect("invalid length of `nonce`");
let unsealed = open_in_place(&key, nonce, ad, 0, sealed)
.map_err(|_| "invalid key/nonce/value: bad seal")?;
let (nonce, cipher) = data.split_at(NONCE_LEN);
let payload = Payload { msg: cipher, aad: name.as_bytes() };
::std::str::from_utf8(unsealed)
.map(|s| s.to_string())
.map_err(|_| "bad unsealed utf8")
let aead = Aes256Gcm::new(GenericArray::from_slice(&self.key));
aead.decrypt(GenericArray::from_slice(nonce), payload)
.map_err(|_| "invalid key/nonce/value: bad seal")
.and_then(|s| String::from_utf8(s).map_err(|_| "bad unsealed utf8"))
}
/// Authenticates and decrypts `cookie`, returning the plaintext version if
/// decryption succeeds or `None` otherwise. Authenticatation and decryption
/// _always_ succeeds if `cookie` was generated by a `PrivateJar` with the
/// same key as `self`.
///
/// # Example
///
/// ```rust
/// use cookie::{CookieJar, Cookie, Key};
///
/// let key = Key::generate();
/// let mut jar = CookieJar::new();
/// assert!(jar.private(&key).get("name").is_none());
///
/// jar.private_mut(&key).add(Cookie::new("name", "value"));
/// assert_eq!(jar.private(&key).get("name").unwrap().value(), "value");
///
/// let plain = jar.get("name").cloned().unwrap();
/// assert_ne!(plain.value(), "value");
/// let decrypted = jar.private(&key).decrypt(plain).unwrap();
/// assert_eq!(decrypted.value(), "value");
///
/// let plain = Cookie::new("plaintext", "hello");
/// assert!(jar.private(&key).decrypt(plain).is_none());
/// ```
pub fn decrypt(&self, mut cookie: Cookie<'static>) -> Option<Cookie<'static>> {
if let Ok(value) = self.unseal(cookie.name(), cookie.value()) {
cookie.set_value(value);
return Some(cookie);
}
None
}
}
impl<J: Borrow<CookieJar>> PrivateJar<J> {
/// Returns a reference to the `Cookie` inside this jar with the name `name`
/// and authenticates and decrypts the cookie's value, returning a `Cookie`
/// with the decrypted value. If the cookie cannot be found, or the cookie
@ -70,25 +134,20 @@ impl<'a> PrivateJar<'a> {
/// use cookie::{CookieJar, Cookie, Key};
///
/// let key = Key::generate();
/// let mut jar = CookieJar::new();
/// let mut private_jar = jar.private(&key);
/// assert!(private_jar.get("name").is_none());
/// let jar = CookieJar::new();
/// assert!(jar.private(&key).get("name").is_none());
///
/// let mut jar = jar;
/// let mut private_jar = jar.private_mut(&key);
/// private_jar.add(Cookie::new("name", "value"));
/// assert_eq!(private_jar.get("name").unwrap().value(), "value");
/// ```
pub fn get(&self, name: &str) -> Option<Cookie<'static>> {
if let Some(cookie_ref) = self.parent.get(name) {
let mut cookie = cookie_ref.clone();
if let Ok(value) = self.unseal(name, cookie.value()) {
cookie.set_value(value);
return Some(cookie);
}
}
None
self.parent.borrow().get(name).and_then(|c| self.decrypt(c.clone()))
}
}
impl<J: BorrowMut<CookieJar>> PrivateJar<J> {
/// Adds `cookie` to the parent jar. The cookie's value is encrypted with
/// authenticated encryption assuring confidentiality, integrity, and
/// authenticity.
@ -100,24 +159,22 @@ impl<'a> PrivateJar<'a> {
///
/// let key = Key::generate();
/// let mut jar = CookieJar::new();
/// jar.private(&key).add(Cookie::new("name", "value"));
/// jar.private_mut(&key).add(Cookie::new("name", "value"));
///
/// assert_ne!(jar.get("name").unwrap().value(), "value");
/// assert_eq!(jar.private(&key).get("name").unwrap().value(), "value");
/// ```
pub fn add(&mut self, mut cookie: Cookie<'static>) {
self.encrypt_cookie(&mut cookie);
// Add the sealed cookie to the parent.
self.parent.add(cookie);
self.parent.borrow_mut().add(cookie);
}
/// Adds an "original" `cookie` to parent jar. The cookie's value is
/// encrypted with authenticated encryption assuring confidentiality,
/// integrity, and authenticity. Adding an original cookie does not affect
/// the [`CookieJar::delta()`](struct.CookieJar.html#method.delta)
/// computation. This method is intended to be used to seed the cookie jar
/// with cookies received from a client's HTTP message.
/// the [`CookieJar::delta()`] computation. This method is intended to be
/// used to seed the cookie jar with cookies received from a client's HTTP
/// message.
///
/// For accurate `delta` computations, this method should not be called
/// after calling `remove`.
@ -129,48 +186,14 @@ impl<'a> PrivateJar<'a> {
///
/// let key = Key::generate();
/// let mut jar = CookieJar::new();
/// jar.private(&key).add_original(Cookie::new("name", "value"));
/// jar.private_mut(&key).add_original(Cookie::new("name", "value"));
///
/// assert_eq!(jar.iter().count(), 1);
/// assert_eq!(jar.delta().count(), 0);
/// ```
pub fn add_original(&mut self, mut cookie: Cookie<'static>) {
self.encrypt_cookie(&mut cookie);
// Add the sealed cookie to the parent.
self.parent.add_original(cookie);
}
/// Encrypts the cookie's value with
/// authenticated encryption assuring confidentiality, integrity, and authenticity.
fn encrypt_cookie(&self, cookie: &mut Cookie) {
let mut data;
let output_len = {
// Create the `SealingKey` structure.
let key = SealingKey::new(ALGO, &self.key).expect("sealing key creation");
// Create a vec to hold the [nonce | cookie value | overhead].
let overhead = ALGO.tag_len();
let cookie_val = cookie.value().as_bytes();
data = vec![0; NONCE_LEN + cookie_val.len() + overhead];
// Randomly generate the nonce, then copy the cookie value as input.
let (nonce, in_out) = data.split_at_mut(NONCE_LEN);
SystemRandom::new().fill(nonce).expect("couldn't random fill nonce");
in_out[..cookie_val.len()].copy_from_slice(cookie_val);
let nonce = Nonce::try_assume_unique_for_key(nonce)
.expect("invalid length of `nonce`");
// Use cookie's name as associated data to prevent value swapping.
let ad = Aad::from(cookie.name().as_bytes());
// Perform the actual sealing operation and get the output length.
seal_in_place(&key, nonce, ad, in_out, overhead).expect("in-place seal")
};
// Base64 encode the nonce and encrypted value.
let sealed_value = base64::encode(&data[..(NONCE_LEN + output_len)]);
cookie.set_value(sealed_value);
self.parent.borrow_mut().add_original(cookie);
}
/// Removes `cookie` from the parent jar.
@ -178,8 +201,8 @@ impl<'a> PrivateJar<'a> {
/// For correct removal, the passed in `cookie` must contain the same `path`
/// and `domain` as the cookie that was initially set.
///
/// See [CookieJar::remove](struct.CookieJar.html#method.remove) for more
/// details.
/// This is identical to [`CookieJar::remove()`]. See the method's
/// documentation for more details.
///
/// # Example
///
@ -188,7 +211,7 @@ impl<'a> PrivateJar<'a> {
///
/// let key = Key::generate();
/// let mut jar = CookieJar::new();
/// let mut private_jar = jar.private(&key);
/// let mut private_jar = jar.private_mut(&key);
///
/// private_jar.add(Cookie::new("name", "value"));
/// assert!(private_jar.get("name").is_some());
@ -197,25 +220,45 @@ impl<'a> PrivateJar<'a> {
/// assert!(private_jar.get("name").is_none());
/// ```
pub fn remove(&mut self, cookie: Cookie<'static>) {
self.parent.remove(cookie);
self.parent.borrow_mut().remove(cookie);
}
}
#[cfg(test)]
mod test {
use {CookieJar, Cookie, Key};
use crate::{CookieJar, Cookie, Key};
#[test]
fn simple() {
let key = Key::generate();
let mut jar = CookieJar::new();
assert_simple_behaviour!(jar, jar.private(&key));
assert_simple_behaviour!(jar, jar.private_mut(&key));
}
#[test]
fn private() {
fn secure() {
let key = Key::generate();
let mut jar = CookieJar::new();
assert_secure_behaviour!(jar, jar.private(&key));
assert_secure_behaviour!(jar, jar.private_mut(&key));
}
#[test]
fn roundtrip() {
// Secret is SHA-256 hash of 'Super secret!' passed through HKDF-SHA256.
let key = Key::from(&[89, 202, 200, 125, 230, 90, 197, 245, 166, 249,
34, 169, 135, 31, 20, 197, 94, 154, 254, 79, 60, 26, 8, 143, 254,
24, 116, 138, 92, 225, 159, 60, 157, 41, 135, 129, 31, 226, 196, 16,
198, 168, 134, 4, 42, 1, 196, 24, 57, 103, 241, 147, 201, 185, 233,
10, 180, 170, 187, 89, 252, 137, 110, 107]);
let mut jar = CookieJar::new();
jar.add(Cookie::new("encrypted_with_ring014",
"lObeZJorGVyeSWUA8khTO/8UCzFVBY9g0MGU6/J3NN1R5x11dn2JIA=="));
jar.add(Cookie::new("encrypted_with_ring016",
"SU1ujceILyMBg3fReqRmA9HUtAIoSPZceOM/CUpObROHEujXIjonkA=="));
let private = jar.private(&key);
assert_eq!(private.get("encrypted_with_ring014").unwrap().value(), "Tamper-proof");
assert_eq!(private.get("encrypted_with_ring016").unwrap().value(), "Tamper-proof");
}
}

189
third_party/rust/cookie/src/secure/signed.rs поставляемый
Просмотреть файл

@ -1,54 +1,106 @@
use secure::ring::digest::{SHA256, Algorithm};
use secure::ring::hmac::{SigningKey, sign, verify_with_own_key as verify};
use secure::{base64, Key};
use std::convert::TryInto;
use std::borrow::{Borrow, BorrowMut};
use {Cookie, CookieJar};
use sha2::Sha256;
use hmac::{Hmac, Mac};
use crate::secure::{base64, Key};
use crate::{Cookie, CookieJar};
// Keep these in sync, and keep the key len synced with the `signed` docs as
// well as the `KEYS_INFO` const in secure::Key.
static HMAC_DIGEST: &'static Algorithm = &SHA256;
const BASE64_DIGEST_LEN: usize = 44;
pub const KEY_LEN: usize = 32;
pub(crate) const BASE64_DIGEST_LEN: usize = 44;
pub(crate) const KEY_LEN: usize = 32;
/// A child cookie jar that authenticates its cookies.
///
/// A _signed_ child jar signs all the cookies added to it and verifies cookies
/// retrieved from it. Any cookies stored in a `SignedJar` are assured integrity
/// and authenticity. In other words, clients cannot tamper with the contents of
/// a cookie nor can they fabricate cookie values, but the data is visible in
/// plaintext.
///
/// This type is only available when the `secure` feature is enabled.
pub struct SignedJar<'a> {
parent: &'a mut CookieJar,
key: SigningKey
/// retrieved from it. Any cookies stored in a `SignedJar` are provided
/// integrity and authenticity. In other words, clients cannot tamper with the
/// contents of a cookie nor can they fabricate cookie values, but the data is
/// visible in plaintext.
#[cfg_attr(all(nightly, doc), doc(cfg(feature = "signed")))]
pub struct SignedJar<J> {
parent: J,
key: [u8; KEY_LEN],
}
impl<'a> SignedJar<'a> {
impl<J> SignedJar<J> {
/// Creates a new child `SignedJar` with parent `parent` and key `key`. This
/// method is typically called indirectly via the `signed` method of
/// method is typically called indirectly via the `signed{_mut}` methods of
/// `CookieJar`.
#[doc(hidden)]
pub fn new(parent: &'a mut CookieJar, key: &Key) -> SignedJar<'a> {
SignedJar { parent: parent, key: SigningKey::new(HMAC_DIGEST, key.signing()) }
pub(crate) fn new(parent: J, key: &Key) -> SignedJar<J> {
SignedJar { parent, key: key.signing().try_into().expect("sign key len") }
}
/// Signs the cookie's value providing integrity and authenticity.
fn sign_cookie(&self, cookie: &mut Cookie) {
// Compute HMAC-SHA256 of the cookie's value.
let mut mac = Hmac::<Sha256>::new_from_slice(&self.key).expect("good key");
mac.update(cookie.value().as_bytes());
// Cookie's new value is [MAC | original-value].
let mut new_value = base64::encode(&mac.finalize().into_bytes());
new_value.push_str(cookie.value());
cookie.set_value(new_value);
}
/// Given a signed value `str` where the signature is prepended to `value`,
/// verifies the signed value and returns it. If there's a problem, returns
/// an `Err` with a string describing the issue.
fn verify(&self, cookie_value: &str) -> Result<String, &'static str> {
if cookie_value.len() < BASE64_DIGEST_LEN {
return Err("length of value is <= BASE64_DIGEST_LEN");
fn _verify(&self, cookie_value: &str) -> Result<String, &'static str> {
if !cookie_value.is_char_boundary(BASE64_DIGEST_LEN) {
return Err("missing or invalid digest");
}
// Split [MAC | original-value] into its two parts.
let (digest_str, value) = cookie_value.split_at(BASE64_DIGEST_LEN);
let sig = base64::decode(digest_str).map_err(|_| "bad base64 digest")?;
let digest = base64::decode(digest_str).map_err(|_| "bad base64 digest")?;
verify(&self.key, value.as_bytes(), &sig)
// Perform the verification.
let mut mac = Hmac::<Sha256>::new_from_slice(&self.key).expect("good key");
mac.update(value.as_bytes());
mac.verify_slice(&digest)
.map(|_| value.to_string())
.map_err(|_| "value did not verify")
}
/// Verifies the authenticity and integrity of `cookie`, returning the
/// plaintext version if verification succeeds or `None` otherwise.
/// Verification _always_ succeeds if `cookie` was generated by a
/// `SignedJar` with the same key as `self`.
///
/// # Example
///
/// ```rust
/// use cookie::{CookieJar, Cookie, Key};
///
/// let key = Key::generate();
/// let mut jar = CookieJar::new();
/// assert!(jar.signed(&key).get("name").is_none());
///
/// jar.signed_mut(&key).add(Cookie::new("name", "value"));
/// assert_eq!(jar.signed(&key).get("name").unwrap().value(), "value");
///
/// let plain = jar.get("name").cloned().unwrap();
/// assert_ne!(plain.value(), "value");
/// let verified = jar.signed(&key).verify(plain).unwrap();
/// assert_eq!(verified.value(), "value");
///
/// let plain = Cookie::new("plaintext", "hello");
/// assert!(jar.signed(&key).verify(plain).is_none());
/// ```
pub fn verify(&self, mut cookie: Cookie<'static>) -> Option<Cookie<'static>> {
if let Ok(value) = self._verify(cookie.value()) {
cookie.set_value(value);
return Some(cookie);
}
None
}
}
impl<J: Borrow<CookieJar>> SignedJar<J> {
/// Returns a reference to the `Cookie` inside this jar with the name `name`
/// and verifies the authenticity and integrity of the cookie's value,
/// returning a `Cookie` with the authenticated value. If the cookie cannot
@ -60,25 +112,20 @@ impl<'a> SignedJar<'a> {
/// use cookie::{CookieJar, Cookie, Key};
///
/// let key = Key::generate();
/// let mut jar = CookieJar::new();
/// let mut signed_jar = jar.signed(&key);
/// assert!(signed_jar.get("name").is_none());
/// let jar = CookieJar::new();
/// assert!(jar.signed(&key).get("name").is_none());
///
/// let mut jar = jar;
/// let mut signed_jar = jar.signed_mut(&key);
/// signed_jar.add(Cookie::new("name", "value"));
/// assert_eq!(signed_jar.get("name").unwrap().value(), "value");
/// ```
pub fn get(&self, name: &str) -> Option<Cookie<'static>> {
if let Some(cookie_ref) = self.parent.get(name) {
let mut cookie = cookie_ref.clone();
if let Ok(value) = self.verify(cookie.value()) {
cookie.set_value(value);
return Some(cookie);
}
}
None
self.parent.borrow().get(name).and_then(|c| self.verify(c.clone()))
}
}
impl<J: BorrowMut<CookieJar>> SignedJar<J> {
/// Adds `cookie` to the parent jar. The cookie's value is signed assuring
/// integrity and authenticity.
///
@ -89,7 +136,7 @@ impl<'a> SignedJar<'a> {
///
/// let key = Key::generate();
/// let mut jar = CookieJar::new();
/// jar.signed(&key).add(Cookie::new("name", "value"));
/// jar.signed_mut(&key).add(Cookie::new("name", "value"));
///
/// assert_ne!(jar.get("name").unwrap().value(), "value");
/// assert!(jar.get("name").unwrap().value().contains("value"));
@ -97,14 +144,14 @@ impl<'a> SignedJar<'a> {
/// ```
pub fn add(&mut self, mut cookie: Cookie<'static>) {
self.sign_cookie(&mut cookie);
self.parent.add(cookie);
self.parent.borrow_mut().add(cookie);
}
/// Adds an "original" `cookie` to this jar. The cookie's value is signed
/// assuring integrity and authenticity. Adding an original cookie does not
/// affect the [`CookieJar::delta()`](struct.CookieJar.html#method.delta)
/// computation. This method is intended to be used to seed the cookie jar
/// with cookies received from a client's HTTP message.
/// affect the [`CookieJar::delta()`] computation. This method is intended
/// to be used to seed the cookie jar with cookies received from a client's
/// HTTP message.
///
/// For accurate `delta` computations, this method should not be called
/// after calling `remove`.
@ -116,22 +163,14 @@ impl<'a> SignedJar<'a> {
///
/// let key = Key::generate();
/// let mut jar = CookieJar::new();
/// jar.signed(&key).add_original(Cookie::new("name", "value"));
/// jar.signed_mut(&key).add_original(Cookie::new("name", "value"));
///
/// assert_eq!(jar.iter().count(), 1);
/// assert_eq!(jar.delta().count(), 0);
/// ```
pub fn add_original(&mut self, mut cookie: Cookie<'static>) {
self.sign_cookie(&mut cookie);
self.parent.add_original(cookie);
}
/// Signs the cookie's value assuring integrity and authenticity.
fn sign_cookie(&self, cookie: &mut Cookie) {
let digest = sign(&self.key, cookie.value().as_bytes());
let mut new_value = base64::encode(digest.as_ref());
new_value.push_str(cookie.value());
cookie.set_value(new_value);
self.parent.borrow_mut().add_original(cookie);
}
/// Removes `cookie` from the parent jar.
@ -139,8 +178,8 @@ impl<'a> SignedJar<'a> {
/// For correct removal, the passed in `cookie` must contain the same `path`
/// and `domain` as the cookie that was initially set.
///
/// See [CookieJar::remove](struct.CookieJar.html#method.remove) for more
/// details.
/// This is identical to [`CookieJar::remove()`]. See the method's
/// documentation for more details.
///
/// # Example
///
@ -149,7 +188,7 @@ impl<'a> SignedJar<'a> {
///
/// let key = Key::generate();
/// let mut jar = CookieJar::new();
/// let mut signed_jar = jar.signed(&key);
/// let mut signed_jar = jar.signed_mut(&key);
///
/// signed_jar.add(Cookie::new("name", "value"));
/// assert!(signed_jar.get("name").is_some());
@ -158,25 +197,55 @@ impl<'a> SignedJar<'a> {
/// assert!(signed_jar.get("name").is_none());
/// ```
pub fn remove(&mut self, cookie: Cookie<'static>) {
self.parent.remove(cookie);
self.parent.borrow_mut().remove(cookie);
}
}
#[cfg(test)]
mod test {
use {CookieJar, Cookie, Key};
use crate::{CookieJar, Cookie, Key};
#[test]
fn simple() {
let key = Key::generate();
let mut jar = CookieJar::new();
assert_simple_behaviour!(jar, jar.signed(&key));
assert_simple_behaviour!(jar, jar.signed_mut(&key));
}
#[test]
fn private() {
let key = Key::generate();
let mut jar = CookieJar::new();
assert_secure_behaviour!(jar, jar.signed(&key));
assert_secure_behaviour!(jar, jar.signed_mut(&key));
}
#[test]
fn roundtrip() {
// Secret is SHA-256 hash of 'Super secret!' passed through HKDF-SHA256.
let key = Key::from(&[89, 202, 200, 125, 230, 90, 197, 245, 166, 249,
34, 169, 135, 31, 20, 197, 94, 154, 254, 79, 60, 26, 8, 143, 254,
24, 116, 138, 92, 225, 159, 60, 157, 41, 135, 129, 31, 226, 196, 16,
198, 168, 134, 4, 42, 1, 196, 24, 57, 103, 241, 147, 201, 185, 233,
10, 180, 170, 187, 89, 252, 137, 110, 107]);
let mut jar = CookieJar::new();
jar.add(Cookie::new("signed_with_ring014",
"3tdHXEQ2kf6fxC7dWzBGmpSLMtJenXLKrZ9cHkSsl1w=Tamper-proof"));
jar.add(Cookie::new("signed_with_ring016",
"3tdHXEQ2kf6fxC7dWzBGmpSLMtJenXLKrZ9cHkSsl1w=Tamper-proof"));
let signed = jar.signed(&key);
assert_eq!(signed.get("signed_with_ring014").unwrap().value(), "Tamper-proof");
assert_eq!(signed.get("signed_with_ring016").unwrap().value(), "Tamper-proof");
}
#[test]
fn issue_178() {
let data = "x=yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy£";
let c = Cookie::parse(data).expect("failed to parse cookie");
let key = Key::from(&[0u8; 64]);
let mut jar = CookieJar::new();
let signed = jar.signed_mut(&key);
assert!(signed.verify(c).is_none());
}
}

1
third_party/rust/num_threads/.cargo-checksum.json поставляемый Normal file
Просмотреть файл

@ -0,0 +1 @@
{"files":{"Cargo.toml":"ed1dbfb8f9eb836549858fb0ca704eddaa480cbe576e8dd50ebca1529dca21b3","LICENSE-Apache":"c69b72788ec261765e460532016b4bb78372f238e449b5e70e3268ec1e18fd15","LICENSE-MIT":"b4bf94a9fceb8846320fda938ee53fc16b506572609da5cf1d2d289a5597a8f8","src/apple.rs":"018e729ea67e8e17428d2e8d93f28ed0f7e7c506fcf78b56f56113235ce7dfcf","src/freebsd.rs":"683636294a62d6b958a5de800b52ddfea609234921e0583906272aacc71e18e5","src/imp.rs":"8cc9d07f05b0aa70e9997648abce7f79bd758c6fc76040d1c8f7beb7bf551e9d","src/lib.rs":"c53382612069b9552846414d1508cbb1401c500a4f34e21addd336001ebd8b7e","src/linux.rs":"67e02ecd105b8a421227bf72814e8388a4f77df1d8a44b8902bc8926f7e6698c"},"package":"2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44"}

36
third_party/rust/num_threads/Cargo.toml поставляемый Normal file
Просмотреть файл

@ -0,0 +1,36 @@
# 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]
name = "num_threads"
version = "0.1.6"
authors = ["Jacob Pratt <open-source@jhpratt.dev>"]
include = [
"src/**/*",
"LICENSE-*",
]
description = "A minimal library that determines the number of running threads for the current process."
categories = [
"api-bindings",
"hardware-support",
"os",
]
license = "MIT OR Apache-2.0"
repository = "https://github.com/jhpratt/num_threads"
[package.metadata.docs.rs]
all-features = true
targets = ["x86_64-unknown-linux-gnu"]
[dependencies]
[target."cfg(any(target_os = \"macos\", target_os = \"ios\", target_os = \"freebsd\"))".dependencies.libc]
version = "0.2.107"

202
third_party/rust/num_threads/LICENSE-Apache поставляемый Normal file
Просмотреть файл

@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright 2021 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.

19
third_party/rust/num_threads/LICENSE-MIT поставляемый Normal file
Просмотреть файл

@ -0,0 +1,19 @@
Copyright (c) 2021 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.

45
third_party/rust/num_threads/src/apple.rs поставляемый Normal file
Просмотреть файл

@ -0,0 +1,45 @@
extern crate libc;
use std::num::NonZeroUsize;
use self::libc::{kern_return_t, mach_msg_type_number_t, mach_port_t, thread_t};
// This constant is from
// /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/
// usr/include/mach/machine/thread_state.h.
//
// It has not been updated since Apple devices started to support 64-bit ARM (iOS), so it
// should be very stable.
const THREAD_STATE_MAX: i32 = 1296;
#[allow(non_camel_case_types)]
// https://github.com/apple/darwin-xnu/blob/a1babec6b135d1f35b2590a1990af3c5c5393479/osfmk/mach/mach_types.defs#L155
type task_inspect_t = mach_port_t;
#[allow(non_camel_case_types)]
// https://github.com/apple/darwin-xnu/blob/a1babec6b135d1f35b2590a1990af3c5c5393479/osfmk/mach/mach_types.defs#L238
type thread_array_t = [thread_t; THREAD_STATE_MAX as usize];
extern "C" {
// https://developer.apple.com/documentation/kernel/1537751-task_threads/
fn task_threads(
target_task: task_inspect_t,
act_list: *mut thread_array_t,
act_listCnt: *mut mach_msg_type_number_t,
) -> kern_return_t;
}
pub(crate) fn num_threads() -> Option<NonZeroUsize> {
// http://web.mit.edu/darwin/src/modules/xnu/osfmk/man/task_threads.html
let mut thread_state = [0u32; THREAD_STATE_MAX as usize];
let mut thread_count = 0;
// Safety: `mach_task_self` always returns a valid value, `thread_state` is large enough, and
// both it and `thread_count` are writable.
let result =
unsafe { task_threads(libc::mach_task_self(), &mut thread_state, &mut thread_count) };
if result == libc::KERN_SUCCESS {
NonZeroUsize::new(thread_count as usize)
} else {
None
}
}

36
third_party/rust/num_threads/src/freebsd.rs поставляемый Normal file
Просмотреть файл

@ -0,0 +1,36 @@
extern crate libc;
use std::num::NonZeroUsize;
use std::{mem, ptr};
pub(crate) fn num_threads() -> Option<NonZeroUsize> {
// Safety: `sysctl` and `getpid` are both thread-safe.
// `kip` is only accessed if sysctl() succeeds and agrees with the expected size,
// and the data only trusted if both its embedded size and pid match expectations
unsafe {
let pid = libc::getpid();
let mib: [libc::c_int; 4] = [libc::CTL_KERN, libc::KERN_PROC, libc::KERN_PROC_PID, pid];
let mut kip: libc::kinfo_proc = mem::zeroed();
let expected_kip_len = mem::size_of_val(&kip);
let mut kip_len = expected_kip_len;
let ret = libc::sysctl(
mib.as_ptr(),
mib.len() as u32,
&mut kip as *mut _ as *mut libc::c_void,
&mut kip_len,
ptr::null(),
0,
);
if ret == 0
&& kip_len == expected_kip_len
&& kip.ki_structsize == expected_kip_len as i32
&& kip.ki_pid == pid
{
NonZeroUsize::new(kip.ki_numthreads as usize)
} else {
None
}
}
}

7
third_party/rust/num_threads/src/imp.rs поставляемый Normal file
Просмотреть файл

@ -0,0 +1,7 @@
//! Fallback if no OS matches.
use std::num::NonZeroUsize;
pub(crate) fn num_threads() -> Option<NonZeroUsize> {
None
}

64
third_party/rust/num_threads/src/lib.rs поставляемый Normal file
Просмотреть файл

@ -0,0 +1,64 @@
//! Minimum supported Rust version: 1.28
use std::num::NonZeroUsize;
#[cfg_attr(any(target_os = "linux", target_os = "android"), path = "linux.rs")]
#[cfg_attr(target_os = "freebsd", path = "freebsd.rs")]
#[cfg_attr(any(target_os = "macos", target_os = "ios"), path = "apple.rs")]
mod imp;
/// Obtain the number of threads currently part of the active process. Returns `None` if the number
/// of threads cannot be determined.
pub fn num_threads() -> Option<NonZeroUsize> {
imp::num_threads()
}
/// Determine if the current process is single-threaded. Returns `None` if the number of threads
/// cannot be determined.
pub fn is_single_threaded() -> Option<bool> {
num_threads().map(|n| n.get() == 1)
}
#[cfg(test)]
mod test {
use std::num::NonZeroUsize;
// Run each expression in its own thread.
macro_rules! threaded {
($first:expr;) => {
$first;
};
($first:expr; $($rest:expr;)*) => {
$first;
::std::thread::spawn(|| {
threaded!($($rest;)*);
})
.join()
.unwrap();
};
}
#[test]
fn num_threads() {
threaded! {
assert_eq!(super::num_threads().map(NonZeroUsize::get), Some(1));
assert_eq!(super::num_threads().map(NonZeroUsize::get), Some(2));
assert_eq!(super::num_threads().map(NonZeroUsize::get), Some(3));
assert_eq!(super::num_threads().map(NonZeroUsize::get), Some(4));
assert_eq!(super::num_threads().map(NonZeroUsize::get), Some(5));
assert_eq!(super::num_threads().map(NonZeroUsize::get), Some(6));
}
}
#[test]
fn is_single_threaded() {
threaded! {
assert_eq!(super::is_single_threaded(), Some(true));
assert_eq!(super::is_single_threaded(), Some(false));
assert_eq!(super::is_single_threaded(), Some(false));
assert_eq!(super::is_single_threaded(), Some(false));
assert_eq!(super::is_single_threaded(), Some(false));
assert_eq!(super::is_single_threaded(), Some(false));
}
}
}

14
third_party/rust/num_threads/src/linux.rs поставляемый Normal file
Просмотреть файл

@ -0,0 +1,14 @@
use std::fs;
use std::num::NonZeroUsize;
pub(crate) fn num_threads() -> Option<NonZeroUsize> {
fs::read_to_string("/proc/self/stat")
.ok()
.as_ref()
// Skip past the pid and (process name) fields
.and_then(|stat| stat.rsplit(')').next())
// 20th field, less the two we skipped
.and_then(|rstat| rstat.split_whitespace().nth(17))
.and_then(|num_threads| num_threads.parse::<usize>().ok())
.and_then(NonZeroUsize::new)
}

1
third_party/rust/time-0.1.43/.cargo-checksum.json поставляемый Normal file
Просмотреть файл

@ -0,0 +1 @@
{"files":{"Cargo.toml":"ddebccd9128093c3fdf84b4f6a131fe634c85e8fc2059afed9ea30e02fe22c58","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"6485b8ed310d3f0340bf1ad1f47645069ce4069dcc6bb46c7d5c6faf41de1fdb","README.md":"35b591c7481ec3ae59210fa4f9b7cecb1b16e632bfd6193b032239e74f9bfdb8","src/display.rs":"52d16abaa37b3ab577747c7d9d2ed6ded1b126458e980dc3e1a571fa6e1f9fda","src/duration.rs":"c706d392bdb7f65b23fcc20189a9a77c50b765b9c548e247238424ed6fb56a46","src/lib.rs":"720e380829cda276466bce34d6a29a2f7aec1f750e4b4522b217c57930380545","src/parse.rs":"65bd9142d8c15eb54a8d4db6e2c48bf1adbcc875953141c17e07ba58f356a027","src/sys.rs":"851994516ff29dd9f5749fa19b1db13b7afe484e4bd1d279b420fda7ed8404ab"},"package":"ca8a50ef2360fbd1eeb0ecd46795a87a19024eb4b53c5dc916ca1fd95fe62438"}

38
third_party/rust/time-0.1.43/Cargo.toml поставляемый Normal file
Просмотреть файл

@ -0,0 +1,38 @@
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
#
# When uploading crates to the registry Cargo will automatically
# "normalize" Cargo.toml files for maximal compatibility
# with all versions of Cargo and also rewrite `path` dependencies
# to registry (e.g., crates.io) dependencies
#
# If you believe there's an error in this file please file an
# issue against the rust-lang/cargo repository. If you're
# editing this file be aware that the upstream Cargo.toml
# will likely look very different (and much more reasonable)
[package]
name = "time"
version = "0.1.43"
authors = ["The Rust Project Developers"]
exclude = [".github", "benches"]
description = "Utilities for working with time-related functions in Rust.\n"
homepage = "https://github.com/time-rs/time"
documentation = "https://docs.rs/time/~0.1"
readme = "README.md"
license = "MIT/Apache-2.0"
repository = "https://github.com/time-rs/time"
[dependencies.libc]
version = "0.2.69"
[dependencies.rustc-serialize]
version = "0.3"
optional = true
[dev-dependencies.log]
version = "0.4"
[dev-dependencies.winapi]
version = "0.3.0"
features = ["std", "processthreadsapi", "winbase"]
[target."cfg(windows)".dependencies.winapi]
version = "0.3.0"
features = ["std", "minwinbase", "minwindef", "ntdef", "profileapi", "sysinfoapi", "timezoneapi"]

Просмотреть файл

25
third_party/rust/time-0.1.43/LICENSE-MIT поставляемый Normal file
Просмотреть файл

@ -0,0 +1,25 @@
Copyright (c) 2014 The Rust Project Developers
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.

31
third_party/rust/time-0.1.43/README.md поставляемый Normal file
Просмотреть файл

@ -0,0 +1,31 @@
time
====
Utilities for working with time-related functions in Rust
[![build status](https://github.com/time-rs/time/workflows/Build/badge.svg?branch=v0.1)](https://github.com/time-rs/time/actions?query=branch%3Av0.1)
[![Documentation](https://docs.rs/time/badge.svg?version=0.1)](https://docs.rs/time/~0.1)
![rustc 1.21.0](https://img.shields.io/badge/rustc-1.21.0-blue)
## time v0.1.x is Deprecated
The 0.1.x series of this library is deprecated and in maintenance mode. No new
features will be added. Active development now occurs in the 0.2.x series.
If you need additional functionality that this crate does not provide, check
out the [`chrono`](https://github.com/chronotope/chrono) crate.
## Usage
Put this in your `Cargo.toml`:
```toml
[dependencies]
time = "0.1"
```
And this in your crate root:
```rust
extern crate time;
```

Просмотреть файл

655
third_party/rust/time-0.1.43/src/duration.rs поставляемый Normal file
Просмотреть файл

@ -0,0 +1,655 @@
// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! Temporal quantification
use std::{fmt, i64};
use std::error::Error;
use std::ops::{Add, Sub, Mul, Div, Neg, FnOnce};
use std::time::Duration as StdDuration;
/// The number of nanoseconds in a microsecond.
const NANOS_PER_MICRO: i32 = 1000;
/// The number of nanoseconds in a millisecond.
const NANOS_PER_MILLI: i32 = 1000_000;
/// The number of nanoseconds in seconds.
const NANOS_PER_SEC: i32 = 1_000_000_000;
/// The number of microseconds per second.
const MICROS_PER_SEC: i64 = 1000_000;
/// The number of milliseconds per second.
const MILLIS_PER_SEC: i64 = 1000;
/// The number of seconds in a minute.
const SECS_PER_MINUTE: i64 = 60;
/// The number of seconds in an hour.
const SECS_PER_HOUR: i64 = 3600;
/// The number of (non-leap) seconds in days.
const SECS_PER_DAY: i64 = 86400;
/// The number of (non-leap) seconds in a week.
const SECS_PER_WEEK: i64 = 604800;
macro_rules! try_opt {
($e:expr) => (match $e { Some(v) => v, None => return None })
}
/// ISO 8601 time duration with nanosecond precision.
/// This also allows for the negative duration; see individual methods for details.
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
pub struct Duration {
secs: i64,
nanos: i32, // Always 0 <= nanos < NANOS_PER_SEC
}
/// The minimum possible `Duration`: `i64::MIN` milliseconds.
pub const MIN: Duration = Duration {
secs: i64::MIN / MILLIS_PER_SEC - 1,
nanos: NANOS_PER_SEC + (i64::MIN % MILLIS_PER_SEC) as i32 * NANOS_PER_MILLI
};
/// The maximum possible `Duration`: `i64::MAX` milliseconds.
pub const MAX: Duration = Duration {
secs: i64::MAX / MILLIS_PER_SEC,
nanos: (i64::MAX % MILLIS_PER_SEC) as i32 * NANOS_PER_MILLI
};
impl Duration {
/// Makes a new `Duration` with given number of weeks.
/// Equivalent to `Duration::seconds(weeks * 7 * 24 * 60 * 60)` with overflow checks.
/// Panics when the duration is out of bounds.
#[inline]
pub fn weeks(weeks: i64) -> Duration {
let secs = weeks.checked_mul(SECS_PER_WEEK).expect("Duration::weeks out of bounds");
Duration::seconds(secs)
}
/// Makes a new `Duration` with given number of days.
/// Equivalent to `Duration::seconds(days * 24 * 60 * 60)` with overflow checks.
/// Panics when the duration is out of bounds.
#[inline]
pub fn days(days: i64) -> Duration {
let secs = days.checked_mul(SECS_PER_DAY).expect("Duration::days out of bounds");
Duration::seconds(secs)
}
/// Makes a new `Duration` with given number of hours.
/// Equivalent to `Duration::seconds(hours * 60 * 60)` with overflow checks.
/// Panics when the duration is out of bounds.
#[inline]
pub fn hours(hours: i64) -> Duration {
let secs = hours.checked_mul(SECS_PER_HOUR).expect("Duration::hours out of bounds");
Duration::seconds(secs)
}
/// Makes a new `Duration` with given number of minutes.
/// Equivalent to `Duration::seconds(minutes * 60)` with overflow checks.
/// Panics when the duration is out of bounds.
#[inline]
pub fn minutes(minutes: i64) -> Duration {
let secs = minutes.checked_mul(SECS_PER_MINUTE).expect("Duration::minutes out of bounds");
Duration::seconds(secs)
}
/// Makes a new `Duration` with given number of seconds.
/// Panics when the duration is more than `i64::MAX` milliseconds
/// or less than `i64::MIN` milliseconds.
#[inline]
pub fn seconds(seconds: i64) -> Duration {
let d = Duration { secs: seconds, nanos: 0 };
if d < MIN || d > MAX {
panic!("Duration::seconds out of bounds");
}
d
}
/// Makes a new `Duration` with given number of milliseconds.
#[inline]
pub fn milliseconds(milliseconds: i64) -> Duration {
let (secs, millis) = div_mod_floor_64(milliseconds, MILLIS_PER_SEC);
let nanos = millis as i32 * NANOS_PER_MILLI;
Duration { secs: secs, nanos: nanos }
}
/// Makes a new `Duration` with given number of microseconds.
#[inline]
pub fn microseconds(microseconds: i64) -> Duration {
let (secs, micros) = div_mod_floor_64(microseconds, MICROS_PER_SEC);
let nanos = micros as i32 * NANOS_PER_MICRO;
Duration { secs: secs, nanos: nanos }
}
/// Makes a new `Duration` with given number of nanoseconds.
#[inline]
pub fn nanoseconds(nanos: i64) -> Duration {
let (secs, nanos) = div_mod_floor_64(nanos, NANOS_PER_SEC as i64);
Duration { secs: secs, nanos: nanos as i32 }
}
/// Runs a closure, returning the duration of time it took to run the
/// closure.
pub fn span<F>(f: F) -> Duration where F: FnOnce() {
let before = super::precise_time_ns();
f();
Duration::nanoseconds((super::precise_time_ns() - before) as i64)
}
/// Returns the total number of whole weeks in the duration.
#[inline]
pub fn num_weeks(&self) -> i64 {
self.num_days() / 7
}
/// Returns the total number of whole days in the duration.
pub fn num_days(&self) -> i64 {
self.num_seconds() / SECS_PER_DAY
}
/// Returns the total number of whole hours in the duration.
#[inline]
pub fn num_hours(&self) -> i64 {
self.num_seconds() / SECS_PER_HOUR
}
/// Returns the total number of whole minutes in the duration.
#[inline]
pub fn num_minutes(&self) -> i64 {
self.num_seconds() / SECS_PER_MINUTE
}
/// Returns the total number of whole seconds in the duration.
pub fn num_seconds(&self) -> i64 {
// If secs is negative, nanos should be subtracted from the duration.
if self.secs < 0 && self.nanos > 0 {
self.secs + 1
} else {
self.secs
}
}
/// Returns the number of nanoseconds such that
/// `nanos_mod_sec() + num_seconds() * NANOS_PER_SEC` is the total number of
/// nanoseconds in the duration.
fn nanos_mod_sec(&self) -> i32 {
if self.secs < 0 && self.nanos > 0 {
self.nanos - NANOS_PER_SEC
} else {
self.nanos
}
}
/// Returns the total number of whole milliseconds in the duration,
pub fn num_milliseconds(&self) -> i64 {
// A proper Duration will not overflow, because MIN and MAX are defined
// such that the range is exactly i64 milliseconds.
let secs_part = self.num_seconds() * MILLIS_PER_SEC;
let nanos_part = self.nanos_mod_sec() / NANOS_PER_MILLI;
secs_part + nanos_part as i64
}
/// Returns the total number of whole microseconds in the duration,
/// or `None` on overflow (exceeding 2<sup>63</sup> microseconds in either direction).
pub fn num_microseconds(&self) -> Option<i64> {
let secs_part = try_opt!(self.num_seconds().checked_mul(MICROS_PER_SEC));
let nanos_part = self.nanos_mod_sec() / NANOS_PER_MICRO;
secs_part.checked_add(nanos_part as i64)
}
/// Returns the total number of whole nanoseconds in the duration,
/// or `None` on overflow (exceeding 2<sup>63</sup> nanoseconds in either direction).
pub fn num_nanoseconds(&self) -> Option<i64> {
let secs_part = try_opt!(self.num_seconds().checked_mul(NANOS_PER_SEC as i64));
let nanos_part = self.nanos_mod_sec();
secs_part.checked_add(nanos_part as i64)
}
/// Add two durations, returning `None` if overflow occurred.
pub fn checked_add(&self, rhs: &Duration) -> Option<Duration> {
let mut secs = try_opt!(self.secs.checked_add(rhs.secs));
let mut nanos = self.nanos + rhs.nanos;
if nanos >= NANOS_PER_SEC {
nanos -= NANOS_PER_SEC;
secs = try_opt!(secs.checked_add(1));
}
let d = Duration { secs: secs, nanos: nanos };
// Even if d is within the bounds of i64 seconds,
// it might still overflow i64 milliseconds.
if d < MIN || d > MAX { None } else { Some(d) }
}
/// Subtract two durations, returning `None` if overflow occurred.
pub fn checked_sub(&self, rhs: &Duration) -> Option<Duration> {
let mut secs = try_opt!(self.secs.checked_sub(rhs.secs));
let mut nanos = self.nanos - rhs.nanos;
if nanos < 0 {
nanos += NANOS_PER_SEC;
secs = try_opt!(secs.checked_sub(1));
}
let d = Duration { secs: secs, nanos: nanos };
// Even if d is within the bounds of i64 seconds,
// it might still overflow i64 milliseconds.
if d < MIN || d > MAX { None } else { Some(d) }
}
/// The minimum possible `Duration`: `i64::MIN` milliseconds.
#[inline]
pub fn min_value() -> Duration { MIN }
/// The maximum possible `Duration`: `i64::MAX` milliseconds.
#[inline]
pub fn max_value() -> Duration { MAX }
/// A duration where the stored seconds and nanoseconds are equal to zero.
#[inline]
pub fn zero() -> Duration {
Duration { secs: 0, nanos: 0 }
}
/// Returns `true` if the duration equals `Duration::zero()`.
#[inline]
pub fn is_zero(&self) -> bool {
self.secs == 0 && self.nanos == 0
}
/// Creates a `time::Duration` object from `std::time::Duration`
///
/// This function errors when original duration is larger than the maximum
/// value supported for this type.
pub fn from_std(duration: StdDuration) -> Result<Duration, OutOfRangeError> {
// We need to check secs as u64 before coercing to i64
if duration.as_secs() > MAX.secs as u64 {
return Err(OutOfRangeError(()));
}
let d = Duration {
secs: duration.as_secs() as i64,
nanos: duration.subsec_nanos() as i32,
};
if d > MAX {
return Err(OutOfRangeError(()));
}
Ok(d)
}
/// Creates a `std::time::Duration` object from `time::Duration`
///
/// This function errors when duration is less than zero. As standard
/// library implementation is limited to non-negative values.
pub fn to_std(&self) -> Result<StdDuration, OutOfRangeError> {
if self.secs < 0 {
return Err(OutOfRangeError(()));
}
Ok(StdDuration::new(self.secs as u64, self.nanos as u32))
}
/// Returns the raw value of duration.
#[cfg(target_env = "sgx")]
pub(crate) fn raw(&self) -> (i64, i32) {
(self.secs, self.nanos)
}
}
impl Neg for Duration {
type Output = Duration;
#[inline]
fn neg(self) -> Duration {
if self.nanos == 0 {
Duration { secs: -self.secs, nanos: 0 }
} else {
Duration { secs: -self.secs - 1, nanos: NANOS_PER_SEC - self.nanos }
}
}
}
impl Add for Duration {
type Output = Duration;
fn add(self, rhs: Duration) -> Duration {
let mut secs = self.secs + rhs.secs;
let mut nanos = self.nanos + rhs.nanos;
if nanos >= NANOS_PER_SEC {
nanos -= NANOS_PER_SEC;
secs += 1;
}
Duration { secs: secs, nanos: nanos }
}
}
impl Sub for Duration {
type Output = Duration;
fn sub(self, rhs: Duration) -> Duration {
let mut secs = self.secs - rhs.secs;
let mut nanos = self.nanos - rhs.nanos;
if nanos < 0 {
nanos += NANOS_PER_SEC;
secs -= 1;
}
Duration { secs: secs, nanos: nanos }
}
}
impl Mul<i32> for Duration {
type Output = Duration;
fn mul(self, rhs: i32) -> Duration {
// Multiply nanoseconds as i64, because it cannot overflow that way.
let total_nanos = self.nanos as i64 * rhs as i64;
let (extra_secs, nanos) = div_mod_floor_64(total_nanos, NANOS_PER_SEC as i64);
let secs = self.secs * rhs as i64 + extra_secs;
Duration { secs: secs, nanos: nanos as i32 }
}
}
impl Div<i32> for Duration {
type Output = Duration;
fn div(self, rhs: i32) -> Duration {
let mut secs = self.secs / rhs as i64;
let carry = self.secs - secs * rhs as i64;
let extra_nanos = carry * NANOS_PER_SEC as i64 / rhs as i64;
let mut nanos = self.nanos / rhs + extra_nanos as i32;
if nanos >= NANOS_PER_SEC {
nanos -= NANOS_PER_SEC;
secs += 1;
}
if nanos < 0 {
nanos += NANOS_PER_SEC;
secs -= 1;
}
Duration { secs: secs, nanos: nanos }
}
}
impl fmt::Display for Duration {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
// technically speaking, negative duration is not valid ISO 8601,
// but we need to print it anyway.
let (abs, sign) = if self.secs < 0 { (-*self, "-") } else { (*self, "") };
let days = abs.secs / SECS_PER_DAY;
let secs = abs.secs - days * SECS_PER_DAY;
let hasdate = days != 0;
let hastime = (secs != 0 || abs.nanos != 0) || !hasdate;
write!(f, "{}P", sign)?;
if hasdate {
write!(f, "{}D", days)?;
}
if hastime {
if abs.nanos == 0 {
write!(f, "T{}S", secs)?;
} else if abs.nanos % NANOS_PER_MILLI == 0 {
write!(f, "T{}.{:03}S", secs, abs.nanos / NANOS_PER_MILLI)?;
} else if abs.nanos % NANOS_PER_MICRO == 0 {
write!(f, "T{}.{:06}S", secs, abs.nanos / NANOS_PER_MICRO)?;
} else {
write!(f, "T{}.{:09}S", secs, abs.nanos)?;
}
}
Ok(())
}
}
/// Represents error when converting `Duration` to/from a standard library
/// implementation
///
/// The `std::time::Duration` supports a range from zero to `u64::MAX`
/// *seconds*, while this module supports signed range of up to
/// `i64::MAX` of *milliseconds*.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct OutOfRangeError(());
impl fmt::Display for OutOfRangeError {
#[allow(deprecated)]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.description())
}
}
impl Error for OutOfRangeError {
fn description(&self) -> &str {
"Source duration value is out of range for the target type"
}
}
// Copied from libnum
#[inline]
fn div_mod_floor_64(this: i64, other: i64) -> (i64, i64) {
(div_floor_64(this, other), mod_floor_64(this, other))
}
#[inline]
fn div_floor_64(this: i64, other: i64) -> i64 {
match div_rem_64(this, other) {
(d, r) if (r > 0 && other < 0)
|| (r < 0 && other > 0) => d - 1,
(d, _) => d,
}
}
#[inline]
fn mod_floor_64(this: i64, other: i64) -> i64 {
match this % other {
r if (r > 0 && other < 0)
|| (r < 0 && other > 0) => r + other,
r => r,
}
}
#[inline]
fn div_rem_64(this: i64, other: i64) -> (i64, i64) {
(this / other, this % other)
}
#[cfg(test)]
mod tests {
use super::{Duration, MIN, MAX, OutOfRangeError};
use std::{i32, i64};
use std::time::Duration as StdDuration;
#[test]
fn test_duration() {
assert!(Duration::seconds(1) != Duration::zero());
assert_eq!(Duration::seconds(1) + Duration::seconds(2), Duration::seconds(3));
assert_eq!(Duration::seconds(86399) + Duration::seconds(4),
Duration::days(1) + Duration::seconds(3));
assert_eq!(Duration::days(10) - Duration::seconds(1000), Duration::seconds(863000));
assert_eq!(Duration::days(10) - Duration::seconds(1000000), Duration::seconds(-136000));
assert_eq!(Duration::days(2) + Duration::seconds(86399) +
Duration::nanoseconds(1234567890),
Duration::days(3) + Duration::nanoseconds(234567890));
assert_eq!(-Duration::days(3), Duration::days(-3));
assert_eq!(-(Duration::days(3) + Duration::seconds(70)),
Duration::days(-4) + Duration::seconds(86400-70));
}
#[test]
fn test_duration_num_days() {
assert_eq!(Duration::zero().num_days(), 0);
assert_eq!(Duration::days(1).num_days(), 1);
assert_eq!(Duration::days(-1).num_days(), -1);
assert_eq!(Duration::seconds(86399).num_days(), 0);
assert_eq!(Duration::seconds(86401).num_days(), 1);
assert_eq!(Duration::seconds(-86399).num_days(), 0);
assert_eq!(Duration::seconds(-86401).num_days(), -1);
assert_eq!(Duration::days(i32::MAX as i64).num_days(), i32::MAX as i64);
assert_eq!(Duration::days(i32::MIN as i64).num_days(), i32::MIN as i64);
}
#[test]
fn test_duration_num_seconds() {
assert_eq!(Duration::zero().num_seconds(), 0);
assert_eq!(Duration::seconds(1).num_seconds(), 1);
assert_eq!(Duration::seconds(-1).num_seconds(), -1);
assert_eq!(Duration::milliseconds(999).num_seconds(), 0);
assert_eq!(Duration::milliseconds(1001).num_seconds(), 1);
assert_eq!(Duration::milliseconds(-999).num_seconds(), 0);
assert_eq!(Duration::milliseconds(-1001).num_seconds(), -1);
}
#[test]
fn test_duration_num_milliseconds() {
assert_eq!(Duration::zero().num_milliseconds(), 0);
assert_eq!(Duration::milliseconds(1).num_milliseconds(), 1);
assert_eq!(Duration::milliseconds(-1).num_milliseconds(), -1);
assert_eq!(Duration::microseconds(999).num_milliseconds(), 0);
assert_eq!(Duration::microseconds(1001).num_milliseconds(), 1);
assert_eq!(Duration::microseconds(-999).num_milliseconds(), 0);
assert_eq!(Duration::microseconds(-1001).num_milliseconds(), -1);
assert_eq!(Duration::milliseconds(i64::MAX).num_milliseconds(), i64::MAX);
assert_eq!(Duration::milliseconds(i64::MIN).num_milliseconds(), i64::MIN);
assert_eq!(MAX.num_milliseconds(), i64::MAX);
assert_eq!(MIN.num_milliseconds(), i64::MIN);
}
#[test]
fn test_duration_num_microseconds() {
assert_eq!(Duration::zero().num_microseconds(), Some(0));
assert_eq!(Duration::microseconds(1).num_microseconds(), Some(1));
assert_eq!(Duration::microseconds(-1).num_microseconds(), Some(-1));
assert_eq!(Duration::nanoseconds(999).num_microseconds(), Some(0));
assert_eq!(Duration::nanoseconds(1001).num_microseconds(), Some(1));
assert_eq!(Duration::nanoseconds(-999).num_microseconds(), Some(0));
assert_eq!(Duration::nanoseconds(-1001).num_microseconds(), Some(-1));
assert_eq!(Duration::microseconds(i64::MAX).num_microseconds(), Some(i64::MAX));
assert_eq!(Duration::microseconds(i64::MIN).num_microseconds(), Some(i64::MIN));
assert_eq!(MAX.num_microseconds(), None);
assert_eq!(MIN.num_microseconds(), None);
// overflow checks
const MICROS_PER_DAY: i64 = 86400_000_000;
assert_eq!(Duration::days(i64::MAX / MICROS_PER_DAY).num_microseconds(),
Some(i64::MAX / MICROS_PER_DAY * MICROS_PER_DAY));
assert_eq!(Duration::days(i64::MIN / MICROS_PER_DAY).num_microseconds(),
Some(i64::MIN / MICROS_PER_DAY * MICROS_PER_DAY));
assert_eq!(Duration::days(i64::MAX / MICROS_PER_DAY + 1).num_microseconds(), None);
assert_eq!(Duration::days(i64::MIN / MICROS_PER_DAY - 1).num_microseconds(), None);
}
#[test]
fn test_duration_num_nanoseconds() {
assert_eq!(Duration::zero().num_nanoseconds(), Some(0));
assert_eq!(Duration::nanoseconds(1).num_nanoseconds(), Some(1));
assert_eq!(Duration::nanoseconds(-1).num_nanoseconds(), Some(-1));
assert_eq!(Duration::nanoseconds(i64::MAX).num_nanoseconds(), Some(i64::MAX));
assert_eq!(Duration::nanoseconds(i64::MIN).num_nanoseconds(), Some(i64::MIN));
assert_eq!(MAX.num_nanoseconds(), None);
assert_eq!(MIN.num_nanoseconds(), None);
// overflow checks
const NANOS_PER_DAY: i64 = 86400_000_000_000;
assert_eq!(Duration::days(i64::MAX / NANOS_PER_DAY).num_nanoseconds(),
Some(i64::MAX / NANOS_PER_DAY * NANOS_PER_DAY));
assert_eq!(Duration::days(i64::MIN / NANOS_PER_DAY).num_nanoseconds(),
Some(i64::MIN / NANOS_PER_DAY * NANOS_PER_DAY));
assert_eq!(Duration::days(i64::MAX / NANOS_PER_DAY + 1).num_nanoseconds(), None);
assert_eq!(Duration::days(i64::MIN / NANOS_PER_DAY - 1).num_nanoseconds(), None);
}
#[test]
fn test_duration_checked_ops() {
assert_eq!(Duration::milliseconds(i64::MAX - 1).checked_add(&Duration::microseconds(999)),
Some(Duration::milliseconds(i64::MAX - 2) + Duration::microseconds(1999)));
assert!(Duration::milliseconds(i64::MAX).checked_add(&Duration::microseconds(1000))
.is_none());
assert_eq!(Duration::milliseconds(i64::MIN).checked_sub(&Duration::milliseconds(0)),
Some(Duration::milliseconds(i64::MIN)));
assert!(Duration::milliseconds(i64::MIN).checked_sub(&Duration::milliseconds(1))
.is_none());
}
#[test]
fn test_duration_mul() {
assert_eq!(Duration::zero() * i32::MAX, Duration::zero());
assert_eq!(Duration::zero() * i32::MIN, Duration::zero());
assert_eq!(Duration::nanoseconds(1) * 0, Duration::zero());
assert_eq!(Duration::nanoseconds(1) * 1, Duration::nanoseconds(1));
assert_eq!(Duration::nanoseconds(1) * 1_000_000_000, Duration::seconds(1));
assert_eq!(Duration::nanoseconds(1) * -1_000_000_000, -Duration::seconds(1));
assert_eq!(-Duration::nanoseconds(1) * 1_000_000_000, -Duration::seconds(1));
assert_eq!(Duration::nanoseconds(30) * 333_333_333,
Duration::seconds(10) - Duration::nanoseconds(10));
assert_eq!((Duration::nanoseconds(1) + Duration::seconds(1) + Duration::days(1)) * 3,
Duration::nanoseconds(3) + Duration::seconds(3) + Duration::days(3));
assert_eq!(Duration::milliseconds(1500) * -2, Duration::seconds(-3));
assert_eq!(Duration::milliseconds(-1500) * 2, Duration::seconds(-3));
}
#[test]
fn test_duration_div() {
assert_eq!(Duration::zero() / i32::MAX, Duration::zero());
assert_eq!(Duration::zero() / i32::MIN, Duration::zero());
assert_eq!(Duration::nanoseconds(123_456_789) / 1, Duration::nanoseconds(123_456_789));
assert_eq!(Duration::nanoseconds(123_456_789) / -1, -Duration::nanoseconds(123_456_789));
assert_eq!(-Duration::nanoseconds(123_456_789) / -1, Duration::nanoseconds(123_456_789));
assert_eq!(-Duration::nanoseconds(123_456_789) / 1, -Duration::nanoseconds(123_456_789));
assert_eq!(Duration::seconds(1) / 3, Duration::nanoseconds(333_333_333));
assert_eq!(Duration::seconds(4) / 3, Duration::nanoseconds(1_333_333_333));
assert_eq!(Duration::seconds(-1) / 2, Duration::milliseconds(-500));
assert_eq!(Duration::seconds(1) / -2, Duration::milliseconds(-500));
assert_eq!(Duration::seconds(-1) / -2, Duration::milliseconds(500));
assert_eq!(Duration::seconds(-4) / 3, Duration::nanoseconds(-1_333_333_333));
assert_eq!(Duration::seconds(-4) / -3, Duration::nanoseconds(1_333_333_333));
}
#[test]
fn test_duration_fmt() {
assert_eq!(Duration::zero().to_string(), "PT0S");
assert_eq!(Duration::days(42).to_string(), "P42D");
assert_eq!(Duration::days(-42).to_string(), "-P42D");
assert_eq!(Duration::seconds(42).to_string(), "PT42S");
assert_eq!(Duration::milliseconds(42).to_string(), "PT0.042S");
assert_eq!(Duration::microseconds(42).to_string(), "PT0.000042S");
assert_eq!(Duration::nanoseconds(42).to_string(), "PT0.000000042S");
assert_eq!((Duration::days(7) + Duration::milliseconds(6543)).to_string(),
"P7DT6.543S");
assert_eq!(Duration::seconds(-86401).to_string(), "-P1DT1S");
assert_eq!(Duration::nanoseconds(-1).to_string(), "-PT0.000000001S");
// the format specifier should have no effect on `Duration`
assert_eq!(format!("{:30}", Duration::days(1) + Duration::milliseconds(2345)),
"P1DT2.345S");
}
#[test]
fn test_to_std() {
assert_eq!(Duration::seconds(1).to_std(), Ok(StdDuration::new(1, 0)));
assert_eq!(Duration::seconds(86401).to_std(), Ok(StdDuration::new(86401, 0)));
assert_eq!(Duration::milliseconds(123).to_std(), Ok(StdDuration::new(0, 123000000)));
assert_eq!(Duration::milliseconds(123765).to_std(), Ok(StdDuration::new(123, 765000000)));
assert_eq!(Duration::nanoseconds(777).to_std(), Ok(StdDuration::new(0, 777)));
assert_eq!(MAX.to_std(), Ok(StdDuration::new(9223372036854775, 807000000)));
assert_eq!(Duration::seconds(-1).to_std(),
Err(OutOfRangeError(())));
assert_eq!(Duration::milliseconds(-1).to_std(),
Err(OutOfRangeError(())));
}
#[test]
fn test_from_std() {
assert_eq!(Ok(Duration::seconds(1)),
Duration::from_std(StdDuration::new(1, 0)));
assert_eq!(Ok(Duration::seconds(86401)),
Duration::from_std(StdDuration::new(86401, 0)));
assert_eq!(Ok(Duration::milliseconds(123)),
Duration::from_std(StdDuration::new(0, 123000000)));
assert_eq!(Ok(Duration::milliseconds(123765)),
Duration::from_std(StdDuration::new(123, 765000000)));
assert_eq!(Ok(Duration::nanoseconds(777)),
Duration::from_std(StdDuration::new(0, 777)));
assert_eq!(Ok(MAX),
Duration::from_std(StdDuration::new(9223372036854775, 807000000)));
assert_eq!(Duration::from_std(StdDuration::new(9223372036854776, 0)),
Err(OutOfRangeError(())));
assert_eq!(Duration::from_std(StdDuration::new(9223372036854775, 807000001)),
Err(OutOfRangeError(())));
}
}

1282
third_party/rust/time-0.1.43/src/lib.rs поставляемый Normal file

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

Просмотреть файл

1
third_party/rust/time-macros/.cargo-checksum.json поставляемый Normal file
Просмотреть файл

@ -0,0 +1 @@
{"files":{"Cargo.toml":"c993889735eed89f265010c2ff35ffdd33c95077f53b012678d7330b7662ba9e","LICENSE-Apache":"b8929fea28678da67251fb2daf9438f67503814211051861612441806d8edb05","LICENSE-MIT":"04620bf27e4a643dd47bf27652320c205acdb776c1f9f24bb8c3bfaba10804c5","src/date.rs":"ef9432de8c7250b6ffe82fe7cd4332018ce22e32b0a414d3a7d60eb18d9b6eca","src/datetime.rs":"5c7f6e07dc2f0dcfcd86216664df53bc008dbc86f346df57a9ff57f52fe43bc6","src/error.rs":"a1de0680c8a51a05f341352e9707fe50d2c29b00c36600c2b642879cea1e89c6","src/format_description/component.rs":"a05e7549db9bab4f3836f5fd5af18cacbfa6b323d0106b027e21bf438a5885e5","src/format_description/error.rs":"f2085c5914c21bce09e8eb73b2b96584ac28a829f151e339b3f4ca7b5d5405a3","src/format_description/mod.rs":"da47af329408e9428753ad98ce433eaf026cfdd6e73e3142b23285251d32d0dd","src/format_description/modifier.rs":"c252c8a7d6608b594a6f715210ff67e804ae2f308025f62c8dd99d707627e4a9","src/format_description/parse.rs":"e131ccb7e7ad28e851a01f1b4ce8a6febcc2093e21c0a18126f15e8c17044625","src/helpers/mod.rs":"d3283903b66388efced79a4b1eca3a6b4644031d1ae211034443df5b6b78dc51","src/helpers/string.rs":"ba5699a4df344cbd71c4143f642f6bc07591f53978a9800d4b49ca1f461f87d9","src/lib.rs":"fecf82d1c8b1287fda03c3728741e477731534cf9c2609def2b525ad29683119","src/offset.rs":"fc9341648e091b4d8f7bec47006c01c21cb038c7ef98bd36a492cf78e7533023","src/quote.rs":"058ae621d7c6951c6289a3e855c74d924d9434c1f3031438587aedeb718be283","src/serde_format_description.rs":"5750a1ecf75ae80dc88d4242a6e56b39f3e25c77fdbcc2e88ec3a91ad8511434","src/time.rs":"3c06562358aed7ef624319c96e3f9c150a069606ab930de98ac379ef16b08100","src/to_tokens.rs":"825150a92396a019fee44f21da0bd257349e276d5e75a23ff86cfc625bef6f10"},"package":"42657b1a6f4d817cda8e7a0ace261fe0cc946cf3a80314390b22cc61ae080792"}

29
third_party/rust/time-macros/Cargo.toml поставляемый Normal file
Просмотреть файл

@ -0,0 +1,29 @@
# 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 = "2018"
name = "time-macros"
version = "0.2.4"
authors = ["Jacob Pratt <open-source@jhpratt.dev>", "Time contributors"]
description = "Procedural macros for the time crate."
readme = "../README.md"
keywords = ["date", "time", "calendar", "duration"]
categories = ["date-and-time"]
license = "MIT OR Apache-2.0"
repository = "https://github.com/time-rs/time"
resolver = "2"
[lib]
proc-macro = true
[features]
large-dates = []

202
third_party/rust/time-macros/LICENSE-Apache поставляемый Normal file
Просмотреть файл

@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright 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.

19
third_party/rust/time-macros/LICENSE-MIT поставляемый Normal file
Просмотреть файл

@ -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.

137
third_party/rust/time-macros/src/date.rs поставляемый Normal file
Просмотреть файл

@ -0,0 +1,137 @@
use std::iter::Peekable;
use proc_macro::{token_stream, TokenTree};
use crate::helpers::{
consume_any_ident, consume_number, consume_punct, days_in_year, days_in_year_month,
weeks_in_year, ymd_to_yo, ywd_to_yo,
};
use crate::to_tokens::ToTokenTree;
use crate::Error;
#[cfg(feature = "large-dates")]
const MAX_YEAR: i32 = 999_999;
#[cfg(not(feature = "large-dates"))]
const MAX_YEAR: i32 = 9_999;
pub(crate) struct Date {
pub(crate) year: i32,
pub(crate) ordinal: u16,
}
pub(crate) fn parse(chars: &mut Peekable<token_stream::IntoIter>) -> Result<Date, Error> {
let (year_sign_span, year_sign, explicit_sign) = if let Ok(span) = consume_punct('-', chars) {
(Some(span), -1, true)
} else if let Ok(span) = consume_punct('+', chars) {
(Some(span), 1, true)
} else {
(None, 1, false)
};
let (year_span, mut year) = consume_number::<i32>("year", chars)?;
year *= year_sign;
if year.abs() > MAX_YEAR {
return Err(Error::InvalidComponent {
name: "year",
value: year.to_string(),
span_start: Some(year_sign_span.unwrap_or(year_span)),
span_end: Some(year_span),
});
}
if !explicit_sign && year.abs() >= 10_000 {
return Err(Error::Custom {
message: "years with more than four digits must have an explicit sign".into(),
span_start: Some(year_sign_span.unwrap_or(year_span)),
span_end: Some(year_span),
});
}
consume_punct('-', chars)?;
// year-week-day
if let Ok(w_span) = consume_any_ident(&["W"], chars) {
let (week_span, week) = consume_number::<u8>("week", chars)?;
consume_punct('-', chars)?;
let (day_span, day) = consume_number::<u8>("day", chars)?;
if week > weeks_in_year(year) {
return Err(Error::InvalidComponent {
name: "week",
value: week.to_string(),
span_start: Some(w_span),
span_end: Some(week_span),
});
}
if day == 0 || day > 7 {
return Err(Error::InvalidComponent {
name: "day",
value: day.to_string(),
span_start: Some(day_span),
span_end: Some(day_span),
});
}
let (year, ordinal) = ywd_to_yo(year, week, day);
return Ok(Date { year, ordinal });
}
// We don't yet know whether it's year-month-day or year-ordinal.
let (month_or_ordinal_span, month_or_ordinal) =
consume_number::<u16>("month or ordinal", chars)?;
// year-month-day
#[allow(clippy::branches_sharing_code)] // clarity
if consume_punct('-', chars).is_ok() {
let (month_span, month) = (month_or_ordinal_span, month_or_ordinal);
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 _;
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);
Ok(Date { year, ordinal })
}
// year-ordinal
else {
let (ordinal_span, ordinal) = (month_or_ordinal_span, month_or_ordinal);
if ordinal == 0 || ordinal > days_in_year(year) {
return Err(Error::InvalidComponent {
name: "ordinal",
value: ordinal.to_string(),
span_start: Some(ordinal_span),
span_end: Some(ordinal_span),
});
}
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),
);
DATE
}}
}
}

57
third_party/rust/time-macros/src/datetime.rs поставляемый Normal file
Просмотреть файл

@ -0,0 +1,57 @@
use std::iter::Peekable;
use proc_macro::{token_stream, Ident, Span, TokenTree};
use crate::date::Date;
use crate::error::Error;
use crate::offset::Offset;
use crate::time::Time;
use crate::to_tokens::ToTokenTree;
use crate::{date, offset, time};
pub(crate) struct DateTime {
date: Date,
time: Time,
offset: Option<Offset>,
}
pub(crate) fn parse(chars: &mut Peekable<token_stream::IntoIter>) -> Result<DateTime, Error> {
let date = date::parse(chars)?;
let time = time::parse(chars)?;
let offset = match offset::parse(chars) {
Ok(offset) => Some(offset),
Err(Error::UnexpectedEndOfInput | Error::MissingComponent { name: "sign", .. }) => None,
Err(err) => return Err(err),
};
if let Some(token) = chars.peek() {
return Err(Error::UnexpectedToken {
tree: token.clone(),
});
}
Ok(DateTime { date, time, offset })
}
impl ToTokenTree for DateTime {
fn into_token_tree(self) -> TokenTree {
let (type_name, maybe_offset) = match self.offset {
Some(offset) => (
Ident::new("OffsetDateTime", Span::mixed_site()),
quote!(.assume_offset(#(offset))),
),
None => (
Ident::new("PrimitiveDateTime", Span::mixed_site()),
quote!(),
),
};
quote_group! {{
const DATE_TIME: ::time::#(type_name) = ::time::PrimitiveDateTime::new(
#(self.date),
#(self.time),
) #S(maybe_offset);
DATE_TIME
}}
}
}

129
third_party/rust/time-macros/src/error.rs поставляемый Normal file
Просмотреть файл

@ -0,0 +1,129 @@
use std::borrow::Cow;
use std::fmt;
use std::iter::once;
use proc_macro::{Delimiter, Group, Ident, Literal, Punct, Spacing, Span, TokenStream, TokenTree};
use crate::format_description::error::InvalidFormatDescription;
trait WithSpan {
fn with_span(self, span: Span) -> Self;
}
impl WithSpan for TokenTree {
fn with_span(mut self, span: Span) -> Self {
self.set_span(span);
self
}
}
pub(crate) enum Error {
MissingComponent {
name: &'static str,
span_start: Option<Span>,
span_end: Option<Span>,
},
InvalidComponent {
name: &'static str,
value: String,
span_start: Option<Span>,
span_end: Option<Span>,
},
ExpectedString {
span_start: Option<Span>,
span_end: Option<Span>,
},
UnexpectedToken {
tree: TokenTree,
},
UnexpectedEndOfInput,
InvalidFormatDescription {
error: InvalidFormatDescription,
span_start: Option<Span>,
span_end: Option<Span>,
},
Custom {
message: Cow<'static, str>,
span_start: Option<Span>,
span_end: Option<Span>,
},
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::MissingComponent { name, .. } => write!(f, "missing component: {}", name),
Self::InvalidComponent { name, value, .. } => {
write!(f, "invalid component: {} was {}", name, value)
}
Self::ExpectedString { .. } => f.write_str("expected string"),
Self::UnexpectedToken { tree } => write!(f, "unexpected token: {}", tree),
Self::UnexpectedEndOfInput => f.write_str("unexpected end of input"),
Self::InvalidFormatDescription { error, .. } => error.fmt(f),
Self::Custom { message, .. } => f.write_str(message),
}
}
}
impl Error {
fn span_start(&self) -> Span {
match self {
Self::MissingComponent { span_start, .. }
| Self::InvalidComponent { span_start, .. }
| Self::ExpectedString { span_start, .. }
| Self::InvalidFormatDescription { span_start, .. }
| Self::Custom { span_start, .. } => *span_start,
Self::UnexpectedToken { tree } => Some(tree.span()),
Self::UnexpectedEndOfInput => Some(Span::mixed_site()),
}
.unwrap_or_else(Span::mixed_site)
}
fn span_end(&self) -> Span {
match self {
Self::MissingComponent { span_end, .. }
| Self::InvalidComponent { span_end, .. }
| Self::ExpectedString { span_end, .. }
| Self::InvalidFormatDescription { span_end, .. }
| Self::Custom { span_end, .. } => *span_end,
Self::UnexpectedToken { tree, .. } => Some(tree.span()),
Self::UnexpectedEndOfInput => Some(Span::mixed_site()),
}
.unwrap_or_else(|| self.span_start())
}
pub(crate) fn to_compile_error(&self) -> TokenStream {
let (start, end) = (self.span_start(), self.span_end());
[
TokenTree::from(Punct::new(':', Spacing::Joint)).with_span(start),
TokenTree::from(Punct::new(':', Spacing::Alone)).with_span(start),
TokenTree::from(Ident::new("core", start)),
TokenTree::from(Punct::new(':', Spacing::Joint)).with_span(start),
TokenTree::from(Punct::new(':', Spacing::Alone)).with_span(start),
TokenTree::from(Ident::new("compile_error", start)),
TokenTree::from(Punct::new('!', Spacing::Alone)).with_span(start),
TokenTree::from(Group::new(
Delimiter::Parenthesis,
TokenStream::from(
TokenTree::from(Literal::string(&self.to_string())).with_span(end),
),
))
.with_span(end),
]
.iter()
.cloned()
.collect()
}
/// Like `to_compile_error`, but for use in macros that produce items.
pub(crate) fn to_compile_error_standalone(&self) -> TokenStream {
let end = self.span_end();
self.to_compile_error()
.into_iter()
.chain(once(
TokenTree::from(Punct::new(';', Spacing::Alone)).with_span(end),
))
.collect()
}
}

168
third_party/rust/time-macros/src/format_description/component.rs поставляемый Normal file
Просмотреть файл

@ -0,0 +1,168 @@
use proc_macro::{Ident, Span, TokenStream};
use crate::format_description::error::InvalidFormatDescription;
use crate::format_description::modifier;
use crate::format_description::modifier::Modifiers;
use crate::to_tokens::ToTokenStream;
pub(crate) enum Component {
Day(modifier::Day),
Month(modifier::Month),
Ordinal(modifier::Ordinal),
Weekday(modifier::Weekday),
WeekNumber(modifier::WeekNumber),
Year(modifier::Year),
Hour(modifier::Hour),
Minute(modifier::Minute),
Period(modifier::Period),
Second(modifier::Second),
Subsecond(modifier::Subsecond),
OffsetHour(modifier::OffsetHour),
OffsetMinute(modifier::OffsetMinute),
OffsetSecond(modifier::OffsetSecond),
}
impl ToTokenStream for Component {
fn append_to(self, ts: &mut TokenStream) {
let mut mts = TokenStream::new();
macro_rules! component_name_and_append {
($($name:ident)*) => {
match self {
$(Self::$name(modifier) => {
modifier.append_to(&mut mts);
stringify!($name)
})*
}
};
}
let component = component_name_and_append![
Day
Month
Ordinal
Weekday
WeekNumber
Year
Hour
Minute
Period
Second
Subsecond
OffsetHour
OffsetMinute
OffsetSecond
];
let component = Ident::new(component, Span::mixed_site());
quote_append! { ts
::time::format_description::Component::#(component)(#S(mts))
}
}
}
pub(crate) enum NakedComponent {
Day,
Month,
Ordinal,
Weekday,
WeekNumber,
Year,
Hour,
Minute,
Period,
Second,
Subsecond,
OffsetHour,
OffsetMinute,
OffsetSecond,
}
impl NakedComponent {
pub(crate) fn parse(
component_name: &[u8],
component_index: usize,
) -> Result<Self, InvalidFormatDescription> {
match component_name {
b"day" => Ok(Self::Day),
b"month" => Ok(Self::Month),
b"ordinal" => Ok(Self::Ordinal),
b"weekday" => Ok(Self::Weekday),
b"week_number" => Ok(Self::WeekNumber),
b"year" => Ok(Self::Year),
b"hour" => Ok(Self::Hour),
b"minute" => Ok(Self::Minute),
b"period" => Ok(Self::Period),
b"second" => Ok(Self::Second),
b"subsecond" => Ok(Self::Subsecond),
b"offset_hour" => Ok(Self::OffsetHour),
b"offset_minute" => Ok(Self::OffsetMinute),
b"offset_second" => Ok(Self::OffsetSecond),
b"" => Err(InvalidFormatDescription::MissingComponentName {
index: component_index,
}),
_ => Err(InvalidFormatDescription::InvalidComponentName {
name: String::from_utf8_lossy(component_name).into_owned(),
index: component_index,
}),
}
}
pub(crate) fn attach_modifiers(self, modifiers: Modifiers) -> Component {
match self {
Self::Day => Component::Day(modifier::Day {
padding: modifiers.padding.unwrap_or_default(),
}),
Self::Month => Component::Month(modifier::Month {
padding: modifiers.padding.unwrap_or_default(),
repr: modifiers.month_repr.unwrap_or_default(),
case_sensitive: modifiers.case_sensitive.unwrap_or(true),
}),
Self::Ordinal => Component::Ordinal(modifier::Ordinal {
padding: modifiers.padding.unwrap_or_default(),
}),
Self::Weekday => Component::Weekday(modifier::Weekday {
repr: modifiers.weekday_repr.unwrap_or_default(),
one_indexed: modifiers.weekday_is_one_indexed.unwrap_or(true),
case_sensitive: modifiers.case_sensitive.unwrap_or(true),
}),
Self::WeekNumber => Component::WeekNumber(modifier::WeekNumber {
padding: modifiers.padding.unwrap_or_default(),
repr: modifiers.week_number_repr.unwrap_or_default(),
}),
Self::Year => Component::Year(modifier::Year {
padding: modifiers.padding.unwrap_or_default(),
repr: modifiers.year_repr.unwrap_or_default(),
iso_week_based: modifiers.year_is_iso_week_based.unwrap_or_default(),
sign_is_mandatory: modifiers.sign_is_mandatory.unwrap_or_default(),
}),
Self::Hour => Component::Hour(modifier::Hour {
padding: modifiers.padding.unwrap_or_default(),
is_12_hour_clock: modifiers.hour_is_12_hour_clock.unwrap_or_default(),
}),
Self::Minute => Component::Minute(modifier::Minute {
padding: modifiers.padding.unwrap_or_default(),
}),
Self::Period => Component::Period(modifier::Period {
is_uppercase: modifiers.period_is_uppercase.unwrap_or(true),
case_sensitive: modifiers.case_sensitive.unwrap_or(true),
}),
Self::Second => Component::Second(modifier::Second {
padding: modifiers.padding.unwrap_or_default(),
}),
Self::Subsecond => Component::Subsecond(modifier::Subsecond {
digits: modifiers.subsecond_digits.unwrap_or_default(),
}),
Self::OffsetHour => Component::OffsetHour(modifier::OffsetHour {
sign_is_mandatory: modifiers.sign_is_mandatory.unwrap_or_default(),
padding: modifiers.padding.unwrap_or_default(),
}),
Self::OffsetMinute => Component::OffsetMinute(modifier::OffsetMinute {
padding: modifiers.padding.unwrap_or_default(),
}),
Self::OffsetSecond => Component::OffsetSecond(modifier::OffsetSecond {
padding: modifiers.padding.unwrap_or_default(),
}),
}
}
}

31
third_party/rust/time-macros/src/format_description/error.rs поставляемый Normal file
Просмотреть файл

@ -0,0 +1,31 @@
use std::fmt;
pub(crate) enum InvalidFormatDescription {
UnclosedOpeningBracket { index: usize },
InvalidComponentName { name: String, index: usize },
InvalidModifier { value: String, index: usize },
MissingComponentName { index: usize },
}
impl fmt::Display for InvalidFormatDescription {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
#[allow(clippy::enum_glob_use)]
use InvalidFormatDescription::*;
match self {
UnclosedOpeningBracket { index } => {
write!(f, "unclosed opening bracket at byte index {}", index)
}
InvalidComponentName { name, index } => write!(
f,
"invalid component name `{}` at byte index {}",
name, index
),
InvalidModifier { value, index } => {
write!(f, "invalid modifier `{}` at byte index {}", value, index)
}
MissingComponentName { index } => {
write!(f, "missing component name at byte index {}", index)
}
}
}
}

40
third_party/rust/time-macros/src/format_description/mod.rs поставляемый Normal file
Просмотреть файл

@ -0,0 +1,40 @@
mod component;
pub(crate) mod error;
pub(crate) mod modifier;
pub(crate) mod parse;
use proc_macro::{Literal, TokenStream};
pub(crate) use self::component::Component;
pub(crate) use self::parse::parse;
use crate::to_tokens::ToTokenStream;
mod helper {
#[must_use = "This does not modify the original slice."]
pub(crate) fn consume_whitespace<'a>(bytes: &'a [u8], index: &mut usize) -> &'a [u8] {
let first_non_whitespace = bytes
.iter()
.position(|c| !c.is_ascii_whitespace())
.unwrap_or(bytes.len());
*index += first_non_whitespace;
&bytes[first_non_whitespace..]
}
}
#[allow(single_use_lifetimes)] // false positive
#[allow(variant_size_differences)]
pub(crate) enum FormatItem<'a> {
Literal(&'a [u8]),
Component(Component),
}
impl ToTokenStream for FormatItem<'_> {
fn append_to(self, ts: &mut TokenStream) {
quote_append! { ts
::time::format_description::FormatItem::#S(match self {
FormatItem::Literal(bytes) => quote! { Literal(#(Literal::byte_string(bytes))) },
FormatItem::Component(component) => quote! { Component(#S(component)) },
})
}
}
}

417
third_party/rust/time-macros/src/format_description/modifier.rs поставляемый Normal file
Просмотреть файл

@ -0,0 +1,417 @@
use core::mem;
use proc_macro::{Ident, Span, TokenStream, TokenTree};
use crate::format_description::error::InvalidFormatDescription;
use crate::format_description::helper;
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;
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,
))
}
}
};
(
$(#[$enum_attr:meta])*
$enum_vis:vis enum $enum_name:ident {$(
$(#[$variant_attr:meta])*
$variant_name:ident
),+ $(,)?}
) => {
$(#[$enum_attr])*
$enum_vis enum $enum_name {$(
$(#[$variant_attr])*
$variant_name
),+}
impl ToTokenStream for $enum_name {
fn append_to(self, ts: &mut TokenStream) {
quote_append! { ts
::time::format_description::modifier::$enum_name::
};
let name = match self {
$(Self::$variant_name => stringify!($variant_name)),+
};
ts.extend([TokenTree::Ident(Ident::new(name, Span::mixed_site()))]);
}
}
}
}
to_tokens! {
pub(crate) struct Day {
pub(crate) padding: Padding,
}
}
to_tokens! {
pub(crate) enum MonthRepr {
Numerical,
Long,
Short,
}
}
to_tokens! {
pub(crate) struct Month {
pub(crate) padding: Padding,
pub(crate) repr: MonthRepr,
pub(crate) case_sensitive: bool,
}
}
to_tokens! {
pub(crate) struct Ordinal {
pub(crate) padding: Padding,
}
}
to_tokens! {
pub(crate) enum WeekdayRepr {
Short,
Long,
Sunday,
Monday,
}
}
to_tokens! {
pub(crate) struct Weekday {
pub(crate) repr: WeekdayRepr,
pub(crate) one_indexed: bool,
pub(crate) case_sensitive: bool,
}
}
to_tokens! {
pub(crate) enum WeekNumberRepr {
Iso,
Sunday,
Monday,
}
}
to_tokens! {
pub(crate) struct WeekNumber {
pub(crate) padding: Padding,
pub(crate) repr: WeekNumberRepr,
}
}
to_tokens! {
pub(crate) enum YearRepr {
Full,
LastTwo,
}
}
to_tokens! {
pub(crate) struct Year {
pub(crate) padding: Padding,
pub(crate) repr: YearRepr,
pub(crate) iso_week_based: bool,
pub(crate) sign_is_mandatory: bool,
}
}
to_tokens! {
pub(crate) struct Hour {
pub(crate) padding: Padding,
pub(crate) is_12_hour_clock: bool,
}
}
to_tokens! {
pub(crate) struct Minute {
pub(crate) padding: Padding,
}
}
to_tokens! {
pub(crate) struct Period {
pub(crate) is_uppercase: bool,
pub(crate) case_sensitive: bool,
}
}
to_tokens! {
pub(crate) struct Second {
pub(crate) padding: Padding,
}
}
to_tokens! {
pub(crate) enum SubsecondDigits {
One,
Two,
Three,
Four,
Five,
Six,
Seven,
Eight,
Nine,
OneOrMore,
}
}
to_tokens! {
pub(crate) struct Subsecond {
pub(crate) digits: SubsecondDigits,
}
}
to_tokens! {
pub(crate) struct OffsetHour {
pub(crate) sign_is_mandatory: bool,
pub(crate) padding: Padding,
}
}
to_tokens! {
pub(crate) struct OffsetMinute {
pub(crate) padding: Padding,
}
}
to_tokens! {
pub(crate) struct OffsetSecond {
pub(crate) padding: Padding,
}
}
to_tokens! {
pub(crate) enum Padding {
Space,
Zero,
None,
}
}
macro_rules! impl_default {
($($type:ty => $default:expr;)*) => {$(
impl Default for $type {
fn default() -> Self {
$default
}
}
)*};
}
impl_default! {
Day => Self { padding: Padding::default() };
MonthRepr => Self::Numerical;
Month => Self {
padding: Padding::default(),
repr: MonthRepr::default(),
case_sensitive: true,
};
Ordinal => Self { padding: Padding::default() };
WeekdayRepr => Self::Long;
Weekday => Self {
repr: WeekdayRepr::default(),
one_indexed: true,
case_sensitive: true,
};
WeekNumberRepr => Self::Iso;
WeekNumber => Self {
padding: Padding::default(),
repr: WeekNumberRepr::default(),
};
YearRepr => Self::Full;
Year => Self {
padding: Padding::default(),
repr: YearRepr::default(),
iso_week_based: false,
sign_is_mandatory: false,
};
Hour => Self {
padding: Padding::default(),
is_12_hour_clock: false,
};
Minute => Self { padding: Padding::default() };
Period => Self { is_uppercase: true, case_sensitive: true };
Second => Self { padding: Padding::default() };
SubsecondDigits => Self::OneOrMore;
Subsecond => Self { digits: SubsecondDigits::default() };
OffsetHour => Self {
sign_is_mandatory: true,
padding: Padding::default(),
};
OffsetMinute => Self { padding: Padding::default() };
OffsetSecond => Self { padding: Padding::default() };
Padding => Self::Zero;
}
#[derive(Default)]
pub(crate) struct Modifiers {
pub(crate) padding: Option<Padding>,
pub(crate) hour_is_12_hour_clock: Option<bool>,
pub(crate) period_is_uppercase: Option<bool>,
pub(crate) month_repr: Option<MonthRepr>,
pub(crate) subsecond_digits: Option<SubsecondDigits>,
pub(crate) weekday_repr: Option<WeekdayRepr>,
pub(crate) weekday_is_one_indexed: Option<bool>,
pub(crate) week_number_repr: Option<WeekNumberRepr>,
pub(crate) year_repr: Option<YearRepr>,
pub(crate) year_is_iso_week_based: Option<bool>,
pub(crate) sign_is_mandatory: Option<bool>,
pub(crate) case_sensitive: Option<bool>,
}
impl Modifiers {
#[allow(clippy::too_many_lines)]
pub(crate) fn parse(
component_name: &[u8],
mut bytes: &[u8],
index: &mut usize,
) -> Result<Self, InvalidFormatDescription> {
let mut modifiers = Self::default();
while !bytes.is_empty() {
// Trim any whitespace between modifiers.
bytes = helper::consume_whitespace(bytes, index);
let modifier;
if let Some(whitespace_loc) = bytes.iter().position(u8::is_ascii_whitespace) {
*index += whitespace_loc;
modifier = &bytes[..whitespace_loc];
bytes = &bytes[whitespace_loc..];
} else {
modifier = mem::take(&mut bytes);
}
if modifier.is_empty() {
break;
}
match (component_name, modifier) {
(
b"day" | b"hour" | b"minute" | b"month" | b"offset_hour" | b"offset_minute"
| b"offset_second" | b"ordinal" | b"second" | b"week_number" | b"year",
b"padding:space",
) => modifiers.padding = Some(Padding::Space),
(
b"day" | b"hour" | b"minute" | b"month" | b"offset_hour" | b"offset_minute"
| b"offset_second" | b"ordinal" | b"second" | b"week_number" | b"year",
b"padding:zero",
) => modifiers.padding = Some(Padding::Zero),
(
b"day" | b"hour" | b"minute" | b"month" | b"offset_hour" | b"offset_minute"
| b"offset_second" | b"ordinal" | b"second" | b"week_number" | b"year",
b"padding:none",
) => modifiers.padding = Some(Padding::None),
(b"hour", b"repr:24") => modifiers.hour_is_12_hour_clock = Some(false),
(b"hour", b"repr:12") => modifiers.hour_is_12_hour_clock = Some(true),
(b"month" | b"period" | b"weekday", b"case_sensitive:true") => {
modifiers.case_sensitive = Some(true)
}
(b"month" | b"period" | b"weekday", b"case_sensitive:false") => {
modifiers.case_sensitive = Some(false)
}
(b"month", b"repr:numerical") => modifiers.month_repr = Some(MonthRepr::Numerical),
(b"month", b"repr:long") => modifiers.month_repr = Some(MonthRepr::Long),
(b"month", b"repr:short") => modifiers.month_repr = Some(MonthRepr::Short),
(b"offset_hour" | b"year", b"sign:automatic") => {
modifiers.sign_is_mandatory = Some(false);
}
(b"offset_hour" | b"year", b"sign:mandatory") => {
modifiers.sign_is_mandatory = Some(true);
}
(b"period", b"case:upper") => modifiers.period_is_uppercase = Some(true),
(b"period", b"case:lower") => modifiers.period_is_uppercase = Some(false),
(b"subsecond", b"digits:1") => {
modifiers.subsecond_digits = Some(SubsecondDigits::One);
}
(b"subsecond", b"digits:2") => {
modifiers.subsecond_digits = Some(SubsecondDigits::Two);
}
(b"subsecond", b"digits:3") => {
modifiers.subsecond_digits = Some(SubsecondDigits::Three);
}
(b"subsecond", b"digits:4") => {
modifiers.subsecond_digits = Some(SubsecondDigits::Four);
}
(b"subsecond", b"digits:5") => {
modifiers.subsecond_digits = Some(SubsecondDigits::Five);
}
(b"subsecond", b"digits:6") => {
modifiers.subsecond_digits = Some(SubsecondDigits::Six);
}
(b"subsecond", b"digits:7") => {
modifiers.subsecond_digits = Some(SubsecondDigits::Seven);
}
(b"subsecond", b"digits:8") => {
modifiers.subsecond_digits = Some(SubsecondDigits::Eight);
}
(b"subsecond", b"digits:9") => {
modifiers.subsecond_digits = Some(SubsecondDigits::Nine);
}
(b"subsecond", b"digits:1+") => {
modifiers.subsecond_digits = Some(SubsecondDigits::OneOrMore);
}
(b"weekday", b"repr:short") => modifiers.weekday_repr = Some(WeekdayRepr::Short),
(b"weekday", b"repr:long") => modifiers.weekday_repr = Some(WeekdayRepr::Long),
(b"weekday", b"repr:sunday") => modifiers.weekday_repr = Some(WeekdayRepr::Sunday),
(b"weekday", b"repr:monday") => modifiers.weekday_repr = Some(WeekdayRepr::Monday),
(b"weekday", b"one_indexed:true") => modifiers.weekday_is_one_indexed = Some(true),
(b"weekday", b"one_indexed:false") => {
modifiers.weekday_is_one_indexed = Some(false);
}
(b"week_number", b"repr:iso") => {
modifiers.week_number_repr = Some(WeekNumberRepr::Iso);
}
(b"week_number", b"repr:sunday") => {
modifiers.week_number_repr = Some(WeekNumberRepr::Sunday);
}
(b"week_number", b"repr:monday") => {
modifiers.week_number_repr = Some(WeekNumberRepr::Monday);
}
(b"year", b"repr:full") => modifiers.year_repr = Some(YearRepr::Full),
(b"year", b"repr:last_two") => modifiers.year_repr = Some(YearRepr::LastTwo),
(b"year", b"base:calendar") => modifiers.year_is_iso_week_based = Some(false),
(b"year", b"base:iso_week") => modifiers.year_is_iso_week_based = Some(true),
_ => {
return Err(InvalidFormatDescription::InvalidModifier {
value: String::from_utf8_lossy(modifier).into_owned(),
index: *index,
});
}
}
}
Ok(modifiers)
}
}

84
third_party/rust/time-macros/src/format_description/parse.rs поставляемый Normal file
Просмотреть файл

@ -0,0 +1,84 @@
use proc_macro::Span;
use crate::format_description::component::{Component, NakedComponent};
use crate::format_description::error::InvalidFormatDescription;
use crate::format_description::{helper, modifier, FormatItem};
use crate::Error;
struct ParsedItem<'a> {
item: FormatItem<'a>,
remaining: &'a [u8],
}
fn parse_component(mut s: &[u8], index: &mut usize) -> Result<Component, InvalidFormatDescription> {
s = helper::consume_whitespace(s, index);
let component_index = *index;
let whitespace_loc = s
.iter()
.position(u8::is_ascii_whitespace)
.unwrap_or(s.len());
*index += whitespace_loc;
let component_name = &s[..whitespace_loc];
s = &s[whitespace_loc..];
s = helper::consume_whitespace(s, index);
Ok(NakedComponent::parse(component_name, component_index)?
.attach_modifiers(modifier::Modifiers::parse(component_name, s, index)?))
}
fn parse_literal<'a>(s: &'a [u8], index: &mut usize) -> ParsedItem<'a> {
let loc = s.iter().position(|&c| c == b'[').unwrap_or(s.len());
*index += loc;
ParsedItem {
item: FormatItem::Literal(&s[..loc]),
remaining: &s[loc..],
}
}
fn parse_item<'a>(
s: &'a [u8],
index: &mut usize,
) -> Result<ParsedItem<'a>, InvalidFormatDescription> {
if let [b'[', b'[', remaining @ ..] = s {
*index += 2;
return Ok(ParsedItem {
item: FormatItem::Literal(&[b'[']),
remaining,
});
};
if s.starts_with(&[b'[']) {
if let Some(bracket_index) = s.iter().position(|&c| c == b']') {
*index += 1; // opening bracket
let ret_val = ParsedItem {
item: FormatItem::Component(parse_component(&s[1..bracket_index], index)?),
remaining: &s[bracket_index + 1..],
};
*index += 1; // closing bracket
Ok(ret_val)
} else {
Err(InvalidFormatDescription::UnclosedOpeningBracket { index: *index })
}
} else {
Ok(parse_literal(s, index))
}
}
pub(crate) fn parse(mut s: &[u8], span: Span) -> Result<Vec<FormatItem<'_>>, Error> {
let mut compound = Vec::new();
let mut loc = 0;
while !s.is_empty() {
let ParsedItem { item, remaining } =
parse_item(s, &mut loc).map_err(|error| Error::InvalidFormatDescription {
error,
span_start: Some(span),
span_end: Some(span),
})?;
s = remaining;
compound.push(item);
}
Ok(compound)
}

136
third_party/rust/time-macros/src/helpers/mod.rs поставляемый Normal file
Просмотреть файл

@ -0,0 +1,136 @@
mod string;
use std::iter::Peekable;
use std::str::FromStr;
use proc_macro::{token_stream, Span, TokenStream, TokenTree};
use crate::Error;
pub(crate) fn get_string_literal(tokens: TokenStream) -> Result<(Span, Vec<u8>), Error> {
let mut tokens = tokens.into_iter();
match (tokens.next(), tokens.next()) {
(Some(TokenTree::Literal(literal)), None) => string::parse(&literal),
(Some(tree), None) => Err(Error::ExpectedString {
span_start: Some(tree.span()),
span_end: Some(tree.span()),
}),
(_, Some(tree)) => Err(Error::UnexpectedToken { tree }),
(None, None) => Err(Error::ExpectedString {
span_start: None,
span_end: None,
}),
}
}
pub(crate) fn consume_number<T: FromStr>(
component_name: &'static str,
chars: &mut Peekable<token_stream::IntoIter>,
) -> Result<(Span, T), Error> {
let (span, digits) = match chars.next() {
Some(TokenTree::Literal(literal)) => (literal.span(), literal.to_string()),
Some(tree) => return Err(Error::UnexpectedToken { tree }),
None => return Err(Error::UnexpectedEndOfInput),
};
if let Ok(value) = digits.replace('_', "").parse() {
Ok((span, value))
} else {
Err(Error::InvalidComponent {
name: component_name,
value: digits,
span_start: Some(span),
span_end: Some(span),
})
}
}
pub(crate) fn consume_any_ident(
idents: &[&str],
chars: &mut Peekable<token_stream::IntoIter>,
) -> Result<Span, Error> {
match chars.peek() {
Some(TokenTree::Ident(char)) if idents.contains(&char.to_string().as_str()) => {
let ret = Ok(char.span());
drop(chars.next());
ret
}
Some(tree) => Err(Error::UnexpectedToken { tree: tree.clone() }),
None => Err(Error::UnexpectedEndOfInput),
}
}
pub(crate) fn consume_punct(
c: char,
chars: &mut Peekable<token_stream::IntoIter>,
) -> Result<Span, Error> {
match chars.peek() {
Some(TokenTree::Punct(punct)) if *punct == c => {
let ret = Ok(punct.span());
drop(chars.next());
ret
}
Some(tree) => Err(Error::UnexpectedToken { tree: tree.clone() }),
None => Err(Error::UnexpectedEndOfInput),
}
}
fn is_leap_year(year: i32) -> bool {
(year % 4 == 0) && ((year % 100 != 0) || (year % 400 == 0))
}
fn jan_weekday(year: i32, ordinal: i32) -> u8 {
macro_rules! div_floor {
($a:expr, $b:expr) => {{
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)
+ div_floor!(adj_year, 400)
+ 6)
.rem_euclid(7)) as _
}
pub(crate) fn days_in_year(year: i32) -> u16 {
365 + is_leap_year(year) as u16
}
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
}
pub(crate) fn weeks_in_year(year: i32) -> u8 {
52 + (jan_weekday(year, 1) + is_leap_year(year) as u8 == 3) as u8
}
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;
(year, ordinal + u16::from(day))
}

188
third_party/rust/time-macros/src/helpers/string.rs поставляемый Normal file
Просмотреть файл

@ -0,0 +1,188 @@
use std::ops::{Index, RangeFrom};
use proc_macro::Span;
use crate::Error;
pub(crate) fn parse(token: &proc_macro::Literal) -> Result<(Span, Vec<u8>), Error> {
let span = token.span();
let repr = token.to_string();
match repr.as_bytes() {
[b'"', ..] => Ok((span, parse_lit_str_cooked(&repr[1..]))),
[b'b', b'"', rest @ ..] => Ok((span, parse_lit_byte_str_cooked(rest))),
[b'r', rest @ ..] | [b'b', b'r', rest @ ..] => Ok((span, parse_lit_str_raw(rest))),
_ => Err(Error::ExpectedString {
span_start: Some(span),
span_end: Some(span),
}),
}
}
fn byte(s: impl AsRef<[u8]>, idx: usize) -> u8 {
s.as_ref().get(idx).copied().unwrap_or_default()
}
fn parse_lit_str_cooked(mut s: &str) -> Vec<u8> {
let mut content = String::new();
'outer: loop {
let ch = match byte(s, 0) {
b'"' => break,
b'\\' => {
let b = byte(s, 1);
s = &s[2..];
match b {
b'x' => {
let (byte, rest) = backslash_x(s);
s = rest;
char::from_u32(u32::from(byte)).expect("byte was just validated")
}
b'u' => {
let (chr, rest) = backslash_u(s);
s = rest;
chr
}
b'n' => '\n',
b'r' => '\r',
b't' => '\t',
b'\\' => '\\',
b'0' => '\0',
b'\'' => '\'',
b'"' => '"',
b'\r' | b'\n' => loop {
let ch = s.chars().next().unwrap_or_default();
if ch.is_whitespace() {
s = &s[ch.len_utf8()..];
} else {
continue 'outer;
}
},
_ => unreachable!("invalid escape"),
}
}
b'\r' => {
// bare CR not permitted
s = &s[2..];
'\n'
}
_ => {
let ch = s.chars().next().unwrap_or_default();
s = &s[ch.len_utf8()..];
ch
}
};
content.push(ch);
}
content.into_bytes()
}
fn parse_lit_str_raw(s: &[u8]) -> Vec<u8> {
let mut pounds = 0;
while byte(s, pounds) == b'#' {
pounds += 1;
}
let close = s
.iter()
.rposition(|&b| b == b'"')
.expect("had a string without trailing \"");
s[pounds + 1..close].to_owned()
}
fn parse_lit_byte_str_cooked(mut v: &[u8]) -> Vec<u8> {
let mut out = Vec::new();
'outer: loop {
let byte = match byte(v, 0) {
b'"' => break,
b'\\' => {
let b = byte(v, 1);
v = &v[2..];
match b {
b'x' => {
let (byte, rest) = backslash_x(v);
v = rest;
byte
}
b'n' => b'\n',
b'r' => b'\r',
b't' => b'\t',
b'\\' => b'\\',
b'0' => b'\0',
b'\'' => b'\'',
b'"' => b'"',
b'\r' | b'\n' => loop {
let byte = byte(v, 0);
let ch = char::from_u32(u32::from(byte)).expect("invalid byte");
if ch.is_whitespace() {
v = &v[1..];
} else {
continue 'outer;
}
},
_ => unreachable!("invalid escape"),
}
}
b'\r' => {
// bare CR not permitted
v = &v[2..];
b'\n'
}
b => {
v = &v[1..];
b
}
};
out.push(byte);
}
out
}
fn backslash_x<S>(s: &S) -> (u8, &S)
where
S: Index<RangeFrom<usize>, Output = S> + AsRef<[u8]> + ?Sized,
{
let mut ch = 0;
let b0 = byte(s, 0);
let b1 = byte(s, 1);
ch += 0x10 * (b0 - b'0');
ch += match b1 {
b'0'..=b'9' => b1 - b'0',
b'a'..=b'f' => 10 + (b1 - b'a'),
b'A'..=b'F' => 10 + (b1 - b'A'),
_ => unreachable!("invalid hex escape"),
};
(ch, &s[2..])
}
fn backslash_u(mut s: &str) -> (char, &str) {
s = &s[1..];
let mut ch = 0;
let mut digits = 0;
loop {
let b = byte(s, 0);
let digit = match b {
b'0'..=b'9' => b - b'0',
b'a'..=b'f' => 10 + b - b'a',
b'A'..=b'F' => 10 + b - b'A',
b'_' if digits > 0 => {
s = &s[1..];
continue;
}
b'}' if digits != 0 => break,
_ => unreachable!("invalid unicode escape"),
};
ch *= 0x10;
ch += u32::from(digit);
digits += 1;
s = &s[1..];
}
s = &s[1..];
(
char::from_u32(ch).expect("invalid unicode escape passed by compiler"),
s,
)
}

129
third_party/rust/time-macros/src/lib.rs поставляемый Normal file
Просмотреть файл

@ -0,0 +1,129 @@
#![deny(
anonymous_parameters,
clippy::all,
const_err,
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, clippy::redundant_pub_crate)]
#[macro_use]
mod quote;
mod date;
mod datetime;
mod error;
mod format_description;
mod helpers;
mod offset;
mod serde_format_description;
mod time;
mod to_tokens;
use proc_macro::{TokenStream, TokenTree};
use self::error::Error;
macro_rules! impl_macros {
($($name:ident)*) => {$(
#[proc_macro]
pub fn $name(input: TokenStream) -> TokenStream {
use crate::to_tokens::ToTokenTree;
let mut iter = input.into_iter().peekable();
match $name::parse(&mut iter) {
Ok(value) => match iter.peek() {
Some(tree) => Error::UnexpectedToken { tree: tree.clone() }.to_compile_error(),
None => TokenStream::from(value.into_token_tree()),
},
Err(err) => err.to_compile_error(),
}
}
)*};
}
impl_macros![date datetime offset time];
// TODO Gate this behind the the `formatting` or `parsing` feature flag when weak dependency
// features land.
#[proc_macro]
pub fn format_description(input: TokenStream) -> TokenStream {
(|| {
let (span, string) = helpers::get_string_literal(input)?;
let items = format_description::parse(&string, span)?;
Ok(quote! {{
const DESCRIPTION: &[::time::format_description::FormatItem<'_>] = &[#S(
items
.into_iter()
.map(|item| quote! { #S(item), })
.collect::<TokenStream>()
)];
DESCRIPTION
}})
})()
.unwrap_or_else(|err: Error| err.to_compile_error())
}
#[proc_macro]
pub fn serde_format_description(input: TokenStream) -> TokenStream {
(|| {
let mut tokens = input.into_iter().peekable();
// First, an identifier (the desired module name)
let mod_name = match tokens.next() {
Some(TokenTree::Ident(ident)) => Ok(ident),
Some(tree) => Err(Error::UnexpectedToken { tree }),
None => Err(Error::UnexpectedEndOfInput),
}?;
// Followed by a comma
helpers::consume_punct(',', &mut tokens)?;
// Then, the type to create serde serializers for (e.g., `OffsetDateTime`).
let formattable = match tokens.next() {
Some(tree @ TokenTree::Ident(_)) => Ok(tree),
Some(tree) => Err(Error::UnexpectedToken { tree }),
None => Err(Error::UnexpectedEndOfInput),
}?;
// Another comma
helpers::consume_punct(',', &mut tokens)?;
// Then, a string literal.
let (span, format_string) = helpers::get_string_literal(tokens.collect())?;
let items = format_description::parse(&format_string, span)?;
let items: TokenStream = items.into_iter().map(|item| quote! { #S(item), }).collect();
Ok(serde_format_description::build(
mod_name,
items,
formattable,
&String::from_utf8_lossy(&format_string),
))
})()
.unwrap_or_else(|err: Error| err.to_compile_error_standalone())
}

95
third_party/rust/time-macros/src/offset.rs поставляемый Normal file
Просмотреть файл

@ -0,0 +1,95 @@
use std::iter::Peekable;
use proc_macro::{token_stream, Span, TokenTree};
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,
pub(crate) seconds: i8,
}
pub(crate) fn parse(chars: &mut Peekable<token_stream::IntoIter>) -> Result<Offset, Error> {
if consume_any_ident(&["utc", "UTC"], chars).is_ok() {
return Ok(Offset {
hours: 0,
minutes: 0,
seconds: 0,
});
}
let sign = if consume_punct('+', chars).is_ok() {
1
} else if consume_punct('-', chars).is_ok() {
-1
} else if let Some(tree) = chars.next() {
return Err(Error::UnexpectedToken { tree });
} else {
return Err(Error::MissingComponent {
name: "sign",
span_start: None,
span_end: None,
});
};
let (hours_span, hours) = consume_number::<i8>("hour", chars)?;
let (mut minutes_span, mut minutes) = (Span::mixed_site(), 0);
let (mut seconds_span, mut seconds) = (Span::mixed_site(), 0);
if consume_punct(':', chars).is_ok() {
let min = consume_number::<i8>("minute", chars)?;
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 {
Err(Error::InvalidComponent {
name: "hour",
value: hours.to_string(),
span_start: Some(hours_span),
span_end: Some(hours_span),
})
} else if minutes >= 60 {
Err(Error::InvalidComponent {
name: "minute",
value: minutes.to_string(),
span_start: Some(minutes_span),
span_end: Some(minutes_span),
})
} else if seconds >= 60 {
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),
);
OFFSET
}}
}
}

132
third_party/rust/time-macros/src/quote.rs поставляемый Normal file
Просмотреть файл

@ -0,0 +1,132 @@
macro_rules! quote {
() => (::proc_macro::TokenStream::new());
($($x:tt)*) => {{
let mut ts = ::proc_macro::TokenStream::new();
let ts_mut = &mut ts;
quote_inner!(ts_mut $($x)*);
ts
}};
}
macro_rules! quote_append {
($ts:ident $($x:tt)*) => {{
quote_inner!($ts $($x)*);
}};
}
macro_rules! quote_group {
({ $($x:tt)* }) => {
::proc_macro::TokenTree::Group(::proc_macro::Group::new(
::proc_macro::Delimiter::Brace,
quote!($($x)*)
))
};
}
macro_rules! sym {
($ts:ident $x:tt $y:tt) => {
$ts.extend([
::proc_macro::TokenTree::from(::proc_macro::Punct::new(
$x,
::proc_macro::Spacing::Joint,
)),
::proc_macro::TokenTree::from(::proc_macro::Punct::new(
$y,
::proc_macro::Spacing::Alone,
)),
]);
};
($ts:ident $x:tt) => {
$ts.extend([::proc_macro::TokenTree::from(::proc_macro::Punct::new(
$x,
::proc_macro::Spacing::Alone,
))]);
};
}
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)*); };
// Identifier
($ts:ident $i:ident $($tail:tt)*) => {
$ts.extend([::proc_macro::TokenTree::from(::proc_macro::Ident::new(
&stringify!($i),
::proc_macro::Span::mixed_site(),
))]);
quote_inner!($ts $($tail)*);
};
// Literal
($ts:ident $l:literal $($tail:tt)*) => {
$ts.extend([::proc_macro::TokenTree::from(::proc_macro::Literal::string(&$l))]);
quote_inner!($ts $($tail)*);
};
// Lifetime
($ts:ident $l:lifetime $($tail:tt)*) => {
$ts.extend([
::proc_macro::TokenTree::from(
::proc_macro::Punct::new('\'', ::proc_macro::Spacing::Joint)
),
::proc_macro::TokenTree::from(::proc_macro::Ident::new(
stringify!($l).trim_start_matches(|c| c == '\''),
::proc_macro::Span::mixed_site(),
)),
]);
quote_inner!($ts $($tail)*);
};
// Groups
($ts:ident ($($inner:tt)*) $($tail:tt)*) => {
$ts.extend([::proc_macro::TokenTree::Group(::proc_macro::Group::new(
::proc_macro::Delimiter::Parenthesis,
quote!($($inner)*)
))]);
quote_inner!($ts $($tail)*);
};
($ts:ident [$($inner:tt)*] $($tail:tt)*) => {
$ts.extend([::proc_macro::TokenTree::Group(::proc_macro::Group::new(
::proc_macro::Delimiter::Bracket,
quote!($($inner)*)
))]);
quote_inner!($ts $($tail)*);
};
($ts:ident {$($inner:tt)*} $($tail:tt)*) => {
$ts.extend([::proc_macro::TokenTree::Group(::proc_macro::Group::new(
::proc_macro::Delimiter::Brace,
quote!($($inner)*)
))]);
quote_inner!($ts $($tail)*);
};
// Interpolated values
// TokenTree by default
($ts:ident #($e:expr) $($tail:tt)*) => {
$ts.extend([$crate::to_tokens::ToTokenTree::into_token_tree($e)]);
quote_inner!($ts $($tail)*);
};
// Allow a TokenStream by request. It's more expensive, so avoid if possible.
($ts:ident #S($e:expr) $($tail:tt)*) => {
$crate::to_tokens::ToTokenStream::append_to($e, $ts);
quote_inner!($ts $($tail)*);
};
}

131
third_party/rust/time-macros/src/serde_format_description.rs поставляемый Normal file
Просмотреть файл

@ -0,0 +1,131 @@
use proc_macro::{Ident, TokenStream, TokenTree};
use crate::to_tokens;
pub(crate) fn build(
mod_name: Ident,
items: impl to_tokens::ToTokenStream,
ty: TokenTree,
format_string: &str,
) -> TokenStream {
let ty_s = &*ty.to_string();
let visitor = quote! {
struct Visitor;
struct OptionVisitor;
impl<'a> ::serde::de::Visitor<'a> for Visitor {
type Value = __TimeSerdeType;
fn expecting(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
write!(
f,
concat!(
"a(n) `",
#(ty_s),
"` in the format \"",
#(format_string),
"\"",
)
)
}
fn visit_str<E: ::serde::de::Error>(
self,
value: &str
) -> Result<__TimeSerdeType, E> {
__TimeSerdeType::parse(value, &DESCRIPTION).map_err(E::custom)
}
}
impl<'a> ::serde::de::Visitor<'a> for OptionVisitor {
type Value = Option<__TimeSerdeType>;
fn expecting(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
write!(
f,
concat!(
"an `Option<",
#(ty_s),
">` in the format \"",
#(format_string),
"\"",
)
)
}
fn visit_some<D: ::serde::de::Deserializer<'a>>(
self,
deserializer: D
) -> Result<Option<__TimeSerdeType>, D::Error> {
deserializer
.deserialize_any(Visitor)
.map(Some)
}
fn visit_none<E: ::serde::de::Error>(
self
) -> Result<Option<__TimeSerdeType>, E> {
Ok(None)
}
}
};
let primary_fns = quote! {
pub fn serialize<S: ::serde::Serializer>(
datetime: &__TimeSerdeType,
serializer: S,
) -> Result<S::Ok, S::Error> {
use ::serde::Serialize;
datetime
.format(&DESCRIPTION)
.map_err(::time::error::Format::into_invalid_serde_value::<S>)?
.serialize(serializer)
}
pub fn deserialize<'a, D: ::serde::Deserializer<'a>>(
deserializer: D
) -> Result<__TimeSerdeType, D::Error> {
use ::serde::Deserialize;
deserializer.deserialize_any(Visitor)
}
};
let options_fns = quote! {
pub fn serialize<S: ::serde::Serializer>(
option: &Option<__TimeSerdeType>,
serializer: S,
) -> Result<S::Ok, S::Error> {
use ::serde::Serialize;
option.map(|datetime| datetime.format(&DESCRIPTION))
.transpose()
.map_err(::time::error::Format::into_invalid_serde_value::<S>)?
.serialize(serializer)
}
pub fn deserialize<'a, D: ::serde::Deserializer<'a>>(
deserializer: D
) -> Result<Option<__TimeSerdeType>, D::Error> {
use ::serde::Deserialize;
deserializer.deserialize_option(OptionVisitor)
}
};
quote! {
mod #(mod_name) {
use ::time::#(ty) as __TimeSerdeType;
const DESCRIPTION: &[::time::format_description::FormatItem<'_>] = &[#S(items)];
#S(visitor)
#S(primary_fns)
pub(super) mod option {
use super::{DESCRIPTION, OptionVisitor, Visitor, __TimeSerdeType};
#S(options_fns)
}
}
}
}

118
third_party/rust/time-macros/src/time.rs поставляемый Normal file
Просмотреть файл

@ -0,0 +1,118 @@
use std::iter::Peekable;
use proc_macro::{token_stream, Span, TokenTree};
use crate::helpers::{consume_any_ident, consume_number, consume_punct};
use crate::to_tokens::ToTokenTree;
use crate::Error;
enum Period {
Am,
Pm,
_24,
}
pub(crate) struct Time {
pub(crate) hour: u8,
pub(crate) minute: u8,
pub(crate) second: u8,
pub(crate) nanosecond: u32,
}
pub(crate) fn parse(chars: &mut Peekable<token_stream::IntoIter>) -> Result<Time, Error> {
fn consume_period(chars: &mut Peekable<token_stream::IntoIter>) -> (Option<Span>, Period) {
if let Ok(span) = consume_any_ident(&["am", "AM"], chars) {
(Some(span), Period::Am)
} else if let Ok(span) = consume_any_ident(&["pm", "PM"], chars) {
(Some(span), Period::Pm)
} else {
(None, Period::_24)
}
}
let (hour_span, hour) = consume_number("hour", chars)?;
let ((minute_span, minute), (second_span, second), (period_span, period)) =
match consume_period(chars) {
// Nothing but the 12-hour clock hour and AM/PM
(period_span @ Some(_), period) => (
(Span::mixed_site(), 0),
(Span::mixed_site(), 0.),
(period_span, period),
),
(None, _) => {
consume_punct(':', chars)?;
let (minute_span, minute) = consume_number::<u8>("minute", chars)?;
let (second_span, second): (_, f64) = if consume_punct(':', chars).is_ok() {
consume_number("second", chars)?
} else {
(Span::mixed_site(), 0.)
};
let (period_span, period) = consume_period(chars);
(
(minute_span, minute),
(second_span, second),
(period_span, period),
)
}
};
let hour = match (hour, period) {
(0, Period::Am | Period::Pm) => {
return Err(Error::InvalidComponent {
name: "hour",
value: hour.to_string(),
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 >= 24 {
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 >= 60 {
Err(Error::InvalidComponent {
name: "minute",
value: minute.to_string(),
span_start: Some(minute_span),
span_end: Some(minute_span),
})
} else if second >= 60. {
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() * 1_000_000_000.).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),
);
TIME
}}
}
}

68
third_party/rust/time-macros/src/to_tokens.rs поставляемый Normal file
Просмотреть файл

@ -0,0 +1,68 @@
use proc_macro::{Group, Ident, Literal, Punct, Span, TokenStream, TokenTree};
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) {
ts.extend([self.into_token_tree()])
}
}
impl ToTokenTree for bool {
fn into_token_tree(self) -> TokenTree {
let lit = if self { "true" } else { "false" };
TokenTree::Ident(Ident::new(lit, Span::mixed_site()))
}
}
impl ToTokenStream for TokenStream {
fn append_to(self, ts: &mut TokenStream) {
ts.extend(self)
}
}
impl ToTokenTree for TokenTree {
fn into_token_tree(self) -> TokenTree {
self
}
}
impl ToTokenTree for &str {
fn into_token_tree(self) -> TokenTree {
TokenTree::Literal(Literal::string(self))
}
}
macro_rules! impl_for_tree_types {
($($type:ty)*) => {$(
impl ToTokenTree for $type {
fn into_token_tree(self) -> TokenTree {
TokenTree::from(self)
}
}
)*};
}
impl_for_tree_types![Ident Literal Group Punct];
macro_rules! impl_for_int {
($($type:ty => $method:ident)*) => {$(
impl ToTokenTree for $type {
fn into_token_tree(self) -> TokenTree {
TokenTree::from(Literal::$method(self))
}
}
)*};
}
impl_for_int! {
i8 => i8_unsuffixed
u8 => u8_unsuffixed
u16 => u16_unsuffixed
i32 => i32_unsuffixed
u32 => u32_unsuffixed
}

2
third_party/rust/time/.cargo-checksum.json поставляемый

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

114
third_party/rust/time/Cargo.toml поставляемый
Просмотреть файл

@ -3,36 +3,102 @@
# 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
# to registry (e.g., crates.io) dependencies.
#
# If you believe there's an error in this file please file an
# issue against the rust-lang/cargo repository. If you're
# editing this file be aware that the upstream Cargo.toml
# will likely look very different (and much more reasonable)
# 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 = "2018"
name = "time"
version = "0.1.43"
authors = ["The Rust Project Developers"]
exclude = [".github", "benches"]
description = "Utilities for working with time-related functions in Rust.\n"
homepage = "https://github.com/time-rs/time"
documentation = "https://docs.rs/time/~0.1"
version = "0.3.9"
authors = ["Jacob Pratt <open-source@jhpratt.dev>", "Time contributors"]
include = ["src/**/*", "LICENSE-*", "README.md", "!src/tests.rs"]
description = "Date and time library. Fully interoperable with the standard library. Mostly compatible with #![no_std]."
homepage = "https://time-rs.github.io"
readme = "README.md"
license = "MIT/Apache-2.0"
keywords = ["date", "time", "calendar", "duration"]
categories = ["date-and-time", "no-std", "parser-implementations", "value-formatting"]
license = "MIT OR Apache-2.0"
repository = "https://github.com/time-rs/time"
[dependencies.libc]
version = "0.2.69"
resolver = "2"
[package.metadata.docs.rs]
all-features = true
rustdoc-args = ["--cfg", "__time_03_docs"]
targets = ["x86_64-unknown-linux-gnu"]
[profile.dev]
debug = 0
[dependencies.rustc-serialize]
version = "0.3"
[lib]
bench = false
[[bench]]
name = "benchmarks"
path = "benchmarks/main.rs"
harness = false
[dependencies.itoa]
version = "1.0.1"
optional = true
[dev-dependencies.log]
version = "0.4"
[dev-dependencies.winapi]
version = "0.3.0"
features = ["std", "processthreadsapi", "winbase"]
[target."cfg(windows)".dependencies.winapi]
version = "0.3.0"
features = ["std", "minwinbase", "minwindef", "ntdef", "profileapi", "sysinfoapi", "timezoneapi"]
[dependencies.quickcheck-dep]
version = "1.0.3"
optional = true
default-features = false
package = "quickcheck"
[dependencies.rand]
version = "0.8.4"
optional = true
default-features = false
[dependencies.serde]
version = "1.0.126"
optional = true
default-features = false
[dependencies.time-macros]
version = "=0.2.4"
optional = true
[dev-dependencies.quickcheck_macros]
version = "1.0.0"
[dev-dependencies.rand]
version = "0.8.4"
default-features = false
[dev-dependencies.serde]
version = "1.0.126"
features = ["derive"]
default-features = false
[dev-dependencies.serde_json]
version = "1.0.68"
[dev-dependencies.serde_test]
version = "1.0.126"
[features]
alloc = []
default = ["std"]
formatting = ["itoa", "std"]
large-dates = ["time-macros/large-dates"]
local-offset = ["std"]
macros = ["time-macros"]
parsing = []
quickcheck = ["quickcheck-dep", "alloc"]
serde-human-readable = ["serde", "formatting", "parsing"]
serde-well-known = ["serde/alloc", "formatting", "parsing"]
std = ["alloc"]
[target."cfg(__ui_tests)".dev-dependencies.trybuild]
version = "=1.0.34"
[target."cfg(bench)".dev-dependencies.criterion]
version = "0.3.5"
[target."cfg(bench)".dev-dependencies.criterion-cycles-per-byte]
version = "0.1.2"
[target."cfg(target_family = \"unix\")".dependencies.libc]
version = "0.2.98"
[target."cfg(target_family = \"unix\")".dependencies.num_threads]
version = "0.1.2"

202
third_party/rust/time/LICENSE-Apache поставляемый Normal file
Просмотреть файл

@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright 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.

38
third_party/rust/time/LICENSE-MIT поставляемый
Просмотреть файл

@ -1,25 +1,19 @@
Copyright (c) 2014 The Rust Project Developers
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:
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 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.
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.

59
third_party/rust/time/README.md поставляемый
Просмотреть файл

@ -1,31 +1,48 @@
time
====
# time
Utilities for working with time-related functions in Rust
[![minimum rustc: 1.53](https://img.shields.io/badge/minimum%20rustc-1.53-yellowgreen?logo=rust&style=flat-square)](https://www.whatrustisit.com)
[![version](https://img.shields.io/crates/v/time?color=blue&logo=rust&style=flat-square)](https://crates.io/crates/time)
[![build status](https://img.shields.io/github/workflow/status/time-rs/time/Build/main?style=flat-square)](https://github.com/time-rs/time/actions)
[![codecov](https://codecov.io/gh/time-rs/time/branch/main/graph/badge.svg?token=yt4XSmQNKQ)](https://codecov.io/gh/time-rs/time)
[![build status](https://github.com/time-rs/time/workflows/Build/badge.svg?branch=v0.1)](https://github.com/time-rs/time/actions?query=branch%3Av0.1)
[![Documentation](https://docs.rs/time/badge.svg?version=0.1)](https://docs.rs/time/~0.1)
![rustc 1.21.0](https://img.shields.io/badge/rustc-1.21.0-blue)
Documentation:
- [latest release](https://docs.rs/time)
- [main branch](https://time-rs.github.io/api/time)
- [book](https://time-rs.github.io/book)
## time v0.1.x is Deprecated
## Minimum Rust version policy
The 0.1.x series of this library is deprecated and in maintenance mode. No new
features will be added. Active development now occurs in the 0.2.x series.
The time crate is guaranteed to compile with any release of rustc from the past six months.
Optional feature flags that enable interoperability with third-party crates (e.g. rand)
follow the policy of that crate if stricter.
If you need additional functionality that this crate does not provide, check
out the [`chrono`](https://github.com/chronotope/chrono) crate.
## Contributing
## Usage
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.
Internal documentation can be viewed [here](https://time-rs.github.io/internal-api/time).
Put this in your `Cargo.toml`:
If you have any questions, feel free to use [Discussions] or [Codestream]. There are a few notes
inline with Codestream, though questions can be asked directly! As a bonus, they are visible and
searchable for others. Feedback (prior to opening a pull request) can be provided with Codestream
and [VS Live Share]. Don't hesitate to ask questions — that's what I'm here for!
```toml
[dependencies]
time = "0.1"
```
If using Codestream, just open up a local copy of this repository. It _should_ add you
automatically.
And this in your crate root:
[Discussions]: https://github.com/time-rs/time/discussions
[Codestream]: https://codestream.com
[VS Live Share]: https://code.visualstudio.com/learn/collaboration/live-share
```rust
extern crate time;
```
## License
This project is licensed under either of
- [Apache License, Version 2.0](https://github.com/time-rs/time/blob/main/LICENSE-Apache)
- [MIT license](https://github.com/time-rs/time/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.

1032
third_party/rust/time/src/date.rs поставляемый Normal file

Разница между файлами не показана из-за своего большого размера Загрузить разницу

1584
third_party/rust/time/src/duration.rs поставляемый

Разница между файлами не показана из-за своего большого размера Загрузить разницу

93
third_party/rust/time/src/error/component_range.rs поставляемый Normal file
Просмотреть файл

@ -0,0 +1,93 @@
//! Component range error
use core::convert::TryFrom;
use core::fmt;
use crate::error;
/// An error type indicating that a component provided to a method was out of range, causing a
/// failure.
// i64 is the narrowest type fitting all use cases. This eliminates the need for a type parameter.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct ComponentRange {
/// Name of the component.
pub(crate) name: &'static str,
/// Minimum allowed value, inclusive.
pub(crate) minimum: i64,
/// Maximum allowed value, inclusive.
pub(crate) maximum: i64,
/// Value that was provided.
pub(crate) value: i64,
/// The minimum and/or maximum value is conditional on the value of other
/// parameters.
pub(crate) conditional_range: bool,
}
impl ComponentRange {
/// Obtain the name of the component whose value was out of range.
pub const fn name(self) -> &'static str {
self.name
}
/// Whether the value's permitted range is conditional, i.e. whether an input with this
/// value could have succeeded if the values of other components were different.
pub const fn is_conditional(self) -> bool {
self.conditional_range
}
}
impl fmt::Display for ComponentRange {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"{} must be in the range {}..={}",
self.name, self.minimum, self.maximum
)?;
if self.conditional_range {
f.write_str(", given values of other parameters")?;
}
Ok(())
}
}
impl From<ComponentRange> for crate::Error {
fn from(original: ComponentRange) -> Self {
Self::ComponentRange(original)
}
}
impl TryFrom<crate::Error> for ComponentRange {
type Error = error::DifferentVariant;
fn try_from(err: crate::Error) -> Result<Self, Self::Error> {
match err {
crate::Error::ComponentRange(err) => Ok(err),
_ => Err(error::DifferentVariant),
}
}
}
/// **This trait implementation is deprecated and will be removed in a future breaking release.**
#[cfg(feature = "serde")]
impl serde::de::Expected for ComponentRange {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"a value in the range {}..={}",
self.minimum, self.maximum
)
}
}
#[cfg(feature = "serde")]
impl ComponentRange {
/// Convert the error to a deserialization error.
pub(crate) fn into_de_error<E: serde::de::Error>(self) -> E {
E::invalid_value(serde::de::Unexpected::Signed(self.value), &self)
}
}
#[cfg(feature = "std")]
impl std::error::Error for ComponentRange {}

37
third_party/rust/time/src/error/conversion_range.rs поставляемый Normal file
Просмотреть файл

@ -0,0 +1,37 @@
//! Conversion range error
use core::convert::TryFrom;
use core::fmt;
use crate::error;
/// An error type indicating that a conversion failed because the target type could not store the
/// initial value.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ConversionRange;
impl fmt::Display for ConversionRange {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("Source value is out of range for the target type")
}
}
#[cfg(feature = "std")]
impl std::error::Error for ConversionRange {}
impl From<ConversionRange> for crate::Error {
fn from(err: ConversionRange) -> Self {
Self::ConversionRange(err)
}
}
impl TryFrom<crate::Error> for ConversionRange {
type Error = error::DifferentVariant;
fn try_from(err: crate::Error) -> Result<Self, Self::Error> {
match err {
crate::Error::ConversionRange(err) => Ok(err),
_ => Err(error::DifferentVariant),
}
}
}

35
third_party/rust/time/src/error/different_variant.rs поставляемый Normal file
Просмотреть файл

@ -0,0 +1,35 @@
//! Different variant error
use core::convert::TryFrom;
use core::fmt;
/// An error type indicating that a [`TryFrom`](core::convert::TryFrom) call failed because the
/// original value was of a different variant.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct DifferentVariant;
impl fmt::Display for DifferentVariant {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "value was of a different variant than required")
}
}
#[cfg(feature = "std")]
impl std::error::Error for DifferentVariant {}
impl From<DifferentVariant> for crate::Error {
fn from(err: DifferentVariant) -> Self {
Self::DifferentVariant(err)
}
}
impl TryFrom<crate::Error> for DifferentVariant {
type Error = Self;
fn try_from(err: crate::Error) -> Result<Self, Self::Error> {
match err {
crate::Error::DifferentVariant(err) => Ok(err),
_ => Err(Self),
}
}
}

97
third_party/rust/time/src/error/format.rs поставляемый Normal file
Просмотреть файл

@ -0,0 +1,97 @@
//! Error formatting a struct
use core::convert::TryFrom;
use core::fmt;
use std::io;
use crate::error;
/// An error occurred when formatting.
#[non_exhaustive]
#[allow(missing_copy_implementations)]
#[cfg_attr(__time_03_docs, doc(cfg(feature = "formatting")))]
#[derive(Debug)]
pub enum Format {
/// The type being formatted does not contain sufficient information to format a component.
#[non_exhaustive]
InsufficientTypeInformation,
/// The component named has a value that cannot be formatted into the requested format.
///
/// This variant is only returned when using well-known formats.
InvalidComponent(&'static str),
/// A value of `std::io::Error` was returned internally.
StdIo(io::Error),
}
impl fmt::Display for Format {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::InsufficientTypeInformation => f.write_str(
"The type being formatted does not contain sufficient information to format a \
component.",
),
Self::InvalidComponent(component) => write!(
f,
"The {} component cannot be formatted into the requested format.",
component
),
Self::StdIo(err) => err.fmt(f),
}
}
}
impl From<io::Error> for Format {
fn from(err: io::Error) -> Self {
Self::StdIo(err)
}
}
impl TryFrom<Format> for io::Error {
type Error = error::DifferentVariant;
fn try_from(err: Format) -> Result<Self, Self::Error> {
match err {
Format::StdIo(err) => Ok(err),
_ => Err(error::DifferentVariant),
}
}
}
#[cfg(feature = "std")]
impl std::error::Error for Format {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match *self {
Self::InsufficientTypeInformation | Self::InvalidComponent(_) => None,
Self::StdIo(ref err) => Some(err),
}
}
}
#[cfg_attr(__time_03_docs, doc(cfg(feature = "formatting")))]
impl From<Format> for crate::Error {
fn from(original: Format) -> Self {
Self::Format(original)
}
}
#[cfg_attr(__time_03_docs, doc(cfg(feature = "formatting")))]
impl TryFrom<crate::Error> for Format {
type Error = error::DifferentVariant;
fn try_from(err: crate::Error) -> Result<Self, Self::Error> {
match err {
crate::Error::Format(err) => Ok(err),
_ => Err(error::DifferentVariant),
}
}
}
#[cfg(feature = "serde")]
impl Format {
/// Obtain an error type for the serializer.
#[doc(hidden)] // Exposed only for the `declare_format_string` macro
pub fn into_invalid_serde_value<S: serde::Serializer>(self) -> S::Error {
use serde::ser::Error;
S::Error::custom(format!("{}", self))
}
}

39
third_party/rust/time/src/error/indeterminate_offset.rs поставляемый Normal file
Просмотреть файл

@ -0,0 +1,39 @@
//! Indeterminate offset
use core::convert::TryFrom;
use core::fmt;
use crate::error;
/// The system's UTC offset could not be determined at the given datetime.
#[cfg_attr(__time_03_docs, doc(cfg(feature = "local-offset")))]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct IndeterminateOffset;
impl fmt::Display for IndeterminateOffset {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("The system's UTC offset could not be determined")
}
}
#[cfg(feature = "std")]
impl std::error::Error for IndeterminateOffset {}
#[cfg_attr(__time_03_docs, doc(cfg(feature = "local-offset")))]
impl From<IndeterminateOffset> for crate::Error {
fn from(err: IndeterminateOffset) -> Self {
Self::IndeterminateOffset(err)
}
}
#[cfg_attr(__time_03_docs, doc(cfg(feature = "std")))]
impl TryFrom<crate::Error> for IndeterminateOffset {
type Error = error::DifferentVariant;
fn try_from(err: crate::Error) -> Result<Self, Self::Error> {
match err {
crate::Error::IndeterminateOffset(err) => Ok(err),
_ => Err(error::DifferentVariant),
}
}
}

95
third_party/rust/time/src/error/invalid_format_description.rs поставляемый Normal file
Просмотреть файл

@ -0,0 +1,95 @@
//! Invalid format description
use alloc::string::String;
use core::convert::TryFrom;
use core::fmt;
use crate::error;
/// The format description provided was not valid.
#[cfg_attr(
__time_03_docs,
doc(cfg(all(any(feature = "formatting", feature = "parsing"), feature = "alloc")))
)]
#[non_exhaustive]
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum InvalidFormatDescription {
/// There was a bracket pair that was opened but not closed.
#[non_exhaustive]
UnclosedOpeningBracket {
/// The zero-based index of the opening bracket.
index: usize,
},
/// A component name is not valid.
#[non_exhaustive]
InvalidComponentName {
/// The name of the invalid component name.
name: String,
/// The zero-based index the component name starts at.
index: usize,
},
/// A modifier is not valid.
#[non_exhaustive]
InvalidModifier {
/// The value of the invalid modifier.
value: String,
/// The zero-based index the modifier starts at.
index: usize,
},
/// A component name is missing.
#[non_exhaustive]
MissingComponentName {
/// The zero-based index where the component name should start.
index: usize,
},
}
#[cfg_attr(
__time_03_docs,
doc(cfg(all(any(feature = "formatting", feature = "parsing"), feature = "alloc")))
)]
impl From<InvalidFormatDescription> for crate::Error {
fn from(original: InvalidFormatDescription) -> Self {
Self::InvalidFormatDescription(original)
}
}
#[cfg_attr(
__time_03_docs,
doc(cfg(all(any(feature = "formatting", feature = "parsing"), feature = "alloc")))
)]
impl TryFrom<crate::Error> for InvalidFormatDescription {
type Error = error::DifferentVariant;
fn try_from(err: crate::Error) -> Result<Self, Self::Error> {
match err {
crate::Error::InvalidFormatDescription(err) => Ok(err),
_ => Err(error::DifferentVariant),
}
}
}
impl fmt::Display for InvalidFormatDescription {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use InvalidFormatDescription::*;
match self {
UnclosedOpeningBracket { index } => {
write!(f, "unclosed opening bracket at byte index {}", index)
}
InvalidComponentName { name, index } => write!(
f,
"invalid component name `{}` at byte index {}",
name, index
),
InvalidModifier { value, index } => {
write!(f, "invalid modifier `{}` at byte index {}", value, index)
}
MissingComponentName { index } => {
write!(f, "missing component name at byte index {}", index)
}
}
}
}
#[cfg(feature = "std")]
impl std::error::Error for InvalidFormatDescription {}

35
third_party/rust/time/src/error/invalid_variant.rs поставляемый Normal file
Просмотреть файл

@ -0,0 +1,35 @@
//! Invalid variant error
use core::convert::TryFrom;
use core::fmt;
/// An error type indicating that a [`FromStr`](core::str::FromStr) call failed because the value
/// was not a valid variant.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct InvalidVariant;
impl fmt::Display for InvalidVariant {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "value was not a valid variant")
}
}
#[cfg(feature = "std")]
impl std::error::Error for InvalidVariant {}
impl From<InvalidVariant> for crate::Error {
fn from(err: InvalidVariant) -> Self {
Self::InvalidVariant(err)
}
}
impl TryFrom<crate::Error> for InvalidVariant {
type Error = crate::error::DifferentVariant;
fn try_from(err: crate::Error) -> Result<Self, Self::Error> {
match err {
crate::Error::InvalidVariant(err) => Ok(err),
_ => Err(crate::error::DifferentVariant),
}
}
}

112
third_party/rust/time/src/error/mod.rs поставляемый Normal file
Просмотреть файл

@ -0,0 +1,112 @@
//! Various error types returned by methods in the time crate.
mod component_range;
mod conversion_range;
mod different_variant;
#[cfg(feature = "formatting")]
mod format;
#[cfg(feature = "local-offset")]
mod indeterminate_offset;
#[cfg(all(any(feature = "formatting", feature = "parsing"), feature = "alloc"))]
mod invalid_format_description;
mod invalid_variant;
#[cfg(feature = "parsing")]
mod parse;
#[cfg(feature = "parsing")]
mod parse_from_description;
#[cfg(feature = "parsing")]
mod try_from_parsed;
use core::fmt;
pub use component_range::ComponentRange;
pub use conversion_range::ConversionRange;
pub use different_variant::DifferentVariant;
#[cfg(feature = "formatting")]
pub use format::Format;
#[cfg(feature = "local-offset")]
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;
/// 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 {
ConversionRange(ConversionRange),
ComponentRange(ComponentRange),
#[cfg(feature = "local-offset")]
IndeterminateOffset(IndeterminateOffset),
#[cfg(feature = "formatting")]
Format(Format),
#[cfg(feature = "parsing")]
ParseFromDescription(ParseFromDescription),
#[cfg(feature = "parsing")]
#[non_exhaustive]
UnexpectedTrailingCharacters,
#[cfg(feature = "parsing")]
TryFromParsed(TryFromParsed),
#[cfg(all(any(feature = "formatting", feature = "parsing"), feature = "alloc"))]
InvalidFormatDescription(InvalidFormatDescription),
DifferentVariant(DifferentVariant),
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"),
#[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),
}
}
}
#[cfg(feature = "std")]
impl std::error::Error for Error {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
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,
#[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),
}
}
}

105
third_party/rust/time/src/error/parse.rs поставляемый Normal file
Просмотреть файл

@ -0,0 +1,105 @@
//! Error that occurred at some stage of parsing
use core::convert::TryFrom;
use core::fmt;
use crate::error::{self, ParseFromDescription, TryFromParsed};
/// An error that occurred at some stage of parsing.
#[cfg_attr(__time_03_docs, doc(cfg(feature = "parsing")))]
#[allow(variant_size_differences)]
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Parse {
#[allow(clippy::missing_docs_in_private_items)]
TryFromParsed(TryFromParsed),
#[allow(clippy::missing_docs_in_private_items)]
ParseFromDescription(ParseFromDescription),
/// The input should have ended, but there were characters remaining.
#[non_exhaustive]
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"),
}
}
}
#[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,
}
}
}
#[cfg_attr(__time_03_docs, doc(cfg(feature = "parsing")))]
impl From<TryFromParsed> for Parse {
fn from(err: TryFromParsed) -> Self {
Self::TryFromParsed(err)
}
}
#[cfg_attr(__time_03_docs, doc(cfg(feature = "parsing")))]
impl TryFrom<Parse> for TryFromParsed {
type Error = error::DifferentVariant;
fn try_from(err: Parse) -> Result<Self, Self::Error> {
match err {
Parse::TryFromParsed(err) => Ok(err),
_ => Err(error::DifferentVariant),
}
}
}
#[cfg_attr(__time_03_docs, doc(cfg(feature = "parsing")))]
impl From<ParseFromDescription> for Parse {
fn from(err: ParseFromDescription) -> Self {
Self::ParseFromDescription(err)
}
}
#[cfg_attr(__time_03_docs, doc(cfg(feature = "parsing")))]
impl TryFrom<Parse> for ParseFromDescription {
type Error = error::DifferentVariant;
fn try_from(err: Parse) -> Result<Self, Self::Error> {
match err {
Parse::ParseFromDescription(err) => Ok(err),
_ => Err(error::DifferentVariant),
}
}
}
#[cfg_attr(__time_03_docs, doc(cfg(feature = "parsing")))]
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,
}
}
}
#[cfg_attr(__time_03_docs, doc(cfg(feature = "parsing")))]
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),
crate::Error::TryFromParsed(err) => Ok(Self::TryFromParsed(err)),
_ => Err(error::DifferentVariant),
}
}
}

51
third_party/rust/time/src/error/parse_from_description.rs поставляемый Normal file
Просмотреть файл

@ -0,0 +1,51 @@
//! Error parsing an input into a [`Parsed`](crate::parsing::Parsed) struct
use core::convert::TryFrom;
use core::fmt;
use crate::error;
/// An error that occurred while parsing the input into a [`Parsed`](crate::parsing::Parsed) struct.
#[cfg_attr(__time_03_docs, doc(cfg(feature = "parsing")))]
#[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),
}
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 '{}' component could not be parsed", name)
}
}
}
}
#[cfg(feature = "std")]
impl std::error::Error for ParseFromDescription {}
#[cfg_attr(__time_03_docs, doc(cfg(feature = "parsing")))]
impl From<ParseFromDescription> for crate::Error {
fn from(original: ParseFromDescription) -> Self {
Self::ParseFromDescription(original)
}
}
#[cfg_attr(__time_03_docs, doc(cfg(feature = "parsing")))]
impl TryFrom<crate::Error> for ParseFromDescription {
type Error = error::DifferentVariant;
fn try_from(err: crate::Error) -> Result<Self, Self::Error> {
match err {
crate::Error::ParseFromDescription(err) => Ok(err),
_ => Err(error::DifferentVariant),
}
}
}

75
third_party/rust/time/src/error/try_from_parsed.rs поставляемый Normal file
Просмотреть файл

@ -0,0 +1,75 @@
//! Error converting a [`Parsed`](crate::parsing::Parsed) struct to another type
use core::convert::TryFrom;
use core::fmt;
use crate::error;
/// An error that occurred when converting a [`Parsed`](crate::parsing::Parsed) to another type.
#[non_exhaustive]
#[cfg_attr(__time_03_docs, doc(cfg(feature = "parsing")))]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TryFromParsed {
/// The [`Parsed`](crate::parsing::Parsed) did not include enough information to construct the
/// type.
InsufficientInformation,
/// Some component contained an invalid value for the type.
ComponentRange(error::ComponentRange),
}
impl fmt::Display for TryFromParsed {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::InsufficientInformation => f.write_str(
"the `Parsed` struct did not include enough information to construct the type",
),
Self::ComponentRange(err) => err.fmt(f),
}
}
}
impl From<error::ComponentRange> for TryFromParsed {
fn from(v: error::ComponentRange) -> Self {
Self::ComponentRange(v)
}
}
impl TryFrom<TryFromParsed> for error::ComponentRange {
type Error = error::DifferentVariant;
fn try_from(err: TryFromParsed) -> Result<Self, Self::Error> {
match err {
TryFromParsed::ComponentRange(err) => Ok(err),
_ => Err(error::DifferentVariant),
}
}
}
#[cfg(feature = "std")]
impl std::error::Error for TryFromParsed {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Self::InsufficientInformation => None,
Self::ComponentRange(err) => Some(err),
}
}
}
#[cfg_attr(__time_03_docs, doc(cfg(feature = "parsing")))]
impl From<TryFromParsed> for crate::Error {
fn from(original: TryFromParsed) -> Self {
Self::TryFromParsed(original)
}
}
#[cfg_attr(__time_03_docs, doc(cfg(feature = "parsing")))]
impl TryFrom<crate::Error> for TryFromParsed {
type Error = error::DifferentVariant;
fn try_from(err: crate::Error) -> Result<Self, Self::Error> {
match err {
crate::Error::TryFromParsed(err) => Ok(err),
_ => Err(error::DifferentVariant),
}
}
}

279
third_party/rust/time/src/ext.rs поставляемый Normal file
Просмотреть файл

@ -0,0 +1,279 @@
//! Extension traits.
use core::time::Duration as StdDuration;
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 * 1_000.) as _)
}
fn milliseconds(self) -> Duration {
Duration::nanoseconds((self * 1_000_000.) as _)
}
fn seconds(self) -> Duration {
Duration::nanoseconds((self * 1_000_000_000.) as _)
}
fn minutes(self) -> Duration {
Duration::nanoseconds((self * 60_000_000_000.) as _)
}
fn hours(self) -> Duration {
Duration::nanoseconds((self * 3_600_000_000_000.) as _)
}
fn days(self) -> Duration {
Duration::nanoseconds((self * 86_400_000_000_000.) as _)
}
fn weeks(self) -> Duration {
Duration::nanoseconds((self * 604_800_000_000_000.) 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 * 60)
}
fn std_hours(self) -> StdDuration {
StdDuration::from_secs(self * 3_600)
}
fn std_days(self) -> StdDuration {
StdDuration::from_secs(self * 86_400)
}
fn std_weeks(self) -> StdDuration {
StdDuration::from_secs(self * 604_800)
}
}
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 * 1_000.) as _)
}
fn std_milliseconds(self) -> StdDuration {
assert!(self >= 0.);
StdDuration::from_nanos((self * 1_000_000.) as _)
}
fn std_seconds(self) -> StdDuration {
assert!(self >= 0.);
StdDuration::from_nanos((self * 1_000_000_000.) as _)
}
fn std_minutes(self) -> StdDuration {
assert!(self >= 0.);
StdDuration::from_nanos((self * 60_000_000_000.) as _)
}
fn std_hours(self) -> StdDuration {
assert!(self >= 0.);
StdDuration::from_nanos((self * 3_600_000_000_000.) as _)
}
fn std_days(self) -> StdDuration {
assert!(self >= 0.);
StdDuration::from_nanos((self * 86_400_000_000_000.) as _)
}
fn std_weeks(self) -> StdDuration {
assert!(self >= 0.);
StdDuration::from_nanos((self * 604_800_000_000_000.) as _)
}
}
// endregion NumericalStdDuration

167
third_party/rust/time/src/format_description/component.rs поставляемый Normal file
Просмотреть файл

@ -0,0 +1,167 @@
//! Part of a format description.
#[cfg(feature = "alloc")]
use alloc::string::String;
use crate::format_description::modifier;
#[cfg(feature = "alloc")]
use crate::{error::InvalidFormatDescription, format_description::modifier::Modifiers};
/// A component of a larger format description.
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Component {
/// Day of the month.
Day(modifier::Day),
/// Month of the year.
Month(modifier::Month),
/// Ordinal day of the year.
Ordinal(modifier::Ordinal),
/// Day of the week.
Weekday(modifier::Weekday),
/// Week within the year.
WeekNumber(modifier::WeekNumber),
/// Year of the date.
Year(modifier::Year),
/// Hour of the day.
Hour(modifier::Hour),
/// Minute within the hour.
Minute(modifier::Minute),
/// AM/PM part of the time.
Period(modifier::Period),
/// Second within the minute.
Second(modifier::Second),
/// Subsecond within the second.
Subsecond(modifier::Subsecond),
/// 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 component with no modifiers present.
#[cfg(feature = "alloc")]
pub(crate) enum NakedComponent {
/// Day of the month.
Day,
/// Month of the year.
Month,
/// Ordinal day of the year.
Ordinal,
/// Day of the week.
Weekday,
/// Week within the year.
WeekNumber,
/// Year of the date.
Year,
/// Hour of the day.
Hour,
/// Minute within the hour.
Minute,
/// AM/PM part of the time.
Period,
/// Second within the minute.
Second,
/// Subsecond within the second.
Subsecond,
/// Hour of the UTC offset.
OffsetHour,
/// Minute within the hour of the UTC offset.
OffsetMinute,
/// Second within the minute of the UTC offset.
OffsetSecond,
}
#[cfg(feature = "alloc")]
impl NakedComponent {
/// Parse a component (without its modifiers) from the provided name.
pub(crate) fn parse(
component_name: &[u8],
component_index: usize,
) -> Result<Self, InvalidFormatDescription> {
match component_name {
b"day" => Ok(Self::Day),
b"month" => Ok(Self::Month),
b"ordinal" => Ok(Self::Ordinal),
b"weekday" => Ok(Self::Weekday),
b"week_number" => Ok(Self::WeekNumber),
b"year" => Ok(Self::Year),
b"hour" => Ok(Self::Hour),
b"minute" => Ok(Self::Minute),
b"period" => Ok(Self::Period),
b"second" => Ok(Self::Second),
b"subsecond" => Ok(Self::Subsecond),
b"offset_hour" => Ok(Self::OffsetHour),
b"offset_minute" => Ok(Self::OffsetMinute),
b"offset_second" => Ok(Self::OffsetSecond),
b"" => Err(InvalidFormatDescription::MissingComponentName {
index: component_index,
}),
_ => Err(InvalidFormatDescription::InvalidComponentName {
name: String::from_utf8_lossy(component_name).into_owned(),
index: component_index,
}),
}
}
/// Attach the necessary modifiers to the component.
pub(crate) fn attach_modifiers(self, modifiers: &Modifiers) -> Component {
match self {
Self::Day => Component::Day(modifier::Day {
padding: modifiers.padding.unwrap_or_default(),
}),
Self::Month => Component::Month(modifier::Month {
padding: modifiers.padding.unwrap_or_default(),
repr: modifiers.month_repr.unwrap_or_default(),
case_sensitive: modifiers.case_sensitive.unwrap_or(true),
}),
Self::Ordinal => Component::Ordinal(modifier::Ordinal {
padding: modifiers.padding.unwrap_or_default(),
}),
Self::Weekday => Component::Weekday(modifier::Weekday {
repr: modifiers.weekday_repr.unwrap_or_default(),
one_indexed: modifiers.weekday_is_one_indexed.unwrap_or(true),
case_sensitive: modifiers.case_sensitive.unwrap_or(true),
}),
Self::WeekNumber => Component::WeekNumber(modifier::WeekNumber {
padding: modifiers.padding.unwrap_or_default(),
repr: modifiers.week_number_repr.unwrap_or_default(),
}),
Self::Year => Component::Year(modifier::Year {
padding: modifiers.padding.unwrap_or_default(),
repr: modifiers.year_repr.unwrap_or_default(),
iso_week_based: modifiers.year_is_iso_week_based.unwrap_or_default(),
sign_is_mandatory: modifiers.sign_is_mandatory.unwrap_or_default(),
}),
Self::Hour => Component::Hour(modifier::Hour {
padding: modifiers.padding.unwrap_or_default(),
is_12_hour_clock: modifiers.hour_is_12_hour_clock.unwrap_or_default(),
}),
Self::Minute => Component::Minute(modifier::Minute {
padding: modifiers.padding.unwrap_or_default(),
}),
Self::Period => Component::Period(modifier::Period {
is_uppercase: modifiers.period_is_uppercase.unwrap_or(true),
case_sensitive: modifiers.case_sensitive.unwrap_or(true),
}),
Self::Second => Component::Second(modifier::Second {
padding: modifiers.padding.unwrap_or_default(),
}),
Self::Subsecond => Component::Subsecond(modifier::Subsecond {
digits: modifiers.subsecond_digits.unwrap_or_default(),
}),
Self::OffsetHour => Component::OffsetHour(modifier::OffsetHour {
sign_is_mandatory: modifiers.sign_is_mandatory.unwrap_or_default(),
padding: modifiers.padding.unwrap_or_default(),
}),
Self::OffsetMinute => Component::OffsetMinute(modifier::OffsetMinute {
padding: modifiers.padding.unwrap_or_default(),
}),
Self::OffsetSecond => Component::OffsetSecond(modifier::OffsetSecond {
padding: modifiers.padding.unwrap_or_default(),
}),
}
}
}

186
third_party/rust/time/src/format_description/mod.rs поставляемый Normal file
Просмотреть файл

@ -0,0 +1,186 @@
//! Description of how types should be formatted and parsed.
//!
//! The formatted value will be output to the provided writer. Format descriptions can be
//! [well-known](crate::format_description::well_known) or obtained by using the
//! [`format_description!`](crate::macros::format_description) macro, the
//! [`format_description::parse`](crate::format_description::parse()) function.
mod component;
pub mod modifier;
#[cfg(feature = "alloc")]
pub(crate) mod parse;
#[cfg(feature = "alloc")]
use alloc::string::String;
use core::convert::TryFrom;
#[cfg(feature = "alloc")]
use core::fmt;
pub use self::component::Component;
#[cfg(feature = "alloc")]
pub use self::parse::parse;
use crate::error;
/// Helper methods.
#[cfg(feature = "alloc")]
mod helper {
/// Consume all leading whitespace, advancing `index` as appropriate.
#[must_use = "This does not modify the original slice."]
pub(crate) fn consume_whitespace<'a>(bytes: &'a [u8], index: &mut usize) -> &'a [u8] {
let first_non_whitespace = bytes
.iter()
.position(|c| !c.is_ascii_whitespace())
.unwrap_or(bytes.len());
*index += first_non_whitespace;
&bytes[first_non_whitespace..]
}
}
/// Well-known formats, typically RFCs.
pub mod well_known {
/// The format described in [RFC 3339](https://tools.ietf.org/html/rfc3339#section-5.6).
///
/// Format example: 1985-04-12T23:20:50.52Z
///
/// ```rust
/// # use time::{format_description::well_known::Rfc3339, macros::datetime, OffsetDateTime};
/// assert_eq!(
/// OffsetDateTime::parse("1985-04-12T23:20:50.52Z", &Rfc3339)?,
/// datetime!(1985-04-12 23:20:50.52 +00:00)
/// );
/// # Ok::<_, time::Error>(())
/// ```
///
/// ```rust
/// # use time::{format_description::well_known::Rfc3339, macros::datetime};
/// assert_eq!(
/// datetime!(1985-04-12 23:20:50.52 +00:00).format(&Rfc3339)?,
/// "1985-04-12T23:20:50.52Z"
/// );
/// # Ok::<_, time::Error>(())
/// ```
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Rfc3339;
/// The format described in [RFC 2822](https://tools.ietf.org/html/rfc2822#section-3.3).
///
/// Example: Fri, 21 Nov 1997 09:55:06 -0600
///
/// # Examples
/// ```rust
/// # use time::{format_description::well_known::Rfc2822, macros::datetime, OffsetDateTime};
/// assert_eq!(
/// OffsetDateTime::parse("Sat, 12 Jun 1993 13:25:19 GMT", &Rfc2822)?,
/// datetime!(1993-06-12 13:25:19 +00:00)
/// );
/// # Ok::<_, time::Error>(())
/// ```
///
/// ```rust
/// # use time::{format_description::well_known::Rfc2822, macros::datetime};
/// assert_eq!(
/// datetime!(1997-11-21 09:55:06 -06:00).format(&Rfc2822)?,
/// "Fri, 21 Nov 1997 09:55:06 -0600"
/// );
/// # Ok::<_, time::Error>(())
/// ```
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Rfc2822;
}
/// 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 FormatItem<'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`.
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.
Optional(&'a Self),
/// A series of `FormatItem`s where, when parsing, the first successful parse is used. When
/// formatting, the first element of the slice is used. An empty slice is a no-op when
/// formatting or parsing.
First(&'a [Self]),
}
#[cfg(feature = "alloc")]
impl fmt::Debug for FormatItem<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
FormatItem::Literal(literal) => f.write_str(&String::from_utf8_lossy(literal)),
FormatItem::Component(component) => component.fmt(f),
FormatItem::Compound(compound) => compound.fmt(f),
FormatItem::Optional(item) => f.debug_tuple("Optional").field(item).finish(),
FormatItem::First(items) => f.debug_tuple("First").field(items).finish(),
}
}
}
impl From<Component> for FormatItem<'_> {
fn from(component: Component) -> Self {
Self::Component(component)
}
}
impl TryFrom<FormatItem<'_>> for Component {
type Error = error::DifferentVariant;
fn try_from(value: FormatItem<'_>) -> Result<Self, Self::Error> {
match value {
FormatItem::Component(component) => Ok(component),
_ => Err(error::DifferentVariant),
}
}
}
impl<'a> From<&'a [FormatItem<'_>]> for FormatItem<'a> {
fn from(items: &'a [FormatItem<'_>]) -> FormatItem<'a> {
FormatItem::Compound(items)
}
}
impl<'a> TryFrom<FormatItem<'a>> for &[FormatItem<'a>] {
type Error = error::DifferentVariant;
fn try_from(value: FormatItem<'a>) -> Result<Self, Self::Error> {
match value {
FormatItem::Compound(items) => Ok(items),
_ => Err(error::DifferentVariant),
}
}
}
impl PartialEq<Component> for FormatItem<'_> {
fn eq(&self, rhs: &Component) -> bool {
matches!(self, FormatItem::Component(component) if component == rhs)
}
}
impl PartialEq<FormatItem<'_>> for Component {
fn eq(&self, rhs: &FormatItem<'_>) -> bool {
rhs == self
}
}
impl PartialEq<&[FormatItem<'_>]> for FormatItem<'_> {
fn eq(&self, rhs: &&[FormatItem<'_>]) -> bool {
matches!(self, FormatItem::Compound(compound) if compound == rhs)
}
}
impl PartialEq<FormatItem<'_>> for &[FormatItem<'_>] {
fn eq(&self, rhs: &FormatItem<'_>) -> bool {
rhs == self
}
}

509
third_party/rust/time/src/format_description/modifier.rs поставляемый Normal file
Просмотреть файл

@ -0,0 +1,509 @@
//! Various modifiers for components.
#[cfg(feature = "alloc")]
use alloc::string::String;
#[cfg(feature = "alloc")]
use core::mem;
#[cfg(feature = "alloc")]
use crate::{error::InvalidFormatDescription, format_description::helper};
// region: date modifiers
/// Day of the month.
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Day {
/// The padding to obtain the minimum width.
pub padding: Padding,
}
/// The representation of a month.
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MonthRepr {
/// The number of the month (January is 1, December is 12).
Numerical,
/// The long form of the month name (e.g. "January").
Long,
/// The short form of the month name (e.g. "Jan").
Short,
}
/// Month of the year.
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Month {
/// The padding to obtain the minimum width.
pub padding: Padding,
/// What form of representation should be used?
pub repr: MonthRepr,
/// Is the value case sensitive when parsing?
pub case_sensitive: bool,
}
/// Ordinal day of the year.
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Ordinal {
/// The padding to obtain the minimum width.
pub padding: Padding,
}
/// The representation used for the day of the week.
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum WeekdayRepr {
/// The short form of the weekday (e.g. "Mon").
Short,
/// The long form of the weekday (e.g. "Monday").
Long,
/// A numerical representation using Sunday as the first day of the week.
///
/// Sunday is either 0 or 1, depending on the other modifier's value.
Sunday,
/// A numerical representation using Monday as the first day of the week.
///
/// Monday is either 0 or 1, depending on the other modifier's value.
Monday,
}
/// Day of the week.
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Weekday {
/// What form of representation should be used?
pub repr: WeekdayRepr,
/// When using a numerical representation, should it be zero or one-indexed?
pub one_indexed: bool,
/// Is the value case sensitive when parsing?
pub case_sensitive: bool,
}
/// The representation used for the week number.
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum WeekNumberRepr {
/// Week 1 is the week that contains January 4.
Iso,
/// Week 1 begins on the first Sunday of the calendar year.
Sunday,
/// Week 1 begins on the first Monday of the calendar year.
Monday,
}
/// Week within the year.
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct WeekNumber {
/// The padding to obtain the minimum width.
pub padding: Padding,
/// What kind of representation should be used?
pub repr: WeekNumberRepr,
}
/// The representation used for a year value.
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum YearRepr {
/// The full value of the year.
Full,
/// Only the last two digits of the year.
LastTwo,
}
/// Year of the date.
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Year {
/// The padding to obtain the minimum width.
pub padding: Padding,
/// What kind of representation should be used?
pub repr: YearRepr,
/// Whether the value is based on the ISO week number or the Gregorian calendar.
pub iso_week_based: bool,
/// Whether the `+` sign is present when a positive year contains fewer than five digits.
pub sign_is_mandatory: bool,
}
// endregion date modifiers
// region: time modifiers
/// Hour of the day.
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Hour {
/// The padding to obtain the minimum width.
pub padding: Padding,
/// Is the hour displayed using a 12 or 24-hour clock?
pub is_12_hour_clock: bool,
}
/// Minute within the hour.
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Minute {
/// The padding to obtain the minimum width.
pub padding: Padding,
}
/// AM/PM part of the time.
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Period {
/// Is the period uppercase or lowercase?
pub is_uppercase: bool,
/// Is the value case sensitive when parsing?
///
/// Note that when `false`, the `is_uppercase` field has no effect on parsing behavior.
pub case_sensitive: bool,
}
/// Second within the minute.
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Second {
/// The padding to obtain the minimum width.
pub padding: Padding,
}
/// The number of digits present in a subsecond representation.
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SubsecondDigits {
/// Exactly one digit.
One,
/// Exactly two digits.
Two,
/// Exactly three digits.
Three,
/// Exactly four digits.
Four,
/// Exactly five digits.
Five,
/// Exactly six digits.
Six,
/// Exactly seven digits.
Seven,
/// Exactly eight digits.
Eight,
/// Exactly nine digits.
Nine,
/// Any number of digits (up to nine) that is at least one. When formatting, the minimum digits
/// necessary will be used.
OneOrMore,
}
/// Subsecond within the second.
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Subsecond {
/// How many digits are present in the component?
pub digits: SubsecondDigits,
}
// endregion time modifiers
// region: offset modifiers
/// Hour of the UTC offset.
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct OffsetHour {
/// Whether the `+` sign is present on positive values.
pub sign_is_mandatory: bool,
/// The padding to obtain the minimum width.
pub padding: Padding,
}
/// Minute within the hour of the UTC offset.
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct OffsetMinute {
/// The padding to obtain the minimum width.
pub padding: Padding,
}
/// Second within the minute of the UTC offset.
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct OffsetSecond {
/// The padding to obtain the minimum width.
pub padding: Padding,
}
// endregion offset modifiers
/// Type of padding to ensure a minimum width.
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Padding {
/// A space character (` `) should be used as padding.
Space,
/// A zero character (`0`) should be used as padding.
Zero,
/// There is no padding. This can result in a width below the otherwise minimum number of
/// characters.
None,
}
/// 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.
$($x)*
};
($($_:tt)*) => {};
}
/// Implement `Default` for the given type. This also generates an inherent implementation of a
/// `default` method that is `const fn`, permitting the default value to be used in const contexts.
// Every modifier should use this macro rather than a derived `Default`.
macro_rules! impl_const_default {
($($(#[$doc:meta])* $(@$pub:ident)? $type:ty => $default:expr;)*) => {$(
impl $type {
if_pub! {
$($pub)?
$(#[$doc])*;
pub const fn default() -> Self {
$default
}
}
}
$(#[$doc])*
impl Default for $type {
fn default() -> Self {
$default
}
}
)*};
}
impl_const_default! {
/// Creates a modifier that indicates the value is [padded with zeroes](Padding::Zero).
@pub Day => Self { padding: Padding::Zero };
/// Creates a modifier that indicates the value uses the
/// [`Numerical`](Self::Numerical) representation.
MonthRepr => Self::Numerical;
/// Creates an instance of this type that indicates the value uses the
/// [`Numerical`](MonthRepr::Numerical) representation, is [padded with zeroes](Padding::Zero),
/// and is case-sensitive when parsing.
@pub Month => Self {
padding: Padding::Zero,
repr: MonthRepr::Numerical,
case_sensitive: true,
};
/// Creates a modifier that indicates the value is [padded with zeroes](Padding::Zero).
@pub Ordinal => Self { padding: Padding::Zero };
/// Creates a modifier that indicates the value uses the [`Long`](Self::Long) representation.
WeekdayRepr => Self::Long;
/// Creates a modifier that indicates the value uses the [`Long`](WeekdayRepr::Long)
/// representation and is case-sensitive when parsing. If the representation is changed to a
/// numerical one, the instance defaults to one-based indexing.
@pub Weekday => Self {
repr: WeekdayRepr::Long,
one_indexed: true,
case_sensitive: true,
};
/// Creates a modifier that indicates that the value uses the [`Iso`](Self::Iso) representation.
WeekNumberRepr => Self::Iso;
/// Creates a modifier that indicates that the value is [padded with zeroes](Padding::Zero)
/// and uses the [`Iso`](WeekNumberRepr::Iso) representation.
@pub WeekNumber => Self {
padding: Padding::Zero,
repr: WeekNumberRepr::Iso,
};
/// Creates a modifier that indicates the value uses the [`Full`](Self::Full) representation.
YearRepr => Self::Full;
/// Creates a modifier that indicates the value uses the [`Full`](YearRepr::Full)
/// representation, is [padded with zeroes](Padding::Zero), uses the Gregorian calendar as its
/// base, and only includes the year's sign if necessary.
@pub Year => Self {
padding: Padding::Zero,
repr: YearRepr::Full,
iso_week_based: false,
sign_is_mandatory: false,
};
/// Creates a modifier that indicates the value is [padded with zeroes](Padding::Zero) and
/// has the 24-hour representation.
@pub Hour => Self {
padding: Padding::Zero,
is_12_hour_clock: false,
};
/// Creates a modifier that indicates the value is [padded with zeroes](Padding::Zero).
@pub Minute => Self { padding: Padding::Zero };
/// Creates a modifier that indicates the value uses the upper-case representation and is
/// case-sensitive when parsing.
@pub Period => Self {
is_uppercase: true,
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).
@pub OffsetHour => Self {
sign_is_mandatory: true,
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;
}
/// The modifiers parsed for any given component. `None` indicates the modifier was not present.
#[cfg(feature = "alloc")]
#[allow(clippy::missing_docs_in_private_items)] // fields
#[derive(Debug, Default)]
pub(crate) struct Modifiers {
pub(crate) padding: Option<Padding>,
pub(crate) hour_is_12_hour_clock: Option<bool>,
pub(crate) period_is_uppercase: Option<bool>,
pub(crate) month_repr: Option<MonthRepr>,
pub(crate) subsecond_digits: Option<SubsecondDigits>,
pub(crate) weekday_repr: Option<WeekdayRepr>,
pub(crate) weekday_is_one_indexed: Option<bool>,
pub(crate) week_number_repr: Option<WeekNumberRepr>,
pub(crate) year_repr: Option<YearRepr>,
pub(crate) year_is_iso_week_based: Option<bool>,
pub(crate) sign_is_mandatory: Option<bool>,
pub(crate) case_sensitive: Option<bool>,
}
#[cfg(feature = "alloc")]
impl Modifiers {
/// Parse the modifiers of a given component.
#[allow(clippy::too_many_lines)]
pub(crate) fn parse(
component_name: &[u8],
mut bytes: &[u8],
index: &mut usize,
) -> Result<Self, InvalidFormatDescription> {
let mut modifiers = Self::default();
while !bytes.is_empty() {
// Trim any whitespace between modifiers.
bytes = helper::consume_whitespace(bytes, index);
let modifier;
if let Some(whitespace_loc) = bytes.iter().position(u8::is_ascii_whitespace) {
*index += whitespace_loc;
modifier = &bytes[..whitespace_loc];
bytes = &bytes[whitespace_loc..];
} else {
modifier = mem::take(&mut bytes);
}
if modifier.is_empty() {
break;
}
match (component_name, modifier) {
(
b"day" | b"hour" | b"minute" | b"month" | b"offset_hour" | b"offset_minute"
| b"offset_second" | b"ordinal" | b"second" | b"week_number" | b"year",
b"padding:space",
) => modifiers.padding = Some(Padding::Space),
(
b"day" | b"hour" | b"minute" | b"month" | b"offset_hour" | b"offset_minute"
| b"offset_second" | b"ordinal" | b"second" | b"week_number" | b"year",
b"padding:zero",
) => modifiers.padding = Some(Padding::Zero),
(
b"day" | b"hour" | b"minute" | b"month" | b"offset_hour" | b"offset_minute"
| b"offset_second" | b"ordinal" | b"second" | b"week_number" | b"year",
b"padding:none",
) => modifiers.padding = Some(Padding::None),
(b"hour", b"repr:24") => modifiers.hour_is_12_hour_clock = Some(false),
(b"hour", b"repr:12") => modifiers.hour_is_12_hour_clock = Some(true),
(b"month" | b"period" | b"weekday", b"case_sensitive:true") => {
modifiers.case_sensitive = Some(true)
}
(b"month" | b"period" | b"weekday", b"case_sensitive:false") => {
modifiers.case_sensitive = Some(false)
}
(b"month", b"repr:numerical") => modifiers.month_repr = Some(MonthRepr::Numerical),
(b"month", b"repr:long") => modifiers.month_repr = Some(MonthRepr::Long),
(b"month", b"repr:short") => modifiers.month_repr = Some(MonthRepr::Short),
(b"offset_hour" | b"year", b"sign:automatic") => {
modifiers.sign_is_mandatory = Some(false);
}
(b"offset_hour" | b"year", b"sign:mandatory") => {
modifiers.sign_is_mandatory = Some(true);
}
(b"period", b"case:upper") => modifiers.period_is_uppercase = Some(true),
(b"period", b"case:lower") => modifiers.period_is_uppercase = Some(false),
(b"subsecond", b"digits:1") => {
modifiers.subsecond_digits = Some(SubsecondDigits::One);
}
(b"subsecond", b"digits:2") => {
modifiers.subsecond_digits = Some(SubsecondDigits::Two);
}
(b"subsecond", b"digits:3") => {
modifiers.subsecond_digits = Some(SubsecondDigits::Three);
}
(b"subsecond", b"digits:4") => {
modifiers.subsecond_digits = Some(SubsecondDigits::Four);
}
(b"subsecond", b"digits:5") => {
modifiers.subsecond_digits = Some(SubsecondDigits::Five);
}
(b"subsecond", b"digits:6") => {
modifiers.subsecond_digits = Some(SubsecondDigits::Six);
}
(b"subsecond", b"digits:7") => {
modifiers.subsecond_digits = Some(SubsecondDigits::Seven);
}
(b"subsecond", b"digits:8") => {
modifiers.subsecond_digits = Some(SubsecondDigits::Eight);
}
(b"subsecond", b"digits:9") => {
modifiers.subsecond_digits = Some(SubsecondDigits::Nine);
}
(b"subsecond", b"digits:1+") => {
modifiers.subsecond_digits = Some(SubsecondDigits::OneOrMore);
}
(b"weekday", b"repr:short") => modifiers.weekday_repr = Some(WeekdayRepr::Short),
(b"weekday", b"repr:long") => modifiers.weekday_repr = Some(WeekdayRepr::Long),
(b"weekday", b"repr:sunday") => modifiers.weekday_repr = Some(WeekdayRepr::Sunday),
(b"weekday", b"repr:monday") => modifiers.weekday_repr = Some(WeekdayRepr::Monday),
(b"weekday", b"one_indexed:true") => modifiers.weekday_is_one_indexed = Some(true),
(b"weekday", b"one_indexed:false") => {
modifiers.weekday_is_one_indexed = Some(false);
}
(b"week_number", b"repr:iso") => {
modifiers.week_number_repr = Some(WeekNumberRepr::Iso);
}
(b"week_number", b"repr:sunday") => {
modifiers.week_number_repr = Some(WeekNumberRepr::Sunday);
}
(b"week_number", b"repr:monday") => {
modifiers.week_number_repr = Some(WeekNumberRepr::Monday);
}
(b"year", b"repr:full") => modifiers.year_repr = Some(YearRepr::Full),
(b"year", b"repr:last_two") => modifiers.year_repr = Some(YearRepr::LastTwo),
(b"year", b"base:calendar") => modifiers.year_is_iso_week_based = Some(false),
(b"year", b"base:iso_week") => modifiers.year_is_iso_week_based = Some(true),
_ => {
return Err(InvalidFormatDescription::InvalidModifier {
value: String::from_utf8_lossy(modifier).into_owned(),
index: *index,
});
}
}
}
Ok(modifiers)
}
}

97
third_party/rust/time/src/format_description/parse.rs поставляемый Normal file
Просмотреть файл

@ -0,0 +1,97 @@
//! Parse a format description into a standardized representation.
use alloc::vec::Vec;
use crate::error::InvalidFormatDescription;
use crate::format_description::component::{Component, NakedComponent};
use crate::format_description::{helper, modifier, FormatItem};
/// The item parsed and remaining chunk of the format description after one iteration.
#[derive(Debug)]
pub(crate) struct ParsedItem<'a> {
/// The item that was parsed.
pub(crate) item: FormatItem<'a>,
/// What is left of the input string after the item was parsed.
pub(crate) remaining: &'a [u8],
}
/// Parse a component from the format description. Neither the leading nor trailing bracket should
/// be present in the parameter.
fn parse_component(mut s: &[u8], index: &mut usize) -> Result<Component, InvalidFormatDescription> {
// Trim any whitespace between the opening bracket and the component name.
s = helper::consume_whitespace(s, index);
// Everything before the first whitespace is the component name.
let component_index = *index;
let whitespace_loc = s
.iter()
.position(u8::is_ascii_whitespace)
.unwrap_or(s.len());
*index += whitespace_loc;
let component_name = &s[..whitespace_loc];
s = &s[whitespace_loc..];
s = helper::consume_whitespace(s, index);
Ok(NakedComponent::parse(component_name, component_index)?
.attach_modifiers(&modifier::Modifiers::parse(component_name, s, index)?))
}
/// Parse a literal string from the format description.
fn parse_literal<'a>(s: &'a [u8], index: &mut usize) -> ParsedItem<'a> {
let loc = s.iter().position(|&c| c == b'[').unwrap_or(s.len());
*index += loc;
ParsedItem {
item: FormatItem::Literal(&s[..loc]),
remaining: &s[loc..],
}
}
/// Parse either a literal or a component from the format description.
fn parse_item<'a>(
s: &'a [u8],
index: &mut usize,
) -> Result<ParsedItem<'a>, InvalidFormatDescription> {
if let [b'[', b'[', remaining @ ..] = s {
*index += 2;
return Ok(ParsedItem {
item: FormatItem::Literal(&[b'[']),
remaining,
});
};
if s.starts_with(&[b'[']) {
if let Some(bracket_index) = s.iter().position(|&c| c == b']') {
*index += 1; // opening bracket
let ret_val = ParsedItem {
item: FormatItem::Component(parse_component(&s[1..bracket_index], index)?),
remaining: &s[bracket_index + 1..],
};
*index += 1; // closing bracket
Ok(ret_val)
} else {
Err(InvalidFormatDescription::UnclosedOpeningBracket { index: *index })
}
} else {
Ok(parse_literal(s, index))
}
}
/// 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).
#[cfg_attr(__time_03_docs, doc(cfg(feature = "alloc")))]
pub fn parse(s: &str) -> Result<Vec<FormatItem<'_>>, InvalidFormatDescription> {
let mut compound = Vec::new();
let mut loc = 0;
let mut s = s.as_bytes();
while !s.is_empty() {
let ParsedItem { item, remaining } = parse_item(s, &mut loc)?;
s = remaining;
compound.push(item);
}
Ok(compound)
}

238
third_party/rust/time/src/formatting/formattable.rs поставляемый Normal file
Просмотреть файл

@ -0,0 +1,238 @@
//! A trait that can be used to format an item from its components.
use core::ops::Deref;
use std::io;
use crate::format_description::well_known::{Rfc2822, Rfc3339};
use crate::format_description::FormatItem;
use crate::formatting::{
format_component, format_number_pad_zero, write, MONTH_NAMES, WEEKDAY_NAMES,
};
use crate::{error, Date, Time, UtcOffset};
/// A type that can be formatted.
#[cfg_attr(__time_03_docs, doc(notable_trait))]
pub trait Formattable: sealed::Sealed {}
impl Formattable for FormatItem<'_> {}
impl Formattable for [FormatItem<'_>] {}
impl Formattable for Rfc3339 {}
impl Formattable for Rfc2822 {}
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)]
use super::*;
/// Format the item using a format description, the intended output, and the various components.
#[cfg_attr(__time_03_docs, doc(cfg(feature = "formatting")))]
pub trait Sealed {
/// Format the item into the provided output, returning the number of bytes written.
fn format_into(
&self,
output: &mut impl io::Write,
date: Option<Date>,
time: Option<Time>,
offset: Option<UtcOffset>,
) -> Result<usize, error::Format>;
/// Format the item directly to a `String`.
fn format(
&self,
date: Option<Date>,
time: Option<Time>,
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> {
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>] {
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)?;
}
Ok(bytes)
}
}
impl<T: Deref> sealed::Sealed for T
where
T::Target: sealed::Sealed,
{
fn format_into(
&self,
output: &mut impl io::Write,
date: Option<Date>,
time: Option<Time>,
offset: Option<UtcOffset>,
) -> Result<usize, error::Format> {
self.deref().format_into(output, date, time, offset)
}
}
// endregion custom formats
// region: well-known formats
impl sealed::Sealed for Rfc2822 {
fn format_into(
&self,
output: &mut impl io::Write,
date: Option<Date>,
time: Option<Time>,
offset: Option<UtcOffset>,
) -> Result<usize, error::Format> {
let date = date.ok_or(error::Format::InsufficientTypeInformation)?;
let time = time.ok_or(error::Format::InsufficientTypeInformation)?;
let offset = offset.ok_or(error::Format::InsufficientTypeInformation)?;
let mut bytes = 0;
let (year, month, day) = date.to_calendar_date();
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],
)?;
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, b" ")?;
bytes += format_number_pad_zero::<_, _, 4>(output, year as u32)?;
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())?;
Ok(bytes)
}
}
impl sealed::Sealed for Rfc3339 {
fn format_into(
&self,
output: &mut impl io::Write,
date: Option<Date>,
time: Option<Time>,
offset: Option<UtcOffset>,
) -> Result<usize, error::Format> {
let date = date.ok_or(error::Format::InsufficientTypeInformation)?;
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.seconds_past_minute() != 0 {
return Err(error::Format::InvalidComponent("offset_second"));
}
bytes += format_number_pad_zero::<_, _, 4>(output, year as u32)?;
bytes += write(output, &[b'-'])?;
bytes += format_number_pad_zero::<_, _, 2>(output, date.month() as u8)?;
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)]
if time.nanosecond() != 0 {
let nanos = time.nanosecond();
bytes += write(output, &[b'.'])?;
bytes += if nanos % 10 != 0 {
format_number_pad_zero::<_, _, 9>(output, nanos)
} else if (nanos / 10) % 10 != 0 {
format_number_pad_zero::<_, _, 8>(output, nanos / 10)
} else if (nanos / 100) % 10 != 0 {
format_number_pad_zero::<_, _, 7>(output, nanos / 100)
} else if (nanos / 1_000) % 10 != 0 {
format_number_pad_zero::<_, _, 6>(output, nanos / 1_000)
} else if (nanos / 10_000) % 10 != 0 {
format_number_pad_zero::<_, _, 5>(output, nanos / 10_000)
} else if (nanos / 100_000) % 10 != 0 {
format_number_pad_zero::<_, _, 4>(output, nanos / 100_000)
} else if (nanos / 1_000_000) % 10 != 0 {
format_number_pad_zero::<_, _, 3>(output, nanos / 1_000_000)
} else if (nanos / 10_000_000) % 10 != 0 {
format_number_pad_zero::<_, _, 2>(output, nanos / 10_000_000)
} else {
format_number_pad_zero::<_, _, 1>(output, nanos / 100_000_000)
}?;
}
if offset == UtcOffset::UTC {
bytes += write(output, &[b'Z'])?;
return Ok(bytes);
}
bytes += write(
output,
if offset.is_negative() {
&[b'-']
} else {
&[b'+']
},
)?;
bytes += format_number_pad_zero::<_, _, 2>(output, offset.whole_hours().unsigned_abs())?;
bytes += write(output, &[b':'])?;
bytes +=
format_number_pad_zero::<_, _, 2>(output, offset.minutes_past_hour().unsigned_abs())?;
Ok(bytes)
}
}
// endregion well-known formats

457
third_party/rust/time/src/formatting/mod.rs поставляемый Normal file
Просмотреть файл

@ -0,0 +1,457 @@
//! Formatting for various types.
pub(crate) mod formattable;
use std::io;
pub use self::formattable::Formattable;
use crate::format_description::{modifier, Component};
use crate::{error, Date, Time, UtcOffset};
#[allow(clippy::missing_docs_in_private_items)]
const MONTH_NAMES: [&[u8]; 12] = [
b"January",
b"February",
b"March",
b"April",
b"May",
b"June",
b"July",
b"August",
b"September",
b"October",
b"November",
b"December",
];
#[allow(clippy::missing_docs_in_private_items)]
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.
fn write(output: &mut impl io::Write, bytes: &[u8]) -> io::Result<usize> {
output.write_all(bytes)?;
Ok(bytes.len())
}
/// Format a number with the provided padding and width.
///
/// The sign must be written by the caller.
pub(crate) fn format_number<W: io::Write, V: itoa::Integer + DigitCount + Copy, const WIDTH: u8>(
output: &mut W,
value: V,
padding: modifier::Padding,
) -> Result<usize, io::Error> {
match padding {
modifier::Padding::Space => format_number_pad_space::<_, _, WIDTH>(output, value),
modifier::Padding::Zero => format_number_pad_zero::<_, _, WIDTH>(output, value),
modifier::Padding::None => write(output, itoa::Buffer::new().format(value).as_bytes()),
}
}
/// Format a number with the provided width and spaces as padding.
///
/// The sign must be written by the caller.
pub(crate) fn format_number_pad_space<
W: io::Write,
V: itoa::Integer + DigitCount + Copy,
const WIDTH: u8,
>(
output: &mut W,
value: V,
) -> Result<usize, io::Error> {
let mut bytes = 0;
for _ in 0..(WIDTH.saturating_sub(value.num_digits())) {
bytes += write(output, &[b' '])?;
}
bytes += write(output, itoa::Buffer::new().format(value).as_bytes())?;
Ok(bytes)
}
/// Format a number with the provided width and zeros as padding.
///
/// The sign must be written by the caller.
pub(crate) fn format_number_pad_zero<
W: io::Write,
V: itoa::Integer + DigitCount + Copy,
const WIDTH: u8,
>(
output: &mut W,
value: V,
) -> Result<usize, io::Error> {
let mut bytes = 0;
for _ in 0..(WIDTH.saturating_sub(value.num_digits())) {
bytes += write(output, &[b'0'])?;
}
bytes += write(output, itoa::Buffer::new().format(value).as_bytes())?;
Ok(bytes)
}
/// Format the provided component into the designated output. An `Err` will be returned if the
/// component requires information that it does not provide or if the value cannot be output to the
/// stream.
pub(crate) fn format_component(
output: &mut impl io::Write,
component: Component,
date: Option<Date>,
time: Option<Time>,
offset: Option<UtcOffset>,
) -> Result<usize, error::Format> {
use Component::*;
Ok(match (component, date, time, offset) {
(Day(modifier), Some(date), ..) => fmt_day(output, date, modifier)?,
(Month(modifier), Some(date), ..) => fmt_month(output, date, modifier)?,
(Ordinal(modifier), Some(date), ..) => fmt_ordinal(output, date, modifier)?,
(Weekday(modifier), Some(date), ..) => fmt_weekday(output, date, modifier)?,
(WeekNumber(modifier), Some(date), ..) => fmt_week_number(output, date, modifier)?,
(Year(modifier), Some(date), ..) => fmt_year(output, date, modifier)?,
(Hour(modifier), _, Some(time), _) => fmt_hour(output, time, modifier)?,
(Minute(modifier), _, Some(time), _) => fmt_minute(output, time, modifier)?,
(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)?,
_ => 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> {
format_number::<_, _, 2>(output, date.day(), padding)
}
/// Format the month into the designated output.
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]),
}
}
/// 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)
}
/// Format the weekday into the designated output.
fn fmt_weekday(
output: &mut impl io::Write,
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],
),
modifier::WeekdayRepr::Long => write(
output,
WEEKDAY_NAMES[date.weekday().number_days_from_monday() as usize],
),
modifier::WeekdayRepr::Sunday => format_number::<_, _, 1>(
output,
date.weekday().number_days_from_sunday() + one_indexed as u8,
modifier::Padding::None,
),
modifier::WeekdayRepr::Monday => format_number::<_, _, 1>(
output,
date.weekday().number_days_from_monday() + one_indexed as u8,
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,
) -> Result<usize, io::Error> {
format_number::<_, _, 2>(
output,
match repr {
modifier::WeekNumberRepr::Iso => date.iso_week(),
modifier::WeekNumberRepr::Sunday => date.sunday_based_week(),
modifier::WeekNumberRepr::Monday => date.monday_based_week(),
},
padding,
)
}
/// Format the year into the designated output.
fn fmt_year(
output: &mut impl io::Write,
date: Date,
modifier::Year {
padding,
repr,
iso_week_based,
sign_is_mandatory,
}: modifier::Year,
) -> Result<usize, io::Error> {
let full_year = if iso_week_based {
date.iso_year_week().0
} else {
date.year()
};
let value = match repr {
modifier::YearRepr::Full => full_year,
modifier::YearRepr::LastTwo => (full_year % 100).abs(),
};
let format_number = match repr {
#[cfg(feature = "large-dates")]
modifier::YearRepr::Full if value.abs() >= 100_000 => format_number::<_, _, 6>,
#[cfg(feature = "large-dates")]
modifier::YearRepr::Full if value.abs() >= 10_000 => format_number::<_, _, 5>,
modifier::YearRepr::Full => format_number::<_, _, 4>,
modifier::YearRepr::LastTwo => format_number::<_, _, 2>,
};
let mut bytes = 0;
if repr != modifier::YearRepr::LastTwo {
if full_year < 0 {
bytes += write(output, &[b'-'])?;
} else if sign_is_mandatory || cfg!(feature = "large-dates") && full_year >= 10_000 {
bytes += write(output, &[b'+'])?;
}
}
bytes += format_number(output, value.unsigned_abs(), padding)?;
Ok(bytes)
}
// endregion date formatters
// region: time formatters
/// Format the hour into the designated output.
fn fmt_hour(
output: &mut impl io::Write,
time: Time,
modifier::Hour {
padding,
is_12_hour_clock,
}: modifier::Hour,
) -> Result<usize, io::Error> {
let value = match (time.hour(), is_12_hour_clock) {
(hour, false) => hour,
(0 | 12, true) => 12,
(hour, true) if hour < 12 => hour,
(hour, true) => hour - 12,
};
format_number::<_, _, 2>(output, value, padding)
}
/// Format the minute into the designated output.
fn fmt_minute(
output: &mut impl io::Write,
time: Time,
modifier::Minute { padding }: modifier::Minute,
) -> Result<usize, io::Error> {
format_number::<_, _, 2>(output, time.minute(), padding)
}
/// Format the period into the designated output.
fn fmt_period(
output: &mut impl io::Write,
time: Time,
modifier::Period {
is_uppercase,
case_sensitive: _, // no effect on formatting
}: modifier::Period,
) -> Result<usize, io::Error> {
match (time.hour() >= 12, is_uppercase) {
(false, false) => write(output, b"am"),
(false, true) => write(output, b"AM"),
(true, false) => write(output, b"pm"),
(true, true) => write(output, b"PM"),
}
}
/// Format the second into the designated output.
fn fmt_second(
output: &mut impl io::Write,
time: Time,
modifier::Second { padding }: modifier::Second,
) -> Result<usize, io::Error> {
format_number::<_, _, 2>(output, time.second(), padding)
}
/// Format the subsecond into the designated output.
fn fmt_subsecond<W: io::Write>(
output: &mut W,
time: Time,
modifier::Subsecond { digits }: modifier::Subsecond,
) -> Result<usize, io::Error> {
use modifier::SubsecondDigits::*;
let nanos = time.nanosecond();
if digits == Nine || (digits == OneOrMore && nanos % 10 != 0) {
format_number_pad_zero::<_, _, 9>(output, nanos)
} else if digits == Eight || (digits == OneOrMore && (nanos / 10) % 10 != 0) {
format_number_pad_zero::<_, _, 8>(output, nanos / 10)
} else if digits == Seven || (digits == OneOrMore && (nanos / 100) % 10 != 0) {
format_number_pad_zero::<_, _, 7>(output, nanos / 100)
} else if digits == Six || (digits == OneOrMore && (nanos / 1_000) % 10 != 0) {
format_number_pad_zero::<_, _, 6>(output, nanos / 1_000)
} else if digits == Five || (digits == OneOrMore && (nanos / 10_000) % 10 != 0) {
format_number_pad_zero::<_, _, 5>(output, nanos / 10_000)
} else if digits == Four || (digits == OneOrMore && (nanos / 100_000) % 10 != 0) {
format_number_pad_zero::<_, _, 4>(output, nanos / 100_000)
} else if digits == Three || (digits == OneOrMore && (nanos / 1_000_000) % 10 != 0) {
format_number_pad_zero::<_, _, 3>(output, nanos / 1_000_000)
} else if digits == Two || (digits == OneOrMore && (nanos / 10_000_000) % 10 != 0) {
format_number_pad_zero::<_, _, 2>(output, nanos / 10_000_000)
} else {
format_number_pad_zero::<_, _, 1>(output, nanos / 100_000_000)
}
}
// endregion time formatters
// region: offset formatters
/// Format the offset hour into the designated output.
fn fmt_offset_hour(
output: &mut impl io::Write,
offset: UtcOffset,
modifier::OffsetHour {
padding,
sign_is_mandatory,
}: modifier::OffsetHour,
) -> Result<usize, io::Error> {
let mut bytes = 0;
if offset.is_negative() {
bytes += write(output, &[b'-'])?;
} else if sign_is_mandatory {
bytes += write(output, &[b'+'])?;
}
bytes += format_number::<_, _, 2>(output, offset.whole_hours().unsigned_abs(), padding)?;
Ok(bytes)
}
/// Format the offset minute into the designated output.
fn fmt_offset_minute(
output: &mut impl io::Write,
offset: UtcOffset,
modifier::OffsetMinute { padding }: modifier::OffsetMinute,
) -> Result<usize, io::Error> {
format_number::<_, _, 2>(output, offset.minutes_past_hour().unsigned_abs(), padding)
}
/// Format the offset second into the designated output.
fn fmt_offset_second(
output: &mut impl io::Write,
offset: UtcOffset,
modifier::OffsetSecond { padding }: modifier::OffsetSecond,
) -> Result<usize, io::Error> {
format_number::<_, _, 2>(output, offset.seconds_past_minute().unsigned_abs(), padding)
}
// endregion offset formatters

262
third_party/rust/time/src/instant.rs поставляемый Normal file
Просмотреть файл

@ -0,0 +1,262 @@
//! The [`Instant`] struct and its associated `impl`s.
use core::cmp::{Ord, Ordering, PartialEq, PartialOrd};
use core::convert::{TryFrom, TryInto};
use core::ops::{Add, Sub};
use core::time::Duration as StdDuration;
use std::borrow::Borrow;
use std::time::Instant as StdInstant;
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`].
#[cfg_attr(__time_03_docs, doc(cfg(feature = "std")))]
#[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
/// # 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
/// # 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
/// # 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.abs_std()).map(Self)
} else {
debug_assert!(duration.is_negative());
self.0.checked_sub(duration.abs_std()).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
/// # 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.abs_std()).map(Self)
} else {
debug_assert!(duration.is_negative());
self.0.checked_add(duration.abs_std()).map(Self)
}
}
// endregion checked arithmetic
/// Obtain the inner [`std::time::Instant`].
///
/// ```rust
/// # 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
impl From<StdInstant> for Instant {
fn from(instant: StdInstant) -> Self {
Self(instant)
}
}
impl From<Instant> for StdInstant {
fn from(instant: Instant) -> Self {
instant.0
}
}
impl Sub for Instant {
type Output = Duration;
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`"),
}
}
}
impl Sub<StdInstant> for Instant {
type Output = Duration;
fn sub(self, other: StdInstant) -> Self::Output {
self - Self(other)
}
}
impl Sub<Instant> for StdInstant {
type Output = Duration;
fn sub(self, other: Instant) -> Self::Output {
Instant(self) - other
}
}
impl Add<Duration> for Instant {
type Output = Self;
fn add(self, duration: Duration) -> Self::Output {
if duration.is_positive() {
Self(self.0 + duration.abs_std())
} else if duration.is_negative() {
Self(self.0 - duration.abs_std())
} else {
self
}
}
}
impl Add<Duration> for StdInstant {
type Output = Self;
fn add(self, duration: Duration) -> Self::Output {
(Instant(self) + duration).0
}
}
impl Add<StdDuration> for Instant {
type Output = Self;
fn add(self, duration: StdDuration) -> Self::Output {
Self(self.0 + duration)
}
}
impl_add_assign!(Instant: Duration, StdDuration);
impl_add_assign!(StdInstant: Duration);
impl Sub<Duration> for Instant {
type Output = Self;
fn sub(self, duration: Duration) -> Self::Output {
if duration.is_positive() {
Self(self.0 - duration.abs_std())
} else if duration.is_negative() {
Self(self.0 + duration.abs_std())
} else {
self
}
}
}
impl Sub<Duration> for StdInstant {
type Output = Self;
fn sub(self, duration: Duration) -> Self::Output {
(Instant(self) - duration).0
}
}
impl Sub<StdDuration> for Instant {
type Output = Self;
fn sub(self, duration: StdDuration) -> Self::Output {
Self(self.0 - duration)
}
}
impl_sub_assign!(Instant: Duration, StdDuration);
impl_sub_assign!(StdInstant: Duration);
impl PartialEq<StdInstant> for Instant {
fn eq(&self, rhs: &StdInstant) -> bool {
self.0.eq(rhs)
}
}
impl PartialEq<Instant> for StdInstant {
fn eq(&self, rhs: &Instant) -> bool {
self.eq(&rhs.0)
}
}
impl PartialOrd<StdInstant> for Instant {
fn partial_cmp(&self, rhs: &StdInstant) -> Option<Ordering> {
self.0.partial_cmp(rhs)
}
}
impl PartialOrd<Instant> for StdInstant {
fn partial_cmp(&self, rhs: &Instant) -> Option<Ordering> {
self.partial_cmp(&rhs.0)
}
}
impl AsRef<StdInstant> for Instant {
fn as_ref(&self) -> &StdInstant {
&self.0
}
}
impl Borrow<StdInstant> for Instant {
fn borrow(&self) -> &StdInstant {
&self.0
}
}
// endregion trait impls

1542
third_party/rust/time/src/lib.rs поставляемый

Разница между файлами не показана из-за своего большого размера Загрузить разницу

114
third_party/rust/time/src/macros.rs поставляемый Normal file
Просмотреть файл

@ -0,0 +1,114 @@
//! Macros to construct statically known values.
/// Construct a [`Date`](crate::Date) with a statically known value.
///
/// The resulting expression can be used in `const` or `static` declarations.
///
/// Three formats are supported: year-week-weekday, year-ordinal, and year-month-day.
///
/// ```rust
/// # use time::{Date, Weekday::*, Month, macros::date};
/// assert_eq!(
/// date!(2020 - W 01 - 3),
/// Date::from_iso_week_date(2020, 1, Wednesday)?
/// );
/// assert_eq!(date!(2020 - 001), Date::from_ordinal_date(2020, 1)?);
/// assert_eq!(
/// date!(2020 - 01 - 01),
/// Date::from_calendar_date(2020, Month::January, 1)?
/// );
/// # Ok::<_, time::Error>(())
/// ```
pub use time_macros::date;
/// Construct a [`PrimitiveDateTime`] or [`OffsetDateTime`] with a statically known value.
///
/// The resulting expression can be used in `const` or `static` declarations.
///
/// The syntax accepted by this macro is the same as [`date!`] and [`time!`], with an optional
/// [`offset!`], all space-separated. If an [`offset!`] is provided, the resulting value will
/// be an [`OffsetDateTime`]; otherwise it will be a [`PrimitiveDateTime`].
///
/// [`OffsetDateTime`]: crate::OffsetDateTime
/// [`PrimitiveDateTime`]: crate::PrimitiveDateTime
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.
///
/// The resulting expression can be used in `const` or `static` declarations, and implements
/// the sealed traits required for both formatting and parsing.
///
/// ```rust
/// # use time::{format_description, macros::format_description};
/// assert_eq!(
/// format_description!("[hour]:[minute]:[second]"),
/// format_description::parse("[hour]:[minute]:[second]")?
/// );
/// # Ok::<_, time::Error>(())
/// ```
///
/// 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).
///
/// [`format_description::parse()`]: crate::format_description::parse()
#[cfg(any(feature = "formatting", feature = "parsing"))]
pub use time_macros::format_description;
/// Construct a [`UtcOffset`](crate::UtcOffset) with a statically known value.
///
/// The resulting expression can be used in `const` or `static` declarations.
///
/// A sign and the hour must be provided; minutes and seconds default to zero. `UTC` (both
/// uppercase and lowercase) is also allowed.
///
/// ```rust
/// # use time::{UtcOffset, macros::offset};
/// assert_eq!(offset!(UTC), UtcOffset::from_hms(0, 0, 0)?);
/// assert_eq!(offset!(utc), UtcOffset::from_hms(0, 0, 0)?);
/// assert_eq!(offset!(+0), UtcOffset::from_hms(0, 0, 0)?);
/// assert_eq!(offset!(+1), UtcOffset::from_hms(1, 0, 0)?);
/// assert_eq!(offset!(-1), UtcOffset::from_hms(-1, 0, 0)?);
/// assert_eq!(offset!(+1:30), UtcOffset::from_hms(1, 30, 0)?);
/// assert_eq!(offset!(-1:30), UtcOffset::from_hms(-1, -30, 0)?);
/// assert_eq!(offset!(+1:30:59), UtcOffset::from_hms(1, 30, 59)?);
/// assert_eq!(offset!(-1:30:59), UtcOffset::from_hms(-1, -30, -59)?);
/// assert_eq!(offset!(+23:59:59), UtcOffset::from_hms(23, 59, 59)?);
/// assert_eq!(offset!(-23:59:59), UtcOffset::from_hms(-23, -59, -59)?);
/// # Ok::<_, time::Error>(())
/// ```
pub use time_macros::offset;
/// Construct a [`Time`](crate::Time) with a statically known value.
///
/// The resulting expression can be used in `const` or `static` declarations.
///
/// Hours and minutes must be provided, while seconds defaults to zero. AM/PM is allowed
/// (either uppercase or lowercase). Any number of subsecond digits may be provided (though any
/// past nine will be discarded).
///
/// All components are validated at compile-time. An error will be raised if any value is
/// invalid.
///
/// ```rust
/// # use time::{Time, macros::time};
/// assert_eq!(time!(0:00), Time::from_hms(0, 0, 0)?);
/// assert_eq!(time!(1:02:03), Time::from_hms(1, 2, 3)?);
/// assert_eq!(
/// time!(1:02:03.004_005_006),
/// Time::from_hms_nano(1, 2, 3, 4_005_006)?
/// );
/// assert_eq!(time!(12:00 am), Time::from_hms(0, 0, 0)?);
/// assert_eq!(time!(1:02:03 am), Time::from_hms(1, 2, 3)?);
/// assert_eq!(
/// time!(1:02:03.004_005_006 am),
/// Time::from_hms_nano(1, 2, 3, 4_005_006)?
/// );
/// assert_eq!(time!(12 pm), Time::from_hms(12, 0, 0)?);
/// assert_eq!(time!(12:00 pm), Time::from_hms(12, 0, 0)?);
/// assert_eq!(time!(1:02:03 pm), Time::from_hms(13, 2, 3)?);
/// assert_eq!(
/// time!(1:02:03.004_005_006 pm),
/// Time::from_hms_nano(13, 2, 3, 4_005_006)?
/// );
/// # Ok::<_, time::Error>(())
/// ```
pub use time_macros::time;

165
third_party/rust/time/src/month.rs поставляемый Normal file
Просмотреть файл

@ -0,0 +1,165 @@
//! The `Month` enum and its associated `impl`s.
use core::convert::TryFrom;
use core::fmt;
use core::num::NonZeroU8;
use core::str::FromStr;
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 {
January = 1,
February = 2,
March = 3,
April = 4,
May = 5,
June = 6,
July = 7,
August = 8,
September = 9,
October = 10,
November = 11,
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),
4 => Ok(April),
5 => Ok(May),
6 => Ok(June),
7 => Ok(July),
8 => Ok(August),
9 => Ok(September),
10 => Ok(October),
11 => Ok(November),
12 => Ok(December),
n => Err(error::ComponentRange {
name: "month",
minimum: 1,
maximum: 12,
value: n as _,
conditional_range: false,
}),
}
}
/// Get the previous month.
///
/// ```rust
/// # use time::Month;
/// assert_eq!(Month::January.previous(), Month::December);
/// ```
pub const fn previous(self) -> Self {
match self {
January => December,
February => January,
March => February,
April => March,
May => April,
June => May,
July => June,
August => July,
September => August,
October => September,
November => October,
December => November,
}
}
/// Get the next month.
///
/// ```rust
/// # use time::Month;
/// assert_eq!(Month::January.next(), Month::February);
/// ```
pub const fn next(self) -> Self {
match self {
January => February,
February => March,
March => April,
April => May,
May => June,
June => July,
July => August,
August => September,
September => October,
October => November,
November => December,
December => January,
}
}
}
impl fmt::Display for Month {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(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 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),
"June" => Ok(June),
"July" => Ok(July),
"August" => Ok(August),
"September" => Ok(September),
"October" => Ok(October),
"November" => Ok(November),
"December" => Ok(December),
_ => Err(error::InvalidVariant),
}
}
}
impl From<Month> for u8 {
fn from(month: Month) -> Self {
month as _
}
}
impl TryFrom<u8> for Month {
type Error = error::ComponentRange;
fn try_from(value: u8) -> Result<Self, Self::Error> {
match NonZeroU8::new(value) {
Some(value) => Self::from_number(value),
None => Err(error::ComponentRange {
name: "month",
minimum: 1,
maximum: 12,
value: 0,
conditional_range: false,
}),
}
}
}

1321
third_party/rust/time/src/offset_date_time.rs поставляемый Normal file

Разница между файлами не показана из-за своего большого размера Загрузить разницу

192
third_party/rust/time/src/parsing/combinator/mod.rs поставляемый Normal file
Просмотреть файл

@ -0,0 +1,192 @@
//! Implementations of the low-level parser combinators.
pub(crate) mod rfc;
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,
}
}
/// Consume the first matching item, returning its associated value.
pub(crate) fn first_match<'a, T>(
options: impl IntoIterator<Item = (&'a [u8], T)>,
case_sensitive: bool,
) -> impl FnMut(&'a [u8]) -> Option<ParsedItem<'a, T>> {
let mut options = options.into_iter();
move |input| {
options.find_map(|(expected, t)| {
if case_sensitive {
Some(ParsedItem(input.strip_prefix(expected)?, t))
} else {
let n = expected.len();
if n <= input.len() {
let (head, tail) = input.split_at(n);
if head.eq_ignore_ascii_case(expected) {
return Some(ParsedItem(tail, t));
}
}
None
}
})
}
}
/// Consume zero or more instances of the provided parser. The parser must return the unit value.
pub(crate) fn zero_or_more<'a, P: Fn(&'a [u8]) -> Option<ParsedItem<'a, ()>>>(
parser: P,
) -> impl FnMut(&'a [u8]) -> ParsedItem<'a, ()> {
move |mut input| {
while let Some(remaining) = parser(input) {
input = remaining.into_inner();
}
ParsedItem(input, ())
}
}
/// Consume one of or more instances of the provided parser. The parser must produce the unit value.
pub(crate) fn one_or_more<'a, P: Fn(&'a [u8]) -> Option<ParsedItem<'a, ()>>>(
parser: P,
) -> impl Fn(&'a [u8]) -> Option<ParsedItem<'a, ()>> {
move |mut input| {
input = parser(input)?.into_inner();
while let Some(remaining) = parser(input) {
input = remaining.into_inner();
}
Some(ParsedItem(input, ()))
}
}
/// Consume between `n` and `m` instances of the provided parser.
pub(crate) fn n_to_m<
'a,
T,
P: Fn(&'a [u8]) -> Option<ParsedItem<'a, T>>,
const N: u8,
const M: u8,
>(
parser: P,
) -> impl Fn(&'a [u8]) -> Option<ParsedItem<'a, &'a [u8]>> {
debug_assert!(M >= N);
move |mut input| {
// We need to keep this to determine the total length eventually consumed.
let orig_input = input;
// Mandatory
for _ in 0..N {
input = parser(input)?.0;
}
// Optional
for _ in N..M {
match parser(input) {
Some(parsed) => input = parsed.0,
None => break,
}
}
Some(ParsedItem(
input,
&orig_input[..(orig_input.len() - input.len())],
))
}
}
/// Consume between `n` and `m` digits, returning the numerical value.
pub(crate) fn n_to_m_digits<T: Integer, const N: u8, const M: u8>(
input: &[u8],
) -> Option<ParsedItem<'_, T>> {
debug_assert!(M >= N);
n_to_m::<_, _, N, M>(any_digit)(input)?.flat_map(|value| value.parse_bytes())
}
/// Consume exactly `n` digits, returning the numerical value.
pub(crate) fn exactly_n_digits<T: Integer, const N: u8>(input: &[u8]) -> Option<ParsedItem<'_, T>> {
n_to_m_digits::<_, N, N>(input)
}
/// Consume exactly `n` digits, returning the numerical value.
pub(crate) fn exactly_n_digits_padded<'a, T: Integer, const N: u8>(
padding: Padding,
) -> impl Fn(&'a [u8]) -> Option<ParsedItem<'a, T>> {
n_to_m_digits_padded::<_, N, N>(padding)
}
/// Consume between `n` and `m` digits, returning the numerical value.
pub(crate) fn n_to_m_digits_padded<'a, T: Integer, const N: u8, const M: u8>(
padding: Padding,
) -> impl Fn(&'a [u8]) -> Option<ParsedItem<'a, T>> {
debug_assert!(M >= N);
move |mut input| match padding {
Padding::None => n_to_m_digits::<_, 1, M>(input),
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;
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,
}
}
ParsedItem(input, &orig_input[..(orig_input.len() - input.len())])
.flat_map(|value| value.parse_bytes())
}
Padding::Zero => n_to_m_digits::<_, N, M>(input),
}
}
/// Consume exactly one digit.
pub(crate) const fn any_digit(input: &[u8]) -> Option<ParsedItem<'_, u8>> {
match input {
[c, remaining @ ..] if c.is_ascii_digit() => Some(ParsedItem(remaining, *c)),
_ => None,
}
}
/// Consume exactly one of the provided ASCII characters.
pub(crate) fn ascii_char<const CHAR: u8>(input: &[u8]) -> Option<ParsedItem<'_, ()>> {
debug_assert!(CHAR.is_ascii_graphic() || CHAR.is_ascii_whitespace());
match input {
[c, remaining @ ..] if *c == CHAR => Some(ParsedItem(remaining, ())),
_ => None,
}
}
/// Consume exactly one of the provided ASCII characters, case-insensitive.
pub(crate) fn ascii_char_ignore_case<const CHAR: u8>(input: &[u8]) -> Option<ParsedItem<'_, ()>> {
debug_assert!(CHAR.is_ascii_graphic() || CHAR.is_ascii_whitespace());
match input {
[c, remaining @ ..] if c.eq_ignore_ascii_case(&CHAR) => Some(ParsedItem(remaining, ())),
_ => None,
}
}
/// Optionally consume an input with a given parser.
pub(crate) fn opt<'a, T>(
parser: impl Fn(&'a [u8]) -> Option<ParsedItem<'a, T>>,
) -> impl Fn(&'a [u8]) -> ParsedItem<'a, Option<T>> {
move |input| match parser(input) {
Some(value) => value.map(Some),
None => ParsedItem(input, None),
}
}

8
third_party/rust/time/src/parsing/combinator/rfc/mod.rs поставляемый Normal file
Просмотреть файл

@ -0,0 +1,8 @@
//! Combinators for rules as defined in an RFC.
//!
//! These rules have been converted strictly following the ABNF syntax as specified in [RFC 2234].
//!
//! [RFC 2234]: https://datatracker.ietf.org/doc/html/rfc2234
pub(crate) mod rfc2234;
pub(crate) mod rfc2822;

13
third_party/rust/time/src/parsing/combinator/rfc/rfc2234.rs поставляемый Normal file
Просмотреть файл

@ -0,0 +1,13 @@
//! Rules defined in [RFC 2234].
//!
//! [RFC 2234]: https://datatracker.ietf.org/doc/html/rfc2234
use crate::parsing::ParsedItem;
/// Consume exactly one space or tab.
pub(crate) const fn wsp(input: &[u8]) -> Option<ParsedItem<'_, ()>> {
match input {
[b' ' | b'\t', rest @ ..] => Some(ParsedItem(rest, ())),
_ => None,
}
}

115
third_party/rust/time/src/parsing/combinator/rfc/rfc2822.rs поставляемый Normal file
Просмотреть файл

@ -0,0 +1,115 @@
//! Rules defined in [RFC 2822].
//!
//! [RFC 2822]: https://datatracker.ietf.org/doc/html/rfc2822
use crate::parsing::combinator::rfc::rfc2234::wsp;
use crate::parsing::combinator::{ascii_char, one_or_more, zero_or_more};
use crate::parsing::ParsedItem;
/// Consume the `fws` rule.
// The full rule is equivalent to /\r\n[ \t]+|[ \t]+(?:\r\n[ \t]+)*/
pub(crate) fn fws(mut input: &[u8]) -> Option<ParsedItem<'_, ()>> {
if let [b'\r', b'\n', rest @ ..] = input {
one_or_more(wsp)(rest)
} else {
input = one_or_more(wsp)(input)?.into_inner();
while let [b'\r', b'\n', rest @ ..] = input {
input = one_or_more(wsp)(rest)?.into_inner();
}
Some(ParsedItem(input, ()))
}
}
/// Consume the `cfws` rule.
// The full rule is equivalent to any combination of `fws` and `comment` so long as it is not empty.
pub(crate) fn cfws(input: &[u8]) -> Option<ParsedItem<'_, ()>> {
one_or_more(|input| fws(input).or_else(|| comment(input)))(input)
}
/// Consume the `comment` rule.
fn comment(mut input: &[u8]) -> Option<ParsedItem<'_, ()>> {
input = ascii_char::<b'('>(input)?.into_inner();
input = zero_or_more(fws)(input).into_inner();
while let Some(rest) = ccontent(input) {
input = rest.into_inner();
input = zero_or_more(fws)(input).into_inner();
}
input = ascii_char::<b')'>(input)?.into_inner();
Some(ParsedItem(input, ()))
}
/// Consume the `ccontent` rule.
fn ccontent(input: &[u8]) -> Option<ParsedItem<'_, ()>> {
ctext(input)
.or_else(|| quoted_pair(input))
.or_else(|| comment(input))
}
/// Consume the `ctext` rule.
#[allow(clippy::unnecessary_lazy_evaluations)] // rust-lang/rust-clippy#8522
fn ctext(input: &[u8]) -> Option<ParsedItem<'_, ()>> {
no_ws_ctl(input).or_else(|| match input {
[33..=39 | 42..=91 | 93..=126, rest @ ..] => Some(ParsedItem(rest, ())),
_ => None,
})
}
/// Consume the `quoted_pair` rule.
fn quoted_pair(mut input: &[u8]) -> Option<ParsedItem<'_, ()>> {
input = ascii_char::<b'\\'>(input)?.into_inner();
let old_input_len = input.len();
input = text(input).into_inner();
// If nothing is parsed, this means we hit the `obs-text` rule and nothing matched. This is
// technically a success, but we should still check the `obs-qp` rule to ensure we consume
// everything possible.
if input.len() == old_input_len {
match input {
[0..=127, rest @ ..] => Some(ParsedItem(rest, ())),
_ => Some(ParsedItem(input, ())),
}
} else {
Some(ParsedItem(input, ()))
}
}
/// Consume the `no_ws_ctl` rule.
const fn no_ws_ctl(input: &[u8]) -> Option<ParsedItem<'_, ()>> {
match input {
[1..=8 | 11..=12 | 14..=31 | 127, rest @ ..] => Some(ParsedItem(rest, ())),
_ => None,
}
}
/// Consume the `text` rule.
fn text<'a>(input: &'a [u8]) -> ParsedItem<'a, ()> {
let new_text = |input: &'a [u8]| match input {
[1..=9 | 11..=12 | 14..=127, rest @ ..] => Some(ParsedItem(rest, ())),
_ => None,
};
let obs_char = |input: &'a [u8]| match input {
// This is technically allowed, but consuming this would mean the rest of the string is
// eagerly consumed without consideration for where the comment actually ends.
[b')', ..] => None,
[0..=9 | 11..=12 | 14..=127, rest @ ..] => Some(rest),
_ => None,
};
let obs_text = |mut input| {
input = zero_or_more(ascii_char::<b'\n'>)(input).into_inner();
input = zero_or_more(ascii_char::<b'\r'>)(input).into_inner();
while let Some(rest) = obs_char(input) {
input = rest;
input = zero_or_more(ascii_char::<b'\n'>)(input).into_inner();
input = zero_or_more(ascii_char::<b'\r'>)(input).into_inner();
}
ParsedItem(input, ())
};
new_text(input).unwrap_or_else(|| obs_text(input))
}

290
third_party/rust/time/src/parsing/component.rs поставляемый Normal file
Просмотреть файл

@ -0,0 +1,290 @@
//! Parsing implementations for all [`Component`](crate::format_description::Component)s.
use core::num::{NonZeroU16, NonZeroU8};
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, opt, sign,
};
use crate::parsing::ParsedItem;
use crate::{Month, Weekday};
// region: date components
/// Parse the "year" component of a `Date`.
pub(crate) fn parse_year(input: &[u8], modifiers: modifier::Year) -> Option<ParsedItem<'_, i32>> {
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::<u32, 4>(modifiers.padding)(input)?;
#[cfg(feature = "large-dates")]
let ParsedItem(input, year) =
n_to_m_digits_padded::<u32, 4, 6>(modifiers.padding)(input)?;
match sign {
Some(b'-') => Some(ParsedItem(input, -(year as i32))),
None if modifiers.sign_is_mandatory || year >= 10_000 => None,
_ => Some(ParsedItem(input, year as i32)),
}
}
modifier::YearRepr::LastTwo => {
Some(exactly_n_digits_padded::<u32, 2>(modifiers.padding)(input)?.map(|v| v as i32))
}
}
}
/// 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(
match modifiers.repr {
modifier::MonthRepr::Numerical => {
return exactly_n_digits_padded::<_, 2>(modifiers.padding)(input)?
.flat_map(|n| Month::from_number(n).ok());
}
modifier::MonthRepr::Long => [
(&b"January"[..], January),
(&b"February"[..], February),
(&b"March"[..], March),
(&b"April"[..], April),
(&b"May"[..], May),
(&b"June"[..], June),
(&b"July"[..], July),
(&b"August"[..], August),
(&b"September"[..], September),
(&b"October"[..], October),
(&b"November"[..], November),
(&b"December"[..], December),
],
modifier::MonthRepr::Short => [
(&b"Jan"[..], January),
(&b"Feb"[..], February),
(&b"Mar"[..], March),
(&b"Apr"[..], April),
(&b"May"[..], May),
(&b"Jun"[..], June),
(&b"Jul"[..], July),
(&b"Aug"[..], August),
(&b"Sep"[..], September),
(&b"Oct"[..], October),
(&b"Nov"[..], November),
(&b"Dec"[..], December),
],
},
modifiers.case_sensitive,
)(input)?;
Some(ParsedItem(remaining, value))
}
/// Parse the "week number" component of a `Date`.
pub(crate) fn parse_week_number(
input: &[u8],
modifiers: modifier::WeekNumber,
) -> Option<ParsedItem<'_, u8>> {
exactly_n_digits_padded::<_, 2>(modifiers.padding)(input)
}
/// Parse the "weekday" component of a `Date`.
pub(crate) fn parse_weekday(
input: &[u8],
modifiers: modifier::Weekday,
) -> Option<ParsedItem<'_, Weekday>> {
first_match(
match (modifiers.repr, modifiers.one_indexed) {
(modifier::WeekdayRepr::Short, _) => [
(&b"Mon"[..], Weekday::Monday),
(&b"Tue"[..], Weekday::Tuesday),
(&b"Wed"[..], Weekday::Wednesday),
(&b"Thu"[..], Weekday::Thursday),
(&b"Fri"[..], Weekday::Friday),
(&b"Sat"[..], Weekday::Saturday),
(&b"Sun"[..], Weekday::Sunday),
],
(modifier::WeekdayRepr::Long, _) => [
(&b"Monday"[..], Weekday::Monday),
(&b"Tuesday"[..], Weekday::Tuesday),
(&b"Wednesday"[..], Weekday::Wednesday),
(&b"Thursday"[..], Weekday::Thursday),
(&b"Friday"[..], Weekday::Friday),
(&b"Saturday"[..], Weekday::Saturday),
(&b"Sunday"[..], Weekday::Sunday),
],
(modifier::WeekdayRepr::Sunday, false) => [
(&b"1"[..], Weekday::Monday),
(&b"2"[..], Weekday::Tuesday),
(&b"3"[..], Weekday::Wednesday),
(&b"4"[..], Weekday::Thursday),
(&b"5"[..], Weekday::Friday),
(&b"6"[..], Weekday::Saturday),
(&b"0"[..], Weekday::Sunday),
],
(modifier::WeekdayRepr::Sunday, true) => [
(&b"2"[..], Weekday::Monday),
(&b"3"[..], Weekday::Tuesday),
(&b"4"[..], Weekday::Wednesday),
(&b"5"[..], Weekday::Thursday),
(&b"6"[..], Weekday::Friday),
(&b"7"[..], Weekday::Saturday),
(&b"1"[..], Weekday::Sunday),
],
(modifier::WeekdayRepr::Monday, false) => [
(&b"0"[..], Weekday::Monday),
(&b"1"[..], Weekday::Tuesday),
(&b"2"[..], Weekday::Wednesday),
(&b"3"[..], Weekday::Thursday),
(&b"4"[..], Weekday::Friday),
(&b"5"[..], Weekday::Saturday),
(&b"6"[..], Weekday::Sunday),
],
(modifier::WeekdayRepr::Monday, true) => [
(&b"1"[..], Weekday::Monday),
(&b"2"[..], Weekday::Tuesday),
(&b"3"[..], Weekday::Wednesday),
(&b"4"[..], Weekday::Thursday),
(&b"5"[..], Weekday::Friday),
(&b"6"[..], Weekday::Saturday),
(&b"7"[..], Weekday::Sunday),
],
},
modifiers.case_sensitive,
)(input)
}
/// Parse the "ordinal" component of a `Date`.
pub(crate) fn parse_ordinal(
input: &[u8],
modifiers: modifier::Ordinal,
) -> Option<ParsedItem<'_, NonZeroU16>> {
exactly_n_digits_padded::<_, 3>(modifiers.padding)(input)
}
/// Parse the "day" component of a `Date`.
pub(crate) fn parse_day(
input: &[u8],
modifiers: modifier::Day,
) -> Option<ParsedItem<'_, NonZeroU8>> {
exactly_n_digits_padded::<_, 2>(modifiers.padding)(input)
}
// endregion date components
// region: time components
/// Indicate whether the hour is "am" or "pm".
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum Period {
#[allow(clippy::missing_docs_in_private_items)]
Am,
#[allow(clippy::missing_docs_in_private_items)]
Pm,
}
/// Parse the "hour" component of a `Time`.
pub(crate) fn parse_hour(input: &[u8], modifiers: modifier::Hour) -> Option<ParsedItem<'_, u8>> {
exactly_n_digits_padded::<_, 2>(modifiers.padding)(input)
}
/// Parse the "minute" component of a `Time`.
pub(crate) fn parse_minute(
input: &[u8],
modifiers: modifier::Minute,
) -> Option<ParsedItem<'_, u8>> {
exactly_n_digits_padded::<_, 2>(modifiers.padding)(input)
}
/// Parse the "second" component of a `Time`.
pub(crate) fn parse_second(
input: &[u8],
modifiers: modifier::Second,
) -> Option<ParsedItem<'_, u8>> {
exactly_n_digits_padded::<_, 2>(modifiers.padding)(input)
}
/// Parse the "period" component of a `Time`. Required if the hour is on a 12-hour clock.
pub(crate) fn parse_period(
input: &[u8],
modifiers: modifier::Period,
) -> Option<ParsedItem<'_, Period>> {
first_match(
if modifiers.is_uppercase {
[(&b"AM"[..], Period::Am), (&b"PM"[..], Period::Pm)]
} else {
[(&b"am"[..], Period::Am), (&b"pm"[..], Period::Pm)]
},
modifiers.case_sensitive,
)(input)
}
/// Parse the "subsecond" component of a `Time`.
pub(crate) fn parse_subsecond(
input: &[u8],
modifiers: modifier::Subsecond,
) -> Option<ParsedItem<'_, u32>> {
use modifier::SubsecondDigits::*;
Some(match modifiers.digits {
One => exactly_n_digits::<u32, 1>(input)?.map(|v| v * 100_000_000),
Two => exactly_n_digits::<u32, 2>(input)?.map(|v| v * 10_000_000),
Three => exactly_n_digits::<u32, 3>(input)?.map(|v| v * 1_000_000),
Four => exactly_n_digits::<u32, 4>(input)?.map(|v| v * 100_000),
Five => exactly_n_digits::<u32, 5>(input)?.map(|v| v * 10_000),
Six => exactly_n_digits::<u32, 6>(input)?.map(|v| v * 1_000),
Seven => exactly_n_digits::<u32, 7>(input)?.map(|v| v * 100),
Eight => exactly_n_digits::<u32, 8>(input)?.map(|v| v * 10),
Nine => exactly_n_digits::<u32, 9>(input)?,
OneOrMore => {
let ParsedItem(mut input, mut value) =
any_digit(input)?.map(|v| (v - b'0') as 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;
input = new_input;
multiplier /= 10;
}
ParsedItem(input, value)
}
})
}
// endregion time components
// region: offset components
/// Parse the "hour" component of a `UtcOffset`.
pub(crate) fn parse_offset_hour(
input: &[u8],
modifiers: modifier::OffsetHour,
) -> Option<ParsedItem<'_, i8>> {
let ParsedItem(input, sign) = opt(sign)(input);
let ParsedItem(input, hour) = exactly_n_digits_padded::<u8, 2>(modifiers.padding)(input)?;
match sign {
Some(b'-') => Some(ParsedItem(input, -(hour as i8))),
None if modifiers.sign_is_mandatory => None,
_ => Some(ParsedItem(input, hour as i8)),
}
}
/// 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::<u8, 2>(modifiers.padding)(input)?
.map(|offset_minute| offset_minute as _),
)
}
/// 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::<u8, 2>(modifiers.padding)(input)?
.map(|offset_second| offset_second as _),
)
}
// endregion offset components

49
third_party/rust/time/src/parsing/mod.rs поставляемый Normal file
Просмотреть файл

@ -0,0 +1,49 @@
//! Parsing for various types.
pub(crate) mod combinator;
pub(crate) mod component;
pub(crate) mod parsable;
mod parsed;
pub(crate) mod shim;
pub use self::parsable::Parsable;
pub use self::parsed::Parsed;
/// An item that has been parsed. Represented as a `(remaining, value)` pair.
#[derive(Debug)]
pub(crate) struct ParsedItem<'a, T>(pub(crate) &'a [u8], pub(crate) T);
impl<'a, T> ParsedItem<'a, T> {
/// Map the value to a new value, preserving the remaining input.
pub(crate) fn map<U>(self, f: impl FnOnce(T) -> U) -> ParsedItem<'a, U> {
ParsedItem(self.0, f(self.1))
}
/// Map the value to a new, optional value, preserving the remaining input.
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)
}
}
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
}
}
impl<'a> ParsedItem<'a, Option<()>> {
/// Discard the potential unit value, returning the remaining input.
#[must_use = "this returns the remaining input"]
pub(crate) const fn into_inner(self) -> &'a [u8] {
self.0
}
}

492
third_party/rust/time/src/parsing/parsable.rs поставляемый Normal file
Просмотреть файл

@ -0,0 +1,492 @@
//! A trait that can be used to parse an item from an input.
use core::convert::TryInto;
use core::ops::Deref;
use crate::error::TryFromParsed;
use crate::format_description::well_known::{Rfc2822, Rfc3339};
use crate::format_description::FormatItem;
use crate::parsing::{Parsed, ParsedItem};
use crate::{error, Date, Month, OffsetDateTime, PrimitiveDateTime, Time, UtcOffset, Weekday};
/// A type that can be parsed.
#[cfg_attr(__time_03_docs, doc(notable_trait))]
pub trait Parsable: sealed::Sealed {}
impl Parsable for FormatItem<'_> {}
impl Parsable for [FormatItem<'_>] {}
impl Parsable for Rfc2822 {}
impl Parsable for Rfc3339 {}
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::*;
/// Parse the item using a format description and an input.
#[cfg_attr(__time_03_docs, doc(cfg(feature = "parsing")))]
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,
) -> Result<&'a [u8], error::Parse>;
/// 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)
}
}
/// 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 [`PrimitiveDateTime`] from the format description.
fn parse_date_time(&self, input: &[u8]) -> 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<'_> {
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<'_>] {
fn parse_into<'a>(
&self,
input: &'a [u8],
parsed: &mut Parsed,
) -> Result<&'a [u8], error::Parse> {
Ok(parsed.parse_items(input, self)?)
}
}
impl<T: Deref> sealed::Sealed for T
where
T::Target: sealed::Sealed,
{
fn parse_into<'a>(
&self,
input: &'a [u8],
parsed: &mut Parsed,
) -> Result<&'a [u8], error::Parse> {
self.deref().parse_into(input, parsed)
}
}
// endregion custom formats
// region: well-known formats
impl sealed::Sealed for Rfc2822 {
fn parse_into<'a>(
&self,
input: &'a [u8],
parsed: &mut Parsed,
) -> 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(
[
(&b"Mon"[..], Weekday::Monday),
(&b"Tue"[..], Weekday::Tuesday),
(&b"Wed"[..], Weekday::Wednesday),
(&b"Thu"[..], Weekday::Thursday),
(&b"Fri"[..], Weekday::Friday),
(&b"Sat"[..], Weekday::Saturday),
(&b"Sun"[..], 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();
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"[..], Month::January),
(&b"Feb"[..], Month::February),
(&b"Mar"[..], Month::March),
(&b"Apr"[..], Month::April),
(&b"May"[..], Month::May),
(&b"Jun"[..], Month::June),
(&b"Jul"[..], Month::July),
(&b"Aug"[..], Month::August),
(&b"Sep"[..], Month::September),
(&b"Oct"[..], Month::October),
(&b"Nov"[..], Month::November),
(&b"Dec"[..], Month::December),
],
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::<u32, 4>(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 _)))
.ok_or(InvalidComponent("year"))?;
let input = fws(input).ok_or(InvalidLiteral)?.into_inner();
input
}
None => {
let input = exactly_n_digits::<u32, 2>(input)
.and_then(|item| {
item.map(|year| if year < 50 { year + 2000 } else { year + 1900 })
.map(|year| year as _)
.consume_value(|value| parsed.set_year(value))
})
.ok_or(InvalidComponent("year"))?;
let input = cfws(input).ok_or(InvalidLiteral)?.into_inner();
input
}
};
let input = exactly_n_digits::<_, 2>(input)
.and_then(|item| item.consume_value(|value| parsed.set_hour_24(value)))
.ok_or(InvalidComponent("hour"))?;
let input = opt(cfws)(input).into_inner();
let input = colon(input).ok_or(InvalidLiteral)?.into_inner();
let input = opt(cfws)(input).into_inner();
let input = exactly_n_digits::<_, 2>(input)
.and_then(|item| item.consume_value(|value| parsed.set_minute(value)))
.ok_or(InvalidComponent("minute"))?;
let input = if let Some(input) = colon(opt(cfws)(input).into_inner()) {
let input = input.into_inner(); // discard the colon
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"))?;
let input = cfws(input).ok_or(InvalidLiteral)?.into_inner();
input
} else {
cfws(input).ok_or(InvalidLiteral)?.into_inner()
};
// The RFC explicitly allows leap seconds.
parsed.set_leap_second_allowed(true);
#[allow(clippy::unnecessary_lazy_evaluations)] // rust-lang/rust-clippy#8522
let zone_literal = first_match(
[
(&b"UT"[..], 0),
(&b"GMT"[..], 0),
(&b"EST"[..], -5),
(&b"EDT"[..], -4),
(&b"CST"[..], -6),
(&b"CDT"[..], -5),
(&b"MST"[..], -7),
(&b"MDT"[..], -6),
(&b"PST"[..], -8),
(&b"PDT"[..], -7),
],
false,
)(input)
.or_else(|| match input {
[
b'a'..=b'i' | b'k'..=b'z' | b'A'..=b'I' | b'K'..=b'Z',
rest @ ..,
] => Some(ParsedItem(rest, 0)),
_ => None,
});
if let Some(zone_literal) = zone_literal {
let input = zone_literal
.consume_value(|value| parsed.set_offset_hour(value))
.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::<u8, 2>(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))
})
.ok_or(InvalidComponent("offset hour"))?;
let input = exactly_n_digits::<u8, 2>(input)
.and_then(|item| {
item.consume_value(|value| parsed.set_offset_minute_signed(value as _))
})
.ok_or(InvalidComponent("offset minute"))?;
Ok(input)
}
}
impl sealed::Sealed for Rfc3339 {
fn parse_into<'a>(
&self,
input: &'a [u8],
parsed: &mut Parsed,
) -> 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::<u32, 4>(input)
.and_then(|item| item.consume_value(|value| parsed.set_year(value as _)))
.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"))?;
let input = ascii_char_ignore_case::<b'T'>(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"))?;
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_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);
let mut multiplier = 10_000_000;
while let Some(ParsedItem(new_input, digit)) = any_digit(input) {
value += (digit - b'0') as 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_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::<u8, 2>(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))
})
.ok_or(InvalidComponent("offset hour"))?;
let input = colon(input).ok_or(InvalidLiteral)?.into_inner();
let input = exactly_n_digits::<u8, 2>(input)
.and_then(|item| {
item.consume_value(|value| parsed.set_offset_minute_signed(value as _))
})
.ok_or(InvalidComponent("offset minute"))?;
Ok(input)
}
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::<u32, 4>(input).ok_or(InvalidComponent("year"))?;
let input = dash(input).ok_or(InvalidLiteral)?.into_inner();
let ParsedItem(input, month) =
exactly_n_digits::<_, 2>(input).ok_or(InvalidComponent("month"))?;
let input = dash(input).ok_or(InvalidLiteral)?.into_inner();
let ParsedItem(input, day) =
exactly_n_digits::<_, 2>(input).ok_or(InvalidComponent("day"))?;
let input = ascii_char_ignore_case::<b'T'>(input)
.ok_or(InvalidLiteral)?
.into_inner();
let ParsedItem(input, hour) =
exactly_n_digits::<_, 2>(input).ok_or(InvalidComponent("hour"))?;
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);
let mut multiplier = 10_000_000;
while let Some(ParsedItem(new_input, digit)) = any_digit(input) {
value += (digit - b'0') as 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::<u8, 2>(input).ok_or(InvalidComponent("offset hour"))?;
let input = colon(input).ok_or(InvalidLiteral)?.into_inner();
let ParsedItem(input, offset_minute) =
exactly_n_digits::<u8, 2>(input).ok_or(InvalidComponent("offset minute"))?;
UtcOffset::from_hms(
if offset_sign == b'-' {
-(offset_hour as i8)
} else {
offset_hour as _
},
offset_minute as _,
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);
}
// 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 dt = Month::from_number(month)
.and_then(|month| Date::from_calendar_date(year as _, month, day))
.and_then(|date| date.with_hms_nano(hour, minute, second, nanosecond))
.map(|date| date.assume_offset(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,
conditional_range: true,
},
)));
}
Ok(dt)
}
}
// endregion well-known formats

625
third_party/rust/time/src/parsing/parsed.rs поставляемый Normal file
Просмотреть файл

@ -0,0 +1,625 @@
//! Information parsed from an input and format description.
use core::convert::{TryFrom, TryInto};
use core::num::{NonZeroU16, NonZeroU8};
use crate::error::TryFromParsed::InsufficientInformation;
use crate::format_description::modifier::{WeekNumberRepr, YearRepr};
use crate::format_description::{Component, FormatItem};
use crate::parsing::component::{
parse_day, parse_hour, parse_minute, parse_month, parse_offset_hour, parse_offset_minute,
parse_offset_second, parse_ordinal, parse_period, parse_second, parse_subsecond,
parse_week_number, parse_weekday, parse_year, Period,
};
use crate::parsing::ParsedItem;
use crate::{error, Date, Month, OffsetDateTime, PrimitiveDateTime, Time, UtcOffset, Weekday};
/// 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 {
/// Calendar year.
year: Option<i32>,
/// The last two digits of the calendar year.
year_last_two: Option<u8>,
/// Year of the [ISO week date](https://en.wikipedia.org/wiki/ISO_week_date).
iso_year: Option<i32>,
/// The last two digits of the ISO week year.
iso_year_last_two: Option<u8>,
/// 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: Option<u8>,
/// Week of the year, where week one begins on the first Monday of the calendar year.
monday_week_number: Option<u8>,
/// Week of the year, where week one is the Monday-to-Sunday period containing January 4.
iso_week_number: Option<NonZeroU8>,
/// Day of the week.
weekday: Option<Weekday>,
/// Day of the year.
ordinal: Option<NonZeroU16>,
/// Day of the month.
day: Option<NonZeroU8>,
/// Hour within the day.
hour_24: Option<u8>,
/// 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>,
/// Whether the `hour_12` field indicates a time that "PM".
hour_12_is_pm: Option<bool>,
/// Minute within the hour.
minute: Option<u8>,
/// Second within the minute.
second: Option<u8>,
/// Nanosecond within the second.
subsecond: Option<u32>,
/// Whole hours of the UTC offset.
offset_hour: Option<i8>,
/// Minutes within the hour of the UTC offset.
offset_minute: Option<i8>,
/// Seconds within the minute of the UTC offset.
offset_second: Option<i8>,
/// Indicates whether a leap second is permitted to be parsed. This is required by some
/// well-known formats.
leap_second_allowed: bool,
}
impl Parsed {
/// Create a new instance of `Parsed` with no information known.
pub const fn new() -> Self {
Self {
year: None,
year_last_two: None,
iso_year: None,
iso_year_last_two: None,
month: None,
sunday_week_number: None,
monday_week_number: None,
iso_week_number: None,
weekday: None,
ordinal: None,
day: None,
hour_24: None,
hour_12: None,
hour_12_is_pm: None,
minute: None,
second: None,
subsecond: None,
offset_hour: None,
offset_minute: None,
offset_second: None,
leap_second_allowed: false,
}
}
/// Parse a single [`FormatItem`], mutating the struct. The remaining input is returned as the
/// `Ok` value.
///
/// If a [`FormatItem::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: &FormatItem<'_>,
) -> Result<&'a [u8], error::ParseFromDescription> {
match item {
FormatItem::Literal(literal) => Self::parse_literal(input, literal),
FormatItem::Component(component) => self.parse_component(input, *component),
FormatItem::Compound(compound) => self.parse_items(input, compound),
FormatItem::Optional(item) => self.parse_item(input, item).or(Ok(input)),
FormatItem::First(items) => {
let mut first_err = None;
for item in items.iter() {
match self.parse_item(input, item) {
Ok(remaining_input) => return Ok(remaining_input),
Err(err) if first_err.is_none() => first_err = Some(err),
Err(_) => {}
}
}
match first_err {
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),
}
}
}
}
/// Parse a sequence of [`FormatItem`]s, mutating the struct. The remaining input is returned as
/// the `Ok` value.
///
/// This method will fail if any of the contained [`FormatItem`]s fail to parse. `self` will not
/// be mutated in this instance.
pub fn parse_items<'a>(
&mut self,
mut input: &'a [u8],
items: &[FormatItem<'_>],
) -> 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)?;
}
*self = this;
Ok(input)
}
/// Parse a literal byte sequence. The remaining input is returned as the `Ok` value.
pub fn parse_literal<'a>(
input: &'a [u8],
literal: &[u8],
) -> Result<&'a [u8], error::ParseFromDescription> {
input
.strip_prefix(literal)
.ok_or(error::ParseFromDescription::InvalidLiteral)
}
/// Parse a single component, mutating the struct. The remaining input is returned as the `Ok`
/// value.
pub fn parse_component<'a>(
&mut self,
input: &'a [u8],
component: Component,
) -> Result<&'a [u8], error::ParseFromDescription> {
use error::ParseFromDescription::InvalidComponent;
match component {
Component::Day(modifiers) => parse_day(input, modifiers)
.and_then(|parsed| parsed.consume_value(|value| self.set_day(value)))
.ok_or(InvalidComponent("day")),
Component::Month(modifiers) => parse_month(input, modifiers)
.and_then(|parsed| parsed.consume_value(|value| self.set_month(value)))
.ok_or(InvalidComponent("month")),
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 => {
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),
}
.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 _),
}
.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 {
self.set_hour_24(value)
}
.ok_or(InvalidComponent("hour"))?;
Ok(remaining)
}
Component::Minute(modifiers) => parse_minute(input, modifiers)
.and_then(|parsed| parsed.consume_value(|value| self.set_minute(value)))
.ok_or(InvalidComponent("minute")),
Component::Period(modifiers) => parse_period(input, modifiers)
.and_then(|parsed| {
parsed.consume_value(|value| self.set_hour_12_is_pm(value == Period::Pm))
})
.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| self.set_offset_hour(value)))
.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| {
parsed.consume_value(|value| self.set_offset_second_signed(value))
})
.ok_or(InvalidComponent("offset second")),
}
}
}
/// Generate getters for each of the fields.
macro_rules! getters {
($($name:ident: $ty:ty),+ $(,)?) => {$(
/// Obtain the named component.
pub const fn $name(&self) -> Option<$ty> {
self.$name
}
)*}
}
/// Getter methods
impl Parsed {
getters! {
year: i32,
year_last_two: u8,
iso_year: i32,
iso_year_last_two: u8,
month: Month,
sunday_week_number: u8,
monday_week_number: u8,
iso_week_number: NonZeroU8,
weekday: Weekday,
ordinal: NonZeroU16,
day: NonZeroU8,
hour_24: u8,
hour_12: NonZeroU8,
hour_12_is_pm: bool,
minute: u8,
second: u8,
subsecond: u32,
offset_hour: i8,
}
/// Obtain the absolute value of the offset minute.
#[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`.
pub const fn offset_minute_signed(&self) -> Option<i8> {
self.offset_minute
}
/// Obtain the absolute value of the offset second.
#[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`.
pub const fn offset_second_signed(&self) -> Option<i8> {
self.offset_second
}
/// Obtain whether leap seconds are permitted in the current format.
pub(crate) const fn leap_second_allowed(&self) -> bool {
self.leap_second_allowed
}
}
/// Generate setters for each of the fields.
///
/// This macro should only be used for fields where the value is not validated beyond its type.
macro_rules! setters {
($($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(())
}
)*}
}
/// 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! {
set_year year: i32,
set_year_last_two year_last_two: u8,
set_iso_year iso_year: i32,
set_iso_year_last_two iso_year_last_two: u8,
set_month month: Month,
set_sunday_week_number sunday_week_number: u8,
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,
set_hour_24 hour_24: u8,
set_hour_12 hour_12: NonZeroU8,
set_hour_12_is_pm hour_12_is_pm: bool,
set_minute minute: u8,
set_second second: u8,
set_subsecond subsecond: u32,
set_offset_hour offset_hour: i8,
}
/// Set the named component.
#[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 {
None
} else {
self.set_offset_minute_signed(value as _)
}
}
/// Set the `offset_minute` component.
pub fn set_offset_minute_signed(&mut self, value: i8) -> Option<()> {
self.offset_minute = Some(value);
Some(())
}
/// Set the named component.
#[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 {
None
} else {
self.set_offset_second_signed(value as _)
}
}
/// Set the `offset_second` component.
pub fn set_offset_second_signed(&mut self, value: i8) -> Option<()> {
self.offset_second = Some(value);
Some(())
}
/// Set the leap second allowed flag.
pub(crate) fn set_leap_second_allowed(&mut self, value: bool) {
self.leap_second_allowed = value;
}
}
/// 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 {
($($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)
}
)*}
}
/// 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! {
with_year year: i32,
with_year_last_two year_last_two: u8,
with_iso_year iso_year: i32,
with_iso_year_last_two iso_year_last_two: u8,
with_month month: Month,
with_sunday_week_number sunday_week_number: u8,
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,
with_hour_24 hour_24: u8,
with_hour_12 hour_12: NonZeroU8,
with_hour_12_is_pm hour_12_is_pm: bool,
with_minute minute: u8,
with_second second: u8,
with_subsecond subsecond: u32,
with_offset_hour offset_hour: i8,
}
/// Set the named component and return `self`.
#[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 = Some(value);
Some(self)
}
/// Set the named component and return `self`.
#[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 = Some(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_ {
(_ => $catch_all:expr $(,)?) => {
$catch_all
};
(($($name:ident),* $(,)?) => $arm:expr, $($rest:tt)*) => {
if let ($(Some($name)),*) = ($(parsed.$name()),*) {
$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() {
Weekday::Monday => 7,
Weekday::Tuesday => 1,
Weekday::Wednesday => 2,
Weekday::Thursday => 3,
Weekday::Friday => 4,
Weekday::Saturday => 5,
Weekday::Sunday => 6,
}
}
// TODO Only the basics have been covered. There are many other valid values that are not
// currently constructed from the information known.
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
- adjustment(year)
+ 1) as u16,
)?),
(year, monday_week_number, weekday) => Ok(Self::from_ordinal_date(
year,
(monday_week_number as i16 * 7 + weekday.number_days_from_monday() as i16
- adjustment(year)
+ 1) as u16,
)?),
_ => 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)?)
}
}
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);
Self::from_hms(hour, minute, second).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";
} else if err.name == "seconds" {
err.name = "offset second";
}
err.into()
})
}
}
impl TryFrom<Parsed> for PrimitiveDateTime {
type Error = error::TryFromParsed;
fn try_from(parsed: Parsed) -> Result<Self, Self::Error> {
Ok(Self::new(parsed.try_into()?, parsed.try_into()?))
}
}
impl TryFrom<Parsed> for OffsetDateTime {
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> {
// 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.leap_second_allowed() && 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 = PrimitiveDateTime::try_from(parsed)?.assume_offset(parsed.try_into()?);
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,
},
));
}
Ok(dt)
}
}

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше