icu_datetime/provider/pattern/runtime/
display.rs

1// This file is part of ICU4X. For terms of use, please see the file
2// called LICENSE at the top level of the ICU4X source tree
3// (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ).
4
5//! Set of `Display` implementations for reference and runtime `Pattern`.
6
7use super::{
8    super::{GenericPatternItem, PatternItem},
9    GenericPattern, Pattern,
10};
11use crate::provider::fields::FieldSymbol;
12use alloc::string::String;
13use core::fmt::{self, Write};
14use writeable::{impl_display_with_writeable, Writeable};
15
16/// A helper function optimized to dump string buffers into `Pattern`
17/// serialization wrapping minimal chunks of the buffer in escaping `'`
18/// literals to produce valid UTF35 pattern string.
19fn dump_buffer_into_formatter<W: Write + ?Sized>(literal: &str, formatter: &mut W) -> fmt::Result {
20    if literal.is_empty() {
21        return Ok(());
22    }
23    // Determine if the literal contains any characters that would need to be escaped.
24    let mut needs_escaping = false;
25    for ch in literal.chars() {
26        if ch.is_ascii_alphabetic() || ch == '\'' {
27            needs_escaping = true;
28            break;
29        }
30    }
31
32    if needs_escaping {
33        let mut ch_iter = literal.trim_end().chars().peekable();
34
35        // Do not escape the leading whitespace.
36        while let Some(ch) = ch_iter.peek() {
37            if ch.is_whitespace() {
38                formatter.write_char(*ch)?;
39                ch_iter.next();
40            } else {
41                break;
42            }
43        }
44
45        // Wrap in "'" and escape "'".
46        formatter.write_char('\'')?;
47        for ch in ch_iter {
48            if ch == '\'' {
49                // Escape a single quote.
50                formatter.write_char('\\')?;
51            }
52            formatter.write_char(ch)?;
53        }
54        formatter.write_char('\'')?;
55
56        // Add the trailing whitespace
57        for ch in literal.chars().rev() {
58            if ch.is_whitespace() {
59                formatter.write_char(ch)?;
60            } else {
61                break;
62            }
63        }
64    } else {
65        formatter.write_str(literal)?;
66    }
67    Ok(())
68}
69
70/// This trait is implemented in order to provide the machinery to convert a [`Pattern`] to a UTS 35
71/// pattern string.
72impl Writeable for Pattern<'_> {
73    fn write_to<W: Write + ?Sized>(&self, formatter: &mut W) -> fmt::Result {
74        let mut buffer = String::new();
75        for pattern_item in self.items.iter() {
76            match pattern_item {
77                PatternItem::Field(field) => {
78                    dump_buffer_into_formatter(&buffer, formatter)?;
79                    buffer.clear();
80                    field.write_to(formatter)?;
81                    if let FieldSymbol::DecimalSecond(decimal_second) = field.symbol {
82                        formatter.write_char('.')?;
83                        for _ in 0..(decimal_second as u8) {
84                            formatter.write_char('S')?;
85                        }
86                    }
87                }
88                PatternItem::Literal(ch) => {
89                    buffer.push(ch);
90                }
91            }
92        }
93        dump_buffer_into_formatter(&buffer, formatter)?;
94        buffer.clear();
95        Ok(())
96    }
97}
98
99impl_display_with_writeable!(Pattern<'_>);
100
101impl Writeable for GenericPattern<'_> {
102    fn write_to<W: Write + ?Sized>(&self, formatter: &mut W) -> fmt::Result {
103        let mut buffer = alloc::string::String::new();
104        for pattern_item in self.items.iter() {
105            match pattern_item {
106                GenericPatternItem::Placeholder(idx) => {
107                    dump_buffer_into_formatter(&buffer, formatter)?;
108                    buffer.clear();
109                    write!(formatter, "{{{idx}}}")?;
110                }
111                GenericPatternItem::Literal(ch) => {
112                    buffer.push(ch);
113                }
114            }
115        }
116        dump_buffer_into_formatter(&buffer, formatter)?;
117        buffer.clear();
118        Ok(())
119    }
120}
121
122impl_display_with_writeable!(GenericPattern<'_>);