forked from time-rs/time
-
Notifications
You must be signed in to change notification settings - Fork 0
/
mod.rs
303 lines (273 loc) · 10.1 KB
/
mod.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
//! 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, vec::Vec};
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]),
}
impl<'a> FormatItem<'a> {
/// Create an OwnedFormatItem for this FormatItem.
/// Due to the self-referential nature of this struct, std::borrow::ToOwned cannot be used.
#[cfg(feature = "alloc")]
pub fn make_owned(&self) -> OwnedFormatItem {
self.into()
}
}
#[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
}
}
/// A complete description of how to format and parse a type which owns its components.
#[non_exhaustive]
#[cfg(feature = "alloc")]
#[derive(Clone, PartialEq, Eq)]
pub enum OwnedFormatItem {
/// Bytes that are formatted as-is.
///
/// **Note**: If you call the `format` method that returns a `String`, these bytes will be
/// passed through `String::from_utf8_lossy`.
Literal(Vec<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(Vec<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(Box<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(Vec<Self>),
}
#[cfg(feature = "alloc")]
impl fmt::Debug for OwnedFormatItem {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
OwnedFormatItem::Literal(literal) => f.write_str(&String::from_utf8_lossy(literal)),
OwnedFormatItem::Component(component) => component.fmt(f),
OwnedFormatItem::Compound(compound) => compound.fmt(f),
OwnedFormatItem::Optional(item) => f.debug_tuple("Optional").field(item).finish(),
OwnedFormatItem::First(items) => f.debug_tuple("First").field(items).finish(),
}
}
}
#[cfg(feature = "alloc")]
impl<'a> From<&FormatItem<'a>> for OwnedFormatItem {
fn from(fi: &FormatItem<'a>) -> Self {
match fi {
FormatItem::Literal(lit) => OwnedFormatItem::Literal(lit.to_vec()),
FormatItem::Component(c) => OwnedFormatItem::Component(*c),
FormatItem::Compound(l) => {
OwnedFormatItem::Compound(l.into_iter().map(|i| i.into()).collect())
}
FormatItem::Optional(item) => OwnedFormatItem::Optional(Box::new((*item).into())),
FormatItem::First(items) => {
OwnedFormatItem::First(items.into_iter().map(|i| i.into()).collect())
}
}
}
}
#[cfg(feature = "alloc")]
impl From<Component> for OwnedFormatItem {
fn from(component: Component) -> Self {
Self::Component(component)
}
}
#[cfg(feature = "alloc")]
impl TryFrom<OwnedFormatItem> for Component {
type Error = error::DifferentVariant;
fn try_from(value: OwnedFormatItem) -> Result<Self, Self::Error> {
match value {
OwnedFormatItem::Component(component) => Ok(component),
_ => Err(error::DifferentVariant),
}
}
}
#[cfg(feature = "alloc")]
impl From<Vec<OwnedFormatItem>> for OwnedFormatItem {
fn from(items: Vec<OwnedFormatItem>) -> OwnedFormatItem {
OwnedFormatItem::Compound(items)
}
}
#[cfg(feature = "alloc")]
impl TryFrom<OwnedFormatItem> for Vec<OwnedFormatItem> {
type Error = error::DifferentVariant;
fn try_from(value: OwnedFormatItem) -> Result<Self, Self::Error> {
match value {
OwnedFormatItem::Compound(items) => Ok(items),
_ => Err(error::DifferentVariant),
}
}
}
#[cfg(feature = "alloc")]
impl PartialEq<Component> for OwnedFormatItem {
fn eq(&self, rhs: &Component) -> bool {
matches!(self, OwnedFormatItem::Component(component) if component == rhs)
}
}
#[cfg(feature = "alloc")]
impl PartialEq<OwnedFormatItem> for Component {
fn eq(&self, rhs: &OwnedFormatItem) -> bool {
rhs == self
}
}