Coverage Report

Created: 2026-07-14 06:17

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/jiff-0.2.31/src/fmt/mod.rs
Line
Count
Source
1
/*!
2
Configurable support for printing and parsing datetimes and durations.
3
4
Note that for most use cases, you should be using the corresponding
5
[`Display`](std::fmt::Display) or [`FromStr`](std::str::FromStr) trait
6
implementations for printing and parsing respectively. The APIs in this module
7
provide more configurable support for printing and parsing.
8
9
# Tables of examples
10
11
The tables below attempt to show some examples of datetime and duration
12
formatting, along with names and links to relevant routines and types. The
13
point of these tables is to give a general overview of the formatting and
14
parsing functionality in these sub-modules.
15
16
## Support for `FromStr` and `Display`
17
18
This table lists the formats supported by the [`FromStr`] and [`Display`]
19
trait implementations on the datetime and duration types in Jiff.
20
21
In all of these cases, the trait implementations are mere conveniences for
22
functionality provided by the [`temporal`] sub-module (and, in a couple cases,
23
the [`friendly`] sub-module). The sub-modules provide lower level control
24
(such as parsing from `&[u8]`) and more configuration (such as controlling the
25
disambiguation strategy used when parsing zoned datetime [RFC-9557] strings).
26
27
| Example | Format | Links |
28
| ------- | ------ | ----- |
29
| `2025-08-20T17:35:00Z` | [RFC-3339] | [`Timestamp`] |
30
| `2025-08-20T17:35:00-05` | [RFC-3339] | [`FromStr`] impl and<br>[`Timestamp::display_with_offset`] |
31
| `2025-08-20T17:35:00+02[Poland]` | [RFC-9557] | [`Zoned`] |
32
| `2025-08-20T17:35:00+02:00[+02:00]` | [RFC-9557] | [`Zoned`] |
33
| `2025-08-20T17:35:00` | [ISO-8601] | [`civil::DateTime`] |
34
| `2025-08-20` | [ISO-8601] | [`civil::Date`] |
35
| `17:35:00` | [ISO-8601] | [`civil::Time`] |
36
| `P1Y2M3W4DT5H6M7S` | [ISO-8601], [Temporal] | [`Span`] |
37
| `PT1H2M3S` | [ISO-8601] | [`SignedDuration`], [`Span`] |
38
| `PT1H2M3.123456789S` | [ISO-8601] | [`SignedDuration`], [`Span`] |
39
| `1d 2h 3m 5s` | [`friendly`] | [`FromStr`] impl and alternative [`Display`]<br>via `{:#}` for [`SignedDuration`], [`Span`] |
40
41
Note that for datetimes like `2025-08-20T17:35:00`, the following variants are
42
also accepted:
43
44
```text
45
2025-08-20 17:35:00
46
2025-08-20T17:35:00.123456789
47
2025-08-20T17:35
48
2025-08-20T17
49
```
50
51
This applies to RFC 3339 and RFC 9557 timestamps as well.
52
53
Also, for ISO 8601 durations, the unit designator labels are matched
54
case insensitively. For example, `PT1h2m3s` is recognized by Jiff.
55
56
## The "friendly" duration format
57
58
This table lists a few examples of the [`friendly`] duration format. Briefly,
59
it is a bespoke format for Jiff, but is meant to match similar bespoke formats
60
used elsewhere and be easier to read than the standard ISO 8601 duration
61
format.
62
63
All examples below can be parsed via a [`Span`]'s [`FromStr`] trait
64
implementation. All examples with units no bigger than hours can be parsed via
65
a [`SignedDuration`]'s [`FromStr`] trait implementation. This table otherwise
66
shows the options for printing durations in the format shown.
67
68
| Example | Print configuration |
69
| ------- | ------------------- |
70
| `1year 2months` | [`Designator::Verbose`] via [`SpanPrinter::designator`] |
71
| `1yr 2mos` | [`Designator::Short`] via [`SpanPrinter::designator`] |
72
| `1y 2mo` | [`Designator::Compact`] via [`SpanPrinter::designator`] (default) |
73
| `1h2m3s` | [`Spacing::None`] via [`SpanPrinter::spacing`] |
74
| `1h 2m 3s` | [`Spacing::BetweenUnits`] via [`SpanPrinter::spacing`] (default) |
75
| `1 h 2 m 3 s` | [`Spacing::BetweenUnitsAndDesignators`] via [`SpanPrinter::spacing`] |
76
| `2d 3h ago` | [`Direction::Auto`] via [`SpanPrinter::direction`] (default) |
77
| `-2d 3h` | [`Direction::Sign`] via [`SpanPrinter::direction`] |
78
| `+2d 3h` | [`Direction::ForceSign`] via [`SpanPrinter::direction`] |
79
| `2d 3h ago` | [`Direction::Suffix`] via [`SpanPrinter::direction`] |
80
| `9.123456789s` | [`FractionalUnit::Second`] via [`SpanPrinter::fractional`] |
81
| `1y, 2mo` | [`SpanPrinter::comma_after_designator`] |
82
| `15d 02:59:15.123` | [`SpanPrinter::hours_minutes_seconds`] |
83
84
## Bespoke datetime formats via `strptime` and `strftime`
85
86
Every datetime type has bespoke formatting routines defined on it. For
87
example, [`Zoned::strptime`] and [`civil::Date::strftime`]. Additionally, the
88
[`strtime`] sub-module also provides convenience routines, [`strtime::format`]
89
and [`strtime::parse`], where the former is generic over any datetime type in
90
Jiff and the latter provides a [`BrokenDownTime`] for granular parsing.
91
92
| Example | Format string |
93
| ------- | ------------- |
94
| `2025-05-20` | `%Y-%m-%d` |
95
| `2025-05-20` | `%F` |
96
| `2025-W21-2` | `%G-W%V-%u` |
97
| `05/20/25` | `%m/%d/%y` |
98
| `Monday, February 10, 2025 at 9:01pm -0500` | `%A, %B %d, %Y at %-I:%M%P %z` |
99
| `Monday, February 10, 2025 at 9:01pm EST` | `%A, %B %d, %Y at %-I:%M%P %Z` |
100
| `Monday, February 10, 2025 at 9:01pm America/New_York` | `%A, %B %d, %Y at %-I:%M%P %Q` |
101
102
The specific conversion specifiers supported are documented in the [`strtime`]
103
sub-module. While precise POSIX compatibility is not guaranteed, the conversion
104
specifiers are generally meant to match prevailing implementations. (Although
105
there are many such implementations and they each tend to have their own quirks
106
and features.)
107
108
## RFC 2822 parsing and printing
109
110
[RFC-2822] support is provided by the [`rfc2822`] sub-module.
111
112
| Example | Links |
113
| ------- | ----- |
114
| `Thu, 29 Feb 2024 05:34 -0500` | [`rfc2822::parse`] and [`rfc2822::to_string`] |
115
| `Thu, 01 Jan 1970 00:00:01 GMT` | [`DateTimePrinter::timestamp_to_rfc9110_string`] |
116
117
[Temporal]: https://tc39.es/proposal-temporal/#sec-temporal-iso8601grammar
118
[ISO-8601]: https://www.iso.org/iso-8601-date-and-time-format.html
119
[RFC-3339]: https://www.rfc-editor.org/rfc/rfc3339
120
[RFC-9557]: https://www.rfc-editor.org/rfc/rfc9557.html
121
[ISO-8601]: https://www.iso.org/iso-8601-date-and-time-format.html
122
[RFC-2822]: https://datatracker.ietf.org/doc/html/rfc2822
123
[RFC-9110]: https://datatracker.ietf.org/doc/html/rfc9110#section-5.6.7-15
124
[`Display`]: std::fmt::Display
125
[`FromStr`]: std::str::FromStr
126
[`friendly`]: crate::fmt::friendly
127
[`temporal`]: crate::fmt::temporal
128
[`rfc2822`]: crate::fmt::rfc2822
129
[`strtime`]: crate::fmt::strtime
130
[`civil::DateTime`]: crate::civil::DateTime
131
[`civil::Date`]: crate::civil::Date
132
[`civil::Date::strftime`]: crate::civil::Date::strftime
133
[`civil::Time`]: crate::civil::Time
134
[`SignedDuration`]: crate::SignedDuration
135
[`Span`]: crate::Span
136
[`Timestamp`]: crate::Timestamp
137
[`Timestamp::display_with_offset`]: crate::Timestamp::display_with_offset
138
[`Zoned`]: crate::Zoned
139
[`Zoned::strptime`]: crate::Zoned::strptime
140
141
[`Designator::Verbose`]: crate::fmt::friendly::Designator::Verbose
142
[`Designator::Short`]: crate::fmt::friendly::Designator::Short
143
[`Designator::Compact`]: crate::fmt::friendly::Designator::Compact
144
[`Spacing::None`]: crate::fmt::friendly::Spacing::None
145
[`Spacing::BetweenUnits`]: crate::fmt::friendly::Spacing::BetweenUnits
146
[`Spacing::BetweenUnitsAndDesignators`]: crate::fmt::friendly::Spacing::BetweenUnitsAndDesignators
147
[`Direction::Auto`]: crate::fmt::friendly::Direction::Auto
148
[`Direction::Sign`]: crate::fmt::friendly::Direction::Sign
149
[`Direction::ForceSign`]: crate::fmt::friendly::Direction::ForceSign
150
[`Direction::Suffix`]: crate::fmt::friendly::Direction::Suffix
151
[`FractionalUnit::Second`]: crate::fmt::friendly::FractionalUnit::Second
152
[`SpanPrinter::designator`]: crate::fmt::friendly::SpanPrinter::designator
153
[`SpanPrinter::spacing`]: crate::fmt::friendly::SpanPrinter::spacing
154
[`SpanPrinter::direction`]: crate::fmt::friendly::SpanPrinter::direction
155
[`SpanPrinter::fractional`]: crate::fmt::friendly::SpanPrinter::fractional
156
[`SpanPrinter::comma_after_designator`]: crate::fmt::friendly::SpanPrinter::comma_after_designator
157
[`SpanPrinter::hours_minutes_seconds`]: crate::fmt::friendly::SpanPrinter::hours_minutes_seconds
158
159
[`BrokenDownTime`]: crate::fmt::strtime::BrokenDownTime
160
[`strtime::parse`]: crate::fmt::strtime::parse
161
[`strtime::format`]: crate::fmt::strtime::format
162
163
[`rfc2822::parse`]: crate::fmt::rfc2822::parse
164
[`rfc2822::to_string`]: crate::fmt::rfc2822::to_string
165
[`DateTimePrinter::timestamp_to_rfc9110_string`]: crate::fmt::rfc2822::DateTimePrinter::timestamp_to_rfc9110_string
166
*/
167
168
use crate::{
169
    error::{fmt::Error as E, Error},
170
    util::escape,
171
};
172
173
mod buffer;
174
pub mod friendly;
175
mod offset;
176
pub mod rfc2822;
177
mod rfc9557;
178
#[cfg(feature = "serde")]
179
pub mod serde;
180
pub mod strtime;
181
pub mod temporal;
182
mod util;
183
184
/// The result of parsing a value out of a slice of bytes.
185
///
186
/// This contains both the parsed value and the offset at which the value
187
/// ended in the input given. This makes it possible to parse, for example, a
188
/// datetime value as a prefix of some larger string without knowing ahead of
189
/// time where it ends.
190
#[derive(Clone)]
191
pub(crate) struct Parsed<'i, V> {
192
    /// The value parsed.
193
    value: V,
194
    /// The remaining unparsed input.
195
    input: &'i [u8],
196
}
197
198
impl<'i, V> Parsed<'i, V> {
199
    #[inline]
200
0
    fn and_then<U>(
201
0
        self,
202
0
        map: impl FnOnce(V) -> Result<U, Error>,
203
0
    ) -> Result<Parsed<'i, U>, Error> {
204
0
        let Parsed { value, input } = self;
205
0
        Ok(Parsed { value: map(value)?, input })
206
0
    }
Unexecuted instantiation: <jiff::fmt::Parsed<()>>::and_then::<jiff::signed_duration::SignedDuration, <jiff::fmt::friendly::parser::SpanParser>::parse_duration::imp::{closure#0}>
Unexecuted instantiation: <jiff::fmt::Parsed<()>>::and_then::<jiff::signed_duration::SignedDuration, <jiff::fmt::temporal::parser::SpanParser>::parse_signed_duration::imp::{closure#0}>
Unexecuted instantiation: <jiff::fmt::Parsed<()>>::and_then::<jiff::span::Span, <jiff::fmt::friendly::parser::SpanParser>::parse_span::imp::{closure#0}>
Unexecuted instantiation: <jiff::fmt::Parsed<()>>::and_then::<jiff::span::Span, <jiff::fmt::temporal::parser::SpanParser>::parse_span::imp::{closure#0}>
Unexecuted instantiation: <jiff::fmt::Parsed<()>>::and_then::<core::time::Duration, <jiff::fmt::friendly::parser::SpanParser>::parse_unsigned_duration::imp::{closure#0}>
Unexecuted instantiation: <jiff::fmt::Parsed<()>>::and_then::<core::time::Duration, <jiff::fmt::temporal::parser::SpanParser>::parse_unsigned_duration::imp::{closure#0}>
207
}
208
209
impl<'i, V: core::fmt::Display> Parsed<'i, V> {
210
    /// Ensures that the parsed value represents the entire input. This occurs
211
    /// precisely when the `input` on this parsed value is empty.
212
    ///
213
    /// This is useful when one expects a parsed value to consume the entire
214
    /// input, and to consider it an error if it doesn't.
215
    #[inline]
216
0
    fn into_full(self) -> Result<V, Error> {
217
0
        if self.input.is_empty() {
218
0
            return Ok(self.value);
219
0
        }
220
0
        Err(Error::from(E::into_full_error(&self.value, self.input)))
221
0
    }
Unexecuted instantiation: <jiff::fmt::Parsed<jiff::signed_duration::SignedDuration>>::into_full
Unexecuted instantiation: <jiff::fmt::Parsed<jiff::span::Span>>::into_full
Unexecuted instantiation: <jiff::fmt::Parsed<jiff::fmt::temporal::parser::ParsedDateTime>>::into_full
Unexecuted instantiation: <jiff::fmt::Parsed<jiff::civil::iso_week_date::ISOWeekDate>>::into_full
222
}
223
224
impl<'i, V> Parsed<'i, V> {
225
    /// Ensures that the parsed value represents the entire input. This occurs
226
    /// precisely when the `input` on this parsed value is empty.
227
    ///
228
    /// This is useful when one expects a parsed value to consume the entire
229
    /// input, and to consider it an error if it doesn't.
230
    ///
231
    /// This is like `Parsed::into_full`, but lets the caller provide a custom
232
    /// `Display` implementation.
233
    #[inline]
234
0
    fn into_full_with(
235
0
        self,
236
0
        display: impl core::fmt::Display,
237
0
    ) -> Result<V, Error> {
238
0
        if self.input.is_empty() {
239
0
            return Ok(self.value);
240
0
        }
241
0
        Err(Error::from(E::into_full_error(&display, self.input)))
242
0
    }
243
}
244
245
impl<'i, V: core::fmt::Debug> core::fmt::Debug for Parsed<'i, V> {
246
0
    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
247
0
        f.debug_struct("Parsed")
248
0
            .field("value", &self.value)
249
0
            .field("input", &escape::Bytes(self.input))
250
0
            .finish()
251
0
    }
252
}
253
254
/// A trait for printing datetimes or spans into Unicode-accepting buffers or
255
/// streams.
256
///
257
/// The most useful implementations of this trait are for the `String` and
258
/// `Vec<u8>` types. But any implementation of [`std::fmt::Write`] and
259
/// [`std::io::Write`] can be used via the [`StdFmtWrite`] and [`StdIoWrite`]
260
/// adapters, respectively.
261
///
262
/// Most users of Jiff should not need to interact with this trait directly.
263
/// Instead, printing is handled via the [`Display`](std::fmt::Display)
264
/// implementation of the relevant type.
265
///
266
/// # Design
267
///
268
/// This trait is a near-clone of the `std::fmt::Write` trait. It's also very
269
/// similar to the `std::io::Write` trait, but like `std::fmt::Write`, this
270
/// trait is limited to writing valid UTF-8. The UTF-8 restriction was adopted
271
/// because we really want to support printing datetimes and spans to `String`
272
/// buffers. If we permitted writing `&[u8]` data, then writing to a `String`
273
/// buffer would always require a costly UTF-8 validation check.
274
///
275
/// The `std::fmt::Write` trait wasn't used itself because:
276
///
277
/// 1. Using a custom trait allows us to require using Jiff's error type.
278
/// (Although this extra flexibility isn't currently used much, since printing
279
/// rarely fails for any reason other than the underlying `jiff::fmt::Write`
280
/// implementation failing.)
281
/// 2. Using a custom trait allows us more control over the implementations of
282
/// the trait. For example, a custom trait means we can format directly into
283
/// a `Vec<u8>` buffer, which isn't possible with `std::fmt::Write` because
284
/// there is no `std::fmt::Write` trait implementation for `Vec<u8>`.
285
pub trait Write {
286
    /// Write the given string to this writer, returning whether the write
287
    /// succeeded or not.
288
    fn write_str(&mut self, string: &str) -> Result<(), Error>;
289
290
    /// Write the given character to this writer, returning whether the write
291
    /// succeeded or not.
292
    #[inline]
293
0
    fn write_char(&mut self, char: char) -> Result<(), Error> {
294
0
        self.write_str(char.encode_utf8(&mut [0; 4]))
295
0
    }
Unexecuted instantiation: <jiff::fmt::StdFmtWrite<&mut core::fmt::Formatter> as jiff::fmt::Write>::write_char
Unexecuted instantiation: <jiff::fmt::buffer::BorrowedWriter as jiff::fmt::Write>::write_char
Unexecuted instantiation: <alloc::string::String as jiff::fmt::Write>::write_char
296
297
    /// Returns a `Vec<u8>` backing store for this implementation.
298
    ///
299
    /// Consumers of this trait may call this method to get a view directly
300
    /// into a buffer for more optimized writing.
301
    ///
302
    /// The default implementation always returns `None`.
303
    ///
304
    /// This method is only available when Jiff's `alloc` feature is enabled.
305
    ///
306
    /// # Safety
307
    ///
308
    /// Callers must ensure that only valid UTF-8 is written to the buffer
309
    /// returned.
310
    #[cfg(feature = "alloc")]
311
    #[inline]
312
0
    unsafe fn as_mut_vec(&mut self) -> Option<&mut alloc::vec::Vec<u8>> {
313
0
        None
314
0
    }
Unexecuted instantiation: <jiff::fmt::StdFmtWrite<&mut core::fmt::Formatter> as jiff::fmt::Write>::as_mut_vec
Unexecuted instantiation: <jiff::fmt::buffer::BorrowedWriter as jiff::fmt::Write>::as_mut_vec
315
}
316
317
#[cfg(any(test, feature = "alloc"))]
318
impl Write for alloc::string::String {
319
    #[inline]
320
0
    fn write_str(&mut self, string: &str) -> Result<(), Error> {
321
0
        self.push_str(string);
322
0
        Ok(())
323
0
    }
324
325
    #[cfg(feature = "alloc")]
326
    #[inline]
327
0
    unsafe fn as_mut_vec(&mut self) -> Option<&mut alloc::vec::Vec<u8>> {
328
        // SAFETY: The conditions are forwarded to the trait interface.
329
0
        unsafe { Some(alloc::string::String::as_mut_vec(self)) }
330
0
    }
331
}
332
333
#[cfg(any(test, feature = "alloc"))]
334
impl Write for alloc::vec::Vec<u8> {
335
    #[inline]
336
0
    fn write_str(&mut self, string: &str) -> Result<(), Error> {
337
0
        self.extend_from_slice(string.as_bytes());
338
0
        Ok(())
339
0
    }
340
341
    #[cfg(feature = "alloc")]
342
    #[inline]
343
0
    unsafe fn as_mut_vec(&mut self) -> Option<&mut alloc::vec::Vec<u8>> {
344
0
        Some(self)
345
0
    }
346
}
347
348
impl<W: Write> Write for &mut W {
349
0
    fn write_str(&mut self, string: &str) -> Result<(), Error> {
350
0
        (**self).write_str(string)
351
0
    }
352
353
    #[inline]
354
0
    fn write_char(&mut self, char: char) -> Result<(), Error> {
355
0
        (**self).write_char(char)
356
0
    }
357
358
    #[cfg(feature = "alloc")]
359
    #[inline]
360
0
    unsafe fn as_mut_vec(&mut self) -> Option<&mut alloc::vec::Vec<u8>> {
361
        // SAFETY: The conditions are forwarded to the trait interface.
362
0
        unsafe { (**self).as_mut_vec() }
363
0
    }
364
}
365
366
impl Write for &mut dyn Write {
367
0
    fn write_str(&mut self, string: &str) -> Result<(), Error> {
368
0
        (**self).write_str(string)
369
0
    }
370
371
    #[inline]
372
0
    fn write_char(&mut self, char: char) -> Result<(), Error> {
373
0
        (**self).write_char(char)
374
0
    }
375
376
    #[cfg(feature = "alloc")]
377
    #[inline]
378
0
    unsafe fn as_mut_vec(&mut self) -> Option<&mut alloc::vec::Vec<u8>> {
379
        // SAFETY: The conditions are forwarded to the trait interface.
380
0
        unsafe { (**self).as_mut_vec() }
381
0
    }
382
}
383
384
/// An adapter for using `std::io::Write` implementations with `fmt::Write`.
385
///
386
/// This is useful when one wants to format a datetime or span directly
387
/// to something with a `std::io::Write` trait implementation but not a
388
/// `fmt::Write` implementation.
389
///
390
/// # Example
391
///
392
/// ```no_run
393
/// use std::{fs::File, io::{BufWriter, Write}, path::Path};
394
///
395
/// use jiff::{civil::date, fmt::{StdIoWrite, temporal::DateTimePrinter}};
396
///
397
/// let zdt = date(2024, 6, 15).at(7, 0, 0, 0).in_tz("America/New_York")?;
398
///
399
/// let path = Path::new("/tmp/output");
400
/// let mut file = BufWriter::new(File::create(path)?);
401
/// DateTimePrinter::new().print_zoned(&zdt, StdIoWrite(&mut file)).unwrap();
402
/// file.flush()?;
403
/// assert_eq!(
404
///     std::fs::read_to_string(path)?,
405
///     "2024-06-15T07:00:00-04:00[America/New_York]",
406
/// );
407
///
408
/// # Ok::<(), Box<dyn std::error::Error>>(())
409
/// ```
410
#[cfg(feature = "std")]
411
#[derive(Clone, Debug)]
412
pub struct StdIoWrite<W>(pub W);
413
414
#[cfg(feature = "std")]
415
impl<W: std::io::Write> Write for StdIoWrite<W> {
416
    #[inline]
417
0
    fn write_str(&mut self, string: &str) -> Result<(), Error> {
418
0
        self.0.write_all(string.as_bytes()).map_err(Error::io)
419
0
    }
420
}
421
422
/// An adapter for using `std::fmt::Write` implementations with `fmt::Write`.
423
///
424
/// This is useful when one wants to format a datetime or span directly
425
/// to something with a `std::fmt::Write` trait implementation but not a
426
/// `fmt::Write` implementation.
427
///
428
/// (Despite using `Std` in this name, this type is available in `core`-only
429
/// configurations.)
430
///
431
/// # Example
432
///
433
/// This example shows the `std::fmt::Display` trait implementation for
434
/// [`civil::DateTime`](crate::civil::DateTime) (but using a wrapper type).
435
///
436
/// ```
437
/// use jiff::{civil::DateTime, fmt::{temporal::DateTimePrinter, StdFmtWrite}};
438
///
439
/// struct MyDateTime(DateTime);
440
///
441
/// impl std::fmt::Display for MyDateTime {
442
///     fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
443
///
444
///         static P: DateTimePrinter = DateTimePrinter::new();
445
///         P.print_datetime(&self.0, StdFmtWrite(f))
446
///             .map_err(|_| std::fmt::Error)
447
///     }
448
/// }
449
///
450
/// let dt = MyDateTime(DateTime::constant(2024, 6, 15, 17, 30, 0, 0));
451
/// assert_eq!(dt.to_string(), "2024-06-15T17:30:00");
452
/// ```
453
#[derive(Clone, Debug)]
454
pub struct StdFmtWrite<W>(pub W);
455
456
impl<W: core::fmt::Write> Write for StdFmtWrite<W> {
457
    #[inline]
458
0
    fn write_str(&mut self, string: &str) -> Result<(), Error> {
459
0
        self.0
460
0
            .write_str(string)
461
0
            .map_err(|_| Error::from(E::StdFmtWriteAdapter))
462
0
    }
463
}
464
465
impl<W: Write> core::fmt::Write for StdFmtWrite<W> {
466
    #[inline]
467
0
    fn write_str(&mut self, string: &str) -> Result<(), core::fmt::Error> {
468
0
        self.0.write_str(string).map_err(|_| core::fmt::Error)
469
0
    }
470
}
471
472
/// An adapter for writing Jiff's formatted output to a
473
/// [`defmt`] formatter.
474
///
475
/// This is used by Jiff's [`defmt::Format`] trait implementations to share the
476
/// same printer code used for [`core::fmt`] output.
477
///
478
/// # Example
479
///
480
/// This example shows the [`defmt::Format`] trait implementation for
481
/// [`civil::DateTime`](crate::civil::DateTime) (but using a wrapper type).
482
///
483
/// ```
484
/// use jiff::{
485
///     civil::DateTime,
486
///     fmt::{temporal::DateTimePrinter, DefmtWrite},
487
/// };
488
///
489
/// struct MyDateTime(DateTime);
490
///
491
/// impl defmt::Format for MyDateTime {
492
///     fn format(&self, f: defmt::Formatter) {
493
///         static P: DateTimePrinter = DateTimePrinter::new();
494
///         defmt::unwrap!(P.print_datetime(&self.0, DefmtWrite(f)));
495
///     }
496
/// }
497
/// ```
498
#[cfg(feature = "defmt")]
499
#[derive(defmt::Format)]
500
pub struct DefmtWrite<'a>(pub defmt::Formatter<'a>);
501
502
#[cfg(feature = "defmt")]
503
impl<'a> core::fmt::Debug for DefmtWrite<'a> {
504
    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
505
        f.debug_struct("DefmtWrite").finish()
506
    }
507
}
508
509
#[cfg(feature = "defmt")]
510
impl<'a> core::fmt::Write for DefmtWrite<'a> {
511
    #[inline]
512
    fn write_str(&mut self, string: &str) -> Result<(), core::fmt::Error> {
513
        defmt::write!(self.0, "{=str}", string);
514
        Ok(())
515
    }
516
}
517
518
#[cfg(feature = "defmt")]
519
impl<'a> Write for DefmtWrite<'a> {
520
    #[inline]
521
    fn write_str(&mut self, string: &str) -> Result<(), Error> {
522
        defmt::write!(self.0, "{=str}", string);
523
        Ok(())
524
    }
525
}