1
//! Various modifiers for components.
2

            
3
use core::num::NonZeroU16;
4

            
5
// region: date modifiers
6
/// Day of the month.
7
#[non_exhaustive]
8
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
9
pub struct Day {
10
    /// The padding to obtain the minimum width.
11
    pub padding: Padding,
12
}
13

            
14
/// The representation of a month.
15
#[non_exhaustive]
16
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
17
pub enum MonthRepr {
18
    /// The number of the month (January is 1, December is 12).
19
    Numerical,
20
    /// The long form of the month name (e.g. "January").
21
    Long,
22
    /// The short form of the month name (e.g. "Jan").
23
    Short,
24
}
25

            
26
/// Month of the year.
27
#[non_exhaustive]
28
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
29
pub struct Month {
30
    /// The padding to obtain the minimum width.
31
    pub padding: Padding,
32
    /// What form of representation should be used?
33
    pub repr: MonthRepr,
34
    /// Is the value case sensitive when parsing?
35
    pub case_sensitive: bool,
36
}
37

            
38
/// Ordinal day of the year.
39
#[non_exhaustive]
40
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
41
pub struct Ordinal {
42
    /// The padding to obtain the minimum width.
43
    pub padding: Padding,
44
}
45

            
46
/// The representation used for the day of the week.
47
#[non_exhaustive]
48
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
49
pub enum WeekdayRepr {
50
    /// The short form of the weekday (e.g. "Mon").
51
    Short,
52
    /// The long form of the weekday (e.g. "Monday").
53
    Long,
54
    /// A numerical representation using Sunday as the first day of the week.
55
    ///
56
    /// Sunday is either 0 or 1, depending on the other modifier's value.
57
    Sunday,
58
    /// A numerical representation using Monday as the first day of the week.
59
    ///
60
    /// Monday is either 0 or 1, depending on the other modifier's value.
61
    Monday,
62
}
63

            
64
/// Day of the week.
65
#[non_exhaustive]
66
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
67
pub struct Weekday {
68
    /// What form of representation should be used?
69
    pub repr: WeekdayRepr,
70
    /// When using a numerical representation, should it be zero or one-indexed?
71
    pub one_indexed: bool,
72
    /// Is the value case sensitive when parsing?
73
    pub case_sensitive: bool,
74
}
75

            
76
/// The representation used for the week number.
77
#[non_exhaustive]
78
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
79
pub enum WeekNumberRepr {
80
    /// Week 1 is the week that contains January 4.
81
    Iso,
82
    /// Week 1 begins on the first Sunday of the calendar year.
83
    Sunday,
84
    /// Week 1 begins on the first Monday of the calendar year.
85
    Monday,
86
}
87

            
88
/// Week within the year.
89
#[non_exhaustive]
90
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
91
pub struct WeekNumber {
92
    /// The padding to obtain the minimum width.
93
    pub padding: Padding,
94
    /// What kind of representation should be used?
95
    pub repr: WeekNumberRepr,
96
}
97

            
98
/// The representation used for a year value.
99
#[non_exhaustive]
100
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
101
pub enum YearRepr {
102
    /// The full value of the year.
103
    Full,
104
    /// Only the last two digits of the year.
105
    LastTwo,
106
}
107

            
108
/// Year of the date.
109
#[non_exhaustive]
110
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
111
pub struct Year {
112
    /// The padding to obtain the minimum width.
113
    pub padding: Padding,
114
    /// What kind of representation should be used?
115
    pub repr: YearRepr,
116
    /// Whether the value is based on the ISO week number or the Gregorian calendar.
117
    pub iso_week_based: bool,
118
    /// Whether the `+` sign is present when a positive year contains fewer than five digits.
119
    pub sign_is_mandatory: bool,
120
}
121
// endregion date modifiers
122

            
123
// region: time modifiers
124
/// Hour of the day.
125
#[non_exhaustive]
126
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
127
pub struct Hour {
128
    /// The padding to obtain the minimum width.
129
    pub padding: Padding,
130
    /// Is the hour displayed using a 12 or 24-hour clock?
131
    pub is_12_hour_clock: bool,
132
}
133

            
134
/// Minute within the hour.
135
#[non_exhaustive]
136
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
137
pub struct Minute {
138
    /// The padding to obtain the minimum width.
139
    pub padding: Padding,
140
}
141

            
142
/// AM/PM part of the time.
143
#[non_exhaustive]
144
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
145
pub struct Period {
146
    /// Is the period uppercase or lowercase?
147
    pub is_uppercase: bool,
148
    /// Is the value case sensitive when parsing?
149
    ///
150
    /// Note that when `false`, the `is_uppercase` field has no effect on parsing behavior.
151
    pub case_sensitive: bool,
152
}
153

            
154
/// Second within the minute.
155
#[non_exhaustive]
156
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
157
pub struct Second {
158
    /// The padding to obtain the minimum width.
159
    pub padding: Padding,
160
}
161

            
162
/// The number of digits present in a subsecond representation.
163
#[non_exhaustive]
164
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
165
pub enum SubsecondDigits {
166
    /// Exactly one digit.
167
    One,
168
    /// Exactly two digits.
169
    Two,
170
    /// Exactly three digits.
171
    Three,
172
    /// Exactly four digits.
173
    Four,
174
    /// Exactly five digits.
175
    Five,
176
    /// Exactly six digits.
177
    Six,
178
    /// Exactly seven digits.
179
    Seven,
180
    /// Exactly eight digits.
181
    Eight,
182
    /// Exactly nine digits.
183
    Nine,
184
    /// Any number of digits (up to nine) that is at least one. When formatting, the minimum digits
185
    /// necessary will be used.
186
    OneOrMore,
187
}
188

            
189
/// Subsecond within the second.
190
#[non_exhaustive]
191
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
192
pub struct Subsecond {
193
    /// How many digits are present in the component?
194
    pub digits: SubsecondDigits,
195
}
196
// endregion time modifiers
197

            
198
// region: offset modifiers
199
/// Hour of the UTC offset.
200
#[non_exhaustive]
201
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
202
pub struct OffsetHour {
203
    /// Whether the `+` sign is present on positive values.
204
    pub sign_is_mandatory: bool,
205
    /// The padding to obtain the minimum width.
206
    pub padding: Padding,
207
}
208

            
209
/// Minute within the hour of the UTC offset.
210
#[non_exhaustive]
211
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
212
pub struct OffsetMinute {
213
    /// The padding to obtain the minimum width.
214
    pub padding: Padding,
215
}
216

            
217
/// Second within the minute of the UTC offset.
218
#[non_exhaustive]
219
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
220
pub struct OffsetSecond {
221
    /// The padding to obtain the minimum width.
222
    pub padding: Padding,
223
}
224
// endregion offset modifiers
225

            
226
/// Type of padding to ensure a minimum width.
227
#[non_exhaustive]
228
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
229
pub enum Padding {
230
    /// A space character (` `) should be used as padding.
231
    Space,
232
    /// A zero character (`0`) should be used as padding.
233
    Zero,
234
    /// There is no padding. This can result in a width below the otherwise minimum number of
235
    /// characters.
236
    None,
237
}
238

            
239
/// Ignore some number of bytes.
240
///
241
/// This has no effect when formatting.
242
#[non_exhaustive]
243
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
244
pub struct Ignore {
245
    /// The number of bytes to ignore.
246
    pub count: NonZeroU16,
247
}
248

            
249
// Needed as `Default` is deliberately not implemented for `Ignore`. The number of bytes to ignore
250
// must be explicitly provided.
251
impl Ignore {
252
    /// Create an instance of `Ignore` with the provided number of bytes to ignore.
253
    pub const fn count(count: NonZeroU16) -> Self {
254
        Self { count }
255
    }
256
}
257

            
258
/// The precision of a Unix timestamp.
259
#[non_exhaustive]
260
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
261
pub enum UnixTimestampPrecision {
262
    /// Seconds since the Unix epoch.
263
    Second,
264
    /// Milliseconds since the Unix epoch.
265
    Millisecond,
266
    /// Microseconds since the Unix epoch.
267
    Microsecond,
268
    /// Nanoseconds since the Unix epoch.
269
    Nanosecond,
270
}
271

            
272
/// A Unix timestamp.
273
#[non_exhaustive]
274
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
275
pub struct UnixTimestamp {
276
    /// The precision of the timestamp.
277
    pub precision: UnixTimestampPrecision,
278
    /// Whether the `+` sign must be present for a non-negative timestamp.
279
    pub sign_is_mandatory: bool,
280
}
281

            
282
/// The end of input.
283
///
284
/// There is currently not customization for this modifier.
285
#[non_exhaustive]
286
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
287
pub struct End;
288

            
289
/// Generate the provided code if and only if `pub` is present.
290
macro_rules! if_pub {
291
    (pub $(#[$attr:meta])*; $($x:tt)*) => {
292
        $(#[$attr])*
293
        ///
294
        /// This function exists since [`Default::default()`] cannot be used in a `const` context.
295
        /// It may be removed once that becomes possible. As the [`Default`] trait is in the
296
        /// prelude, removing this function in the future will not cause any resolution failures for
297
        /// the overwhelming majority of users; only users who use `#![no_implicit_prelude]` will be
298
        /// affected. As such it will not be considered a breaking change.
299
        $($x)*
300
    };
301
    ($($_:tt)*) => {};
302
}
303

            
304
/// Implement `Default` for the given type. This also generates an inherent implementation of a
305
/// `default` method that is `const fn`, permitting the default value to be used in const contexts.
306
// Every modifier should use this macro rather than a derived `Default`.
307
macro_rules! impl_const_default {
308
    ($($(#[$doc:meta])* $(@$pub:ident)? $type:ty => $default:expr;)*) => {$(
309
        impl $type {
310
            if_pub! {
311
                $($pub)?
312
                $(#[$doc])*;
313
                pub const fn default() -> Self {
314
                    $default
315
                }
316
            }
317
        }
318

            
319
        $(#[$doc])*
320
        impl Default for $type {
321
            fn default() -> Self {
322
                $default
323
            }
324
        }
325
    )*};
326
}
327

            
328
impl_const_default! {
329
    /// Creates a modifier that indicates the value is [padded with zeroes](Padding::Zero).
330
    @pub Day => Self { padding: Padding::Zero };
331
    /// Creates a modifier that indicates the value uses the
332
    /// [`Numerical`](Self::Numerical) representation.
333
    MonthRepr => Self::Numerical;
334
    /// Creates an instance of this type that indicates the value uses the
335
    /// [`Numerical`](MonthRepr::Numerical) representation, is [padded with zeroes](Padding::Zero),
336
    /// and is case-sensitive when parsing.
337
    @pub Month => Self {
338
        padding: Padding::Zero,
339
        repr: MonthRepr::Numerical,
340
        case_sensitive: true,
341
    };
342
    /// Creates a modifier that indicates the value is [padded with zeroes](Padding::Zero).
343
    @pub Ordinal => Self { padding: Padding::Zero };
344
    /// Creates a modifier that indicates the value uses the [`Long`](Self::Long) representation.
345
    WeekdayRepr => Self::Long;
346
    /// Creates a modifier that indicates the value uses the [`Long`](WeekdayRepr::Long)
347
    /// representation and is case-sensitive when parsing. If the representation is changed to a
348
    /// numerical one, the instance defaults to one-based indexing.
349
    @pub Weekday => Self {
350
        repr: WeekdayRepr::Long,
351
        one_indexed: true,
352
        case_sensitive: true,
353
    };
354
    /// Creates a modifier that indicates that the value uses the [`Iso`](Self::Iso) representation.
355
    WeekNumberRepr => Self::Iso;
356
    /// Creates a modifier that indicates that the value is [padded with zeroes](Padding::Zero)
357
            /// and uses the [`Iso`](WeekNumberRepr::Iso) representation.
358
    @pub WeekNumber => Self {
359
        padding: Padding::Zero,
360
        repr: WeekNumberRepr::Iso,
361
    };
362
    /// Creates a modifier that indicates the value uses the [`Full`](Self::Full) representation.
363
    YearRepr => Self::Full;
364
    /// Creates a modifier that indicates the value uses the [`Full`](YearRepr::Full)
365
    /// representation, is [padded with zeroes](Padding::Zero), uses the Gregorian calendar as its
366
    /// base, and only includes the year's sign if necessary.
367
    @pub Year => Self {
368
        padding: Padding::Zero,
369
        repr: YearRepr::Full,
370
        iso_week_based: false,
371
        sign_is_mandatory: false,
372
    };
373
    /// Creates a modifier that indicates the value is [padded with zeroes](Padding::Zero) and
374
    /// has the 24-hour representation.
375
    @pub Hour => Self {
376
        padding: Padding::Zero,
377
        is_12_hour_clock: false,
378
    };
379
    /// Creates a modifier that indicates the value is [padded with zeroes](Padding::Zero).
380
    @pub Minute => Self { padding: Padding::Zero };
381
    /// Creates a modifier that indicates the value uses the upper-case representation and is
382
    /// case-sensitive when parsing.
383
    @pub Period => Self {
384
        is_uppercase: true,
385
        case_sensitive: true,
386
    };
387
    /// Creates a modifier that indicates the value is [padded with zeroes](Padding::Zero).
388
    @pub Second => Self { padding: Padding::Zero };
389
    /// Creates a modifier that indicates the stringified value contains [one or more
390
    /// digits](Self::OneOrMore).
391
    SubsecondDigits => Self::OneOrMore;
392
    /// Creates a modifier that indicates the stringified value contains [one or more
393
    /// digits](SubsecondDigits::OneOrMore).
394
    @pub Subsecond => Self { digits: SubsecondDigits::OneOrMore };
395
    /// Creates a modifier that indicates the value only uses a sign for negative values and is
396
    /// [padded with zeroes](Padding::Zero).
397
    @pub OffsetHour => Self {
398
        sign_is_mandatory: false,
399
        padding: Padding::Zero,
400
    };
401
    /// Creates a modifier that indicates the value is [padded with zeroes](Padding::Zero).
402
    @pub OffsetMinute => Self { padding: Padding::Zero };
403
    /// Creates a modifier that indicates the value is [padded with zeroes](Padding::Zero).
404
    @pub OffsetSecond => Self { padding: Padding::Zero };
405
    /// Creates a modifier that indicates the value is [padded with zeroes](Self::Zero).
406
    Padding => Self::Zero;
407
    /// Creates a modifier that indicates the value represents the [number of seconds](Self::Second)
408
    /// since the Unix epoch.
409
    UnixTimestampPrecision => Self::Second;
410
    /// Creates a modifier that indicates the value represents the [number of
411
    /// seconds](UnixTimestampPrecision::Second) since the Unix epoch. The sign is not mandatory.
412
    @pub UnixTimestamp => Self {
413
        precision: UnixTimestampPrecision::Second,
414
        sign_is_mandatory: false,
415
    };
416
    /// Creates a modifier used to represent the end of input.
417
    @pub End => End;
418
}