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
// This file is part of ICU4X. For terms of use, please see the file
// called LICENSE at the top level of the ICU4X source tree
// (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ).

use core::fmt;

use crate::{
    input::TimeZoneInput,
    time_zone::{FormatTimeZone, FormatTimeZoneWithFallback, TimeZoneFormatter},
    DateTimeError,
};
use writeable::Writeable;

/// [`FormattedTimeZone`] is a intermediate structure which can be retrieved as an output from [`TimeZoneFormatter`].
#[derive(Debug, Copy, Clone)]
pub struct FormattedTimeZone<'l, T>
where
    T: TimeZoneInput,
{
    pub(crate) time_zone_format: &'l TimeZoneFormatter,
    pub(crate) time_zone: &'l T,
}

impl<'l, T> Writeable for FormattedTimeZone<'l, T>
where
    T: TimeZoneInput,
{
    /// Format time zone with fallbacks.
    fn write_to<W: fmt::Write + ?Sized>(&self, sink: &mut W) -> fmt::Result {
        let mut sink = writeable::adapters::CoreWriteAsPartsWrite(sink);

        let r = match self.write_no_fallback(&mut sink) {
            Ok(fmt_result) => Ok(fmt_result?),
            Err(_) => self
                .time_zone_format
                .fallback_unit
                .format_with_last_resort_fallback(
                    &mut sink,
                    self.time_zone,
                    &self.time_zone_format.data_payloads,
                )?,
        };

        debug_assert!(r.is_ok(), "{r:?}");
        Ok(())
    }

    // TODO(#489): Implement writeable_length_hint
}

impl<'l, T> fmt::Display for FormattedTimeZone<'l, T>
where
    T: TimeZoneInput,
{
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        self.write_to(f)
    }
}

impl<'l, T> FormattedTimeZone<'l, T>
where
    T: TimeZoneInput,
{
    /// Write time zone with no fallback.
    ///
    /// # Examples
    ///
    /// ```
    /// use icu::datetime::time_zone::TimeZoneFormatter;
    /// use icu::datetime::DateTimeError;
    /// use icu::locid::locale;
    /// use icu::timezone::CustomTimeZone;
    /// use tinystr::tinystr;
    ///
    /// let mut tzf =
    ///     TimeZoneFormatter::try_new(&locale!("en").into(), Default::default())
    ///         .unwrap();
    /// let mut buf = String::new();
    ///
    /// let mut time_zone = "Z".parse::<CustomTimeZone>().unwrap();
    /// time_zone.time_zone_id = Some(tinystr!(8, "gblon").into());
    ///
    /// // There are no non-fallback formats enabled:
    /// assert!(matches!(
    ///     tzf.format(&time_zone).write_no_fallback(&mut buf),
    ///     Err(DateTimeError::UnsupportedOptions)
    /// ));
    /// assert!(buf.is_empty());
    ///
    /// // Enable a non-fallback format:
    /// tzf.include_generic_location_format().unwrap();
    /// assert!(matches!(
    ///     tzf.format(&time_zone).write_no_fallback(&mut buf),
    ///     Ok(Ok(_))
    /// ));
    /// assert_eq!("London Time", buf);
    ///
    /// // Errors still occur if the time zone is not supported:
    /// buf.clear();
    /// time_zone.time_zone_id = Some(tinystr!(8, "zzzzz").into());
    /// assert!(matches!(
    ///     tzf.format(&time_zone).write_no_fallback(&mut buf),
    ///     Err(DateTimeError::UnsupportedOptions)
    /// ));
    ///
    /// // Use the `Writable` trait instead to enable infallible formatting:
    /// writeable::assert_writeable_eq!(tzf.format(&time_zone), "GMT");
    /// ```
    pub fn write_no_fallback<W>(&self, mut w: &mut W) -> Result<fmt::Result, DateTimeError>
    where
        W: core::fmt::Write + ?Sized,
    {
        for unit in self.time_zone_format.format_units.iter() {
            match unit.format(
                &mut writeable::adapters::CoreWriteAsPartsWrite(&mut w),
                self.time_zone,
                &self.time_zone_format.data_payloads,
            ) {
                Ok(r) => return Ok(r),
                Err(DateTimeError::UnsupportedOptions) => continue,
                Err(e) => return Err(e),
            }
        }
        Err(DateTimeError::UnsupportedOptions)
    }
}