use crate::lazy_automaton::LazyAutomaton;
use crate::provider::*;
use crate::ListLength;
#[cfg(feature = "datagen")]
use alloc::borrow::Cow;
#[cfg(feature = "datagen")]
use icu_provider::DataError;
use writeable::{LengthHint, Writeable};
impl<'data> ListFormatterPatternsV1<'data> {
#[cfg(feature = "datagen")]
pub fn try_new(
[start, middle, end, pair, short_start, short_middle, short_end, short_pair, narrow_start, narrow_middle, narrow_end, narrow_pair]: [&str; 12],
) -> Result<Self, DataError> {
Ok(Self([
ListJoinerPattern::from_str(start, true, false)?.into(),
ListJoinerPattern::from_str(middle, false, false)?.into(),
ListJoinerPattern::from_str(end, false, true)?.into(),
ListJoinerPattern::from_str(pair, true, true)?.into(),
ListJoinerPattern::from_str(short_start, true, false)?.into(),
ListJoinerPattern::from_str(short_middle, false, false)?.into(),
ListJoinerPattern::from_str(short_end, false, true)?.into(),
ListJoinerPattern::from_str(short_pair, true, true)?.into(),
ListJoinerPattern::from_str(narrow_start, true, false)?.into(),
ListJoinerPattern::from_str(narrow_middle, false, false)?.into(),
ListJoinerPattern::from_str(narrow_end, false, true)?.into(),
ListJoinerPattern::from_str(narrow_pair, true, true)?.into(),
]))
}
#[cfg(feature = "datagen")]
pub fn make_conditional(
&mut self,
pattern: &str,
regex: &SerdeDFA<'static>,
alternative_pattern: &str,
) -> Result<(), DataError> {
let old = ListJoinerPattern::from_str(pattern, true, true)?;
for i in 0..12 {
#[allow(clippy::indexing_slicing)] if self.0[i].default == old {
self.0[i].special_case = Some(SpecialCasePattern {
condition: regex.clone(),
pattern: ListJoinerPattern::from_str(
alternative_pattern,
i % 4 == 0 || i % 4 == 3, i % 4 == 2 || i % 4 == 3, )?,
});
}
}
Ok(())
}
pub(crate) fn size_hint(&self, style: ListLength, len: usize) -> LengthHint {
match len {
0 | 1 => LengthHint::exact(0),
2 => self.pair(style).size_hint(),
n => {
self.start(style).size_hint()
+ self.middle(style).size_hint() * (n - 3)
+ self.end(style).size_hint()
}
}
}
}
type PatternParts<'a> = (&'a str, &'a str, &'a str);
impl<'a> ConditionalListJoinerPattern<'a> {
pub(crate) fn parts<'b, W: Writeable + ?Sized>(
&'a self,
following_value: &'b W,
) -> PatternParts<'a> {
match &self.special_case {
Some(SpecialCasePattern { condition, pattern })
if condition.deref().matches_earliest_fwd_lazy(following_value) =>
{
pattern.borrow_tuple()
}
_ => self.default.borrow_tuple(),
}
}
fn size_hint(&'a self) -> LengthHint {
let mut hint = self.default.size_hint();
if let Some(special_case) = &self.special_case {
hint |= special_case.pattern.size_hint()
}
hint
}
}
impl<'data> ListJoinerPattern<'data> {
#[cfg(feature = "datagen")]
fn from_str(pattern: &str, allow_prefix: bool, allow_suffix: bool) -> Result<Self, DataError> {
match (pattern.find("{0}"), pattern.find("{1}")) {
(Some(index_0), Some(index_1))
if index_0 < index_1
&& (allow_prefix || index_0 == 0)
&& (allow_suffix || index_1 == pattern.len() - 3) =>
{
if (index_0 > 0 && !cfg!(test)) || index_1 - 3 >= 256 {
return Err(DataError::custom(
"Found valid pattern that cannot be stored in ListFormatterPatternsV1",
)
.with_debug_context(pattern));
}
#[allow(clippy::indexing_slicing)] Ok(ListJoinerPattern {
string: Cow::Owned(alloc::format!(
"{}{}{}",
&pattern[0..index_0],
&pattern[index_0 + 3..index_1],
&pattern[index_1 + 3..]
)),
index_0: index_0 as u8,
index_1: (index_1 - 3) as u8,
})
}
_ => Err(DataError::custom("Invalid list pattern").with_debug_context(pattern)),
}
}
fn borrow_tuple(&'data self) -> PatternParts<'data> {
#![allow(clippy::indexing_slicing)] let index_0 = self.index_0 as usize;
let index_1 = self.index_1 as usize;
(
&self.string[0..index_0],
&self.string[index_0..index_1],
&self.string[index_1..],
)
}
fn size_hint(&self) -> LengthHint {
LengthHint::exact(self.string.len())
}
}
#[cfg(feature = "datagen")]
impl<'data> From<ListJoinerPattern<'data>> for ConditionalListJoinerPattern<'data> {
fn from(default: ListJoinerPattern<'data>) -> Self {
Self {
default,
special_case: None,
}
}
}
#[cfg(all(test, feature = "datagen"))]
pub mod test {
use super::*;
pub fn test_patterns() -> ListFormatterPatternsV1<'static> {
let mut patterns = ListFormatterPatternsV1::try_new([
"@{0}:{1}",
"{0},{1}",
"{0}.{1}!",
"${0};{1}+",
"{0}1{1}",
"{0}12{1}",
"{0}12{1}34",
"{0}123{1}456",
"{0}: {1}",
"{0}, {1}",
"{0}. {1}",
"{0}. {1}",
])
.unwrap();
patterns
.make_conditional(
"{0}. {1}",
&SerdeDFA::new(Cow::Borrowed("A")).unwrap(),
"{0} :o {1}",
)
.unwrap();
patterns
}
#[test]
fn rejects_bad_patterns() {
assert!(ListJoinerPattern::from_str("{0} and", true, true).is_err());
assert!(ListJoinerPattern::from_str("and {1}", true, true).is_err());
assert!(ListJoinerPattern::from_str("{1} and {0}", true, true).is_err());
assert!(ListJoinerPattern::from_str("{1{0}}", true, true).is_err());
assert!(ListJoinerPattern::from_str("{0\u{202e}} and {1}", true, true).is_err());
assert!(ListJoinerPattern::from_str("{{0}} {{1}}", true, true).is_ok());
assert!(ListJoinerPattern::from_str("{0} and {1} ", true, true).is_ok());
assert!(ListJoinerPattern::from_str("{0} and {1} ", true, false).is_err());
assert!(ListJoinerPattern::from_str(" {0} and {1}", true, true).is_ok());
assert!(ListJoinerPattern::from_str(" {0} and {1}", false, true).is_err());
}
#[test]
fn produces_correct_parts() {
assert_eq!(
test_patterns().pair(ListLength::Wide).parts(""),
("$", ";", "+")
);
}
#[test]
fn produces_correct_parts_conditionally() {
assert_eq!(
test_patterns().end(ListLength::Narrow).parts("A"),
("", " :o ", "")
);
assert_eq!(
test_patterns().end(ListLength::Narrow).parts("a"),
("", " :o ", "")
);
assert_eq!(
test_patterns().end(ListLength::Narrow).parts("ab"),
("", " :o ", "")
);
assert_eq!(
test_patterns().end(ListLength::Narrow).parts("B"),
("", ". ", "")
);
assert_eq!(
test_patterns().end(ListLength::Narrow).parts("BA"),
("", ". ", "")
);
}
#[test]
fn size_hint_works() {
let pattern = test_patterns();
assert_eq!(
pattern.size_hint(ListLength::Short, 0),
LengthHint::exact(0)
);
assert_eq!(
pattern.size_hint(ListLength::Short, 1),
LengthHint::exact(0)
);
assert_eq!(
pattern.size_hint(ListLength::Short, 2),
LengthHint::exact(6)
);
assert_eq!(
pattern.size_hint(ListLength::Short, 200),
LengthHint::exact(1 + 2 * 197 + 4)
);
assert_eq!(
pattern.size_hint(ListLength::Narrow, 200),
LengthHint::exact(2 + 197 * 2) + LengthHint::between(2, 4)
);
}
}