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                    let ch: char = field.symbol.into();
81                    for _ in 0..field.length.to_len() {
82                        formatter.write_char(ch)?;
83                    }
84                    if let FieldSymbol::DecimalSecond(decimal_second) = field.symbol {
85                        formatter.write_char('.')?;
86                        for _ in 0..(decimal_second as u8) {
87                            formatter.write_char('S')?;
88                        }
89                    }
90                }
91                PatternItem::Literal(ch) => {
92                    buffer.push(ch);
93                }
94            }
95        }
96        dump_buffer_into_formatter(&buffer, formatter)?;
97        buffer.clear();
98        Ok(())
99    }
100}
101
102impl_display_with_writeable!(Pattern<'_>);
103
104impl Writeable for GenericPattern<'_> {
105    fn write_to<W: Write + ?Sized>(&self, formatter: &mut W) -> fmt::Result {
106        let mut buffer = alloc::string::String::new();
107        for pattern_item in self.items.iter() {
108            match pattern_item {
109                GenericPatternItem::Placeholder(idx) => {
110                    dump_buffer_into_formatter(&buffer, formatter)?;
111                    buffer.clear();
112                    write!(formatter, "{{{idx}}}")?;
113                }
114                GenericPatternItem::Literal(ch) => {
115                    buffer.push(ch);
116                }
117            }
118        }
119        dump_buffer_into_formatter(&buffer, formatter)?;
120        buffer.clear();
121        Ok(())
122    }
123}
124
125impl_display_with_writeable!(GenericPattern<'_>);