icu_datetime/provider/pattern/reference/
generic.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
5use super::{super::GenericPatternItem, super::PatternError, Parser};
6#[cfg(test)]
7use super::{super::PatternItem, Pattern};
8use alloc::vec::Vec;
9use core::str::FromStr;
10
11/// A fully-owned, non-zero-copy type corresponding to [`GenericPattern`](super::super::runtime::GenericPattern).
12///
13/// <div class="stab unstable">
14/// 🚧 This code is considered unstable; it may change at any time, in breaking or non-breaking ways,
15/// including in SemVer minor releases. While the serde representation of data structs is guaranteed
16/// to be stable, their Rust representation might not be. Use with caution.
17/// </div>
18#[derive(Debug)]
19#[allow(clippy::exhaustive_structs)] // this type is stable
20pub struct GenericPattern {
21    pub(crate) items: Vec<GenericPatternItem>,
22}
23
24impl GenericPattern {
25    #[cfg(test)]
26    pub(crate) fn combined(self, replacements: Vec<Pattern>) -> Result<Pattern, PatternError> {
27        let size = replacements.iter().fold(0, |acc, r| acc + r.items.len());
28        let mut result = Vec::with_capacity(self.items.len() + size);
29
30        for item in self.items {
31            match item {
32                GenericPatternItem::Placeholder(idx) => {
33                    #[allow(clippy::unwrap_used)] // idx is a valid base-10 digit
34                    let replacement = replacements.get(idx as usize).ok_or_else(|| {
35                        PatternError::UnknownSubstitution(char::from_digit(idx as u32, 10).unwrap())
36                    })?;
37                    result.extend(replacement.items.iter());
38                }
39                GenericPatternItem::Literal(ch) => result.push(PatternItem::Literal(ch)),
40            }
41        }
42
43        Ok(result.into())
44    }
45}
46
47impl From<Vec<GenericPatternItem>> for GenericPattern {
48    fn from(items: Vec<GenericPatternItem>) -> Self {
49        Self { items }
50    }
51}
52
53impl FromStr for GenericPattern {
54    type Err = PatternError;
55
56    fn from_str(s: &str) -> Result<Self, Self::Err> {
57        Parser::new(s).parse_generic().map(Self::from)
58    }
59}
60
61#[cfg(test)]
62#[cfg(feature = "datagen")]
63mod test {
64    use super::*;
65
66    #[test]
67    fn test_reference_generic_pattern_combine() {
68        let pattern: GenericPattern = "{0} 'at' {1}"
69            .parse()
70            .expect("Failed to parse a generic pattern.");
71
72        let date = "y/M/d".parse().expect("Failed to parse a date pattern.");
73        let time = "HH:mm".parse().expect("Failed to parse a time pattern.");
74
75        let pattern = pattern
76            .combined(vec![date, time])
77            .expect("Failed to combine date and time.");
78        assert_eq!(pattern.to_runtime_pattern().to_string(), "y/M/d 'at' HH:mm");
79    }
80}