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
// 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 ).

//! Set of `Display` implementations for reference and runtime `Pattern`.

use super::{
    super::{GenericPatternItem, PatternItem},
    GenericPattern, Pattern,
};
use crate::fields::FieldSymbol;
use alloc::string::String;
use core::fmt::{self, Write};
use writeable::{impl_display_with_writeable, Writeable};

/// A helper function optimized to dump string buffers into `Pattern`
/// serialization wrapping minimal chunks of the buffer in escaping `'`
/// literals to produce valid UTF35 pattern string.
fn dump_buffer_into_formatter<W: Write + ?Sized>(literal: &str, formatter: &mut W) -> fmt::Result {
    if literal.is_empty() {
        return Ok(());
    }
    // Determine if the literal contains any characters that would need to be escaped.
    let mut needs_escaping = false;
    for ch in literal.chars() {
        if ch.is_ascii_alphabetic() || ch == '\'' {
            needs_escaping = true;
            break;
        }
    }

    if needs_escaping {
        let mut ch_iter = literal.trim_end().chars().peekable();

        // Do not escape the leading whitespace.
        while let Some(ch) = ch_iter.peek() {
            if ch.is_whitespace() {
                formatter.write_char(*ch)?;
                ch_iter.next();
            } else {
                break;
            }
        }

        // Wrap in "'" and escape "'".
        formatter.write_char('\'')?;
        for ch in ch_iter {
            if ch == '\'' {
                // Escape a single quote.
                formatter.write_char('\\')?;
            }
            formatter.write_char(ch)?;
        }
        formatter.write_char('\'')?;

        // Add the trailing whitespace
        for ch in literal.chars().rev() {
            if ch.is_whitespace() {
                formatter.write_char(ch)?;
            } else {
                break;
            }
        }
    } else {
        formatter.write_str(literal)?;
    }
    Ok(())
}

/// This trait is implemented in order to provide the machinery to convert a [`Pattern`] to a UTS 35
/// pattern string.
impl Writeable for Pattern<'_> {
    fn write_to<W: Write + ?Sized>(&self, formatter: &mut W) -> fmt::Result {
        let mut buffer = String::new();
        for pattern_item in self.items.iter() {
            match pattern_item {
                PatternItem::Field(field) => {
                    dump_buffer_into_formatter(&buffer, formatter)?;
                    buffer.clear();
                    let ch: char = field.symbol.into();
                    for _ in 0..field.length.to_len() {
                        formatter.write_char(ch)?;
                    }
                    if let FieldSymbol::DecimalSecond(decimal_second) = field.symbol {
                        formatter.write_char('.')?;
                        for _ in 0..(decimal_second as u8) {
                            formatter.write_char('S')?;
                        }
                    }
                }
                PatternItem::Literal(ch) => {
                    buffer.push(ch);
                }
            }
        }
        dump_buffer_into_formatter(&buffer, formatter)?;
        buffer.clear();
        Ok(())
    }
}

impl_display_with_writeable!(Pattern<'_>);

impl Writeable for GenericPattern<'_> {
    fn write_to<W: Write + ?Sized>(&self, formatter: &mut W) -> fmt::Result {
        let mut buffer = alloc::string::String::new();
        for pattern_item in self.items.iter() {
            match pattern_item {
                GenericPatternItem::Placeholder(idx) => {
                    dump_buffer_into_formatter(&buffer, formatter)?;
                    buffer.clear();
                    write!(formatter, "{{{idx}}}")?;
                }
                GenericPatternItem::Literal(ch) => {
                    buffer.push(ch);
                }
            }
        }
        dump_buffer_into_formatter(&buffer, formatter)?;
        buffer.clear();
        Ok(())
    }
}

impl_display_with_writeable!(GenericPattern<'_>);