use core::convert::Infallible;
use core::{cmp::Ordering, str::FromStr};
use writeable::adapters::WriteableAsTryWriteableInfallible;
use writeable::Writeable;
use crate::common::*;
use crate::Error;
#[cfg(feature = "alloc")]
use alloc::{boxed::Box, string::String};
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
#[allow(clippy::exhaustive_enums)] pub enum SinglePlaceholderKey {
Singleton,
}
impl FromStr for SinglePlaceholderKey {
type Err = Infallible;
fn from_str(_: &str) -> Result<Self, Self::Err> {
Ok(Self::Singleton)
}
}
impl<W> PlaceholderValueProvider<SinglePlaceholderKey> for (W,)
where
W: Writeable,
{
type Error = Infallible;
type W<'a>
= WriteableAsTryWriteableInfallible<&'a W>
where
Self: 'a;
type L<'a, 'l>
= &'l str
where
Self: 'a;
fn value_for(&self, _key: SinglePlaceholderKey) -> Self::W<'_> {
WriteableAsTryWriteableInfallible(&self.0)
}
#[inline]
fn map_literal<'a, 'l>(&'a self, literal: &'l str) -> Self::L<'a, 'l> {
literal
}
}
impl<W> PlaceholderValueProvider<SinglePlaceholderKey> for [W; 1]
where
W: Writeable,
{
type Error = Infallible;
type W<'a>
= WriteableAsTryWriteableInfallible<&'a W>
where
Self: 'a;
type L<'a, 'l>
= &'l str
where
Self: 'a;
fn value_for(&self, _key: SinglePlaceholderKey) -> Self::W<'_> {
let [value] = self;
WriteableAsTryWriteableInfallible(value)
}
#[inline]
fn map_literal<'a, 'l>(&'a self, literal: &'l str) -> Self::L<'a, 'l> {
literal
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[allow(clippy::exhaustive_enums)] pub enum SinglePlaceholder {}
impl crate::private::Sealed for SinglePlaceholder {}
impl PatternBackend for SinglePlaceholder {
type PlaceholderKey<'a> = SinglePlaceholderKey;
#[cfg(feature = "alloc")]
type PlaceholderKeyCow<'a> = SinglePlaceholderKey;
type Error<'a> = Infallible;
type Store = str;
type Iter<'a> = SinglePlaceholderPatternIterator<'a>;
fn validate_store(store: &Self::Store) -> Result<(), Error> {
let placeholder_offset_char = store.chars().next().ok_or(Error::InvalidPattern)?;
let initial_offset = placeholder_offset_char.len_utf8();
let placeholder_offset = placeholder_offset_char as usize;
if placeholder_offset > store.len() - initial_offset + 1 {
return Err(Error::InvalidPattern);
}
if placeholder_offset >= 0xD800 {
return Err(Error::InvalidPattern);
}
Ok(())
}
fn iter_items(store: &Self::Store) -> Self::Iter<'_> {
let placeholder_offset_char = match store.chars().next() {
Some(i) => i,
None => {
debug_assert!(false);
'\0'
}
};
let initial_offset = placeholder_offset_char.len_utf8();
SinglePlaceholderPatternIterator {
store,
placeholder_offset: placeholder_offset_char as usize + initial_offset - 1,
current_offset: initial_offset,
}
}
#[cfg(feature = "alloc")]
fn try_from_items<
'cow,
'ph,
I: Iterator<Item = Result<PatternItemCow<'cow, Self::PlaceholderKey<'ph>>, Error>>,
>(
items: I,
) -> Result<Box<str>, Error> {
let mut result = String::new();
let mut seen_placeholder = false;
for item in items {
match item? {
PatternItemCow::Literal(s) => result.push_str(&s),
PatternItemCow::Placeholder(_) if !seen_placeholder => {
seen_placeholder = true;
let placeholder_offset =
u32::try_from(result.len() + 1).map_err(|_| Error::InvalidPattern)?;
if placeholder_offset >= 0xD800 {
return Err(Error::InvalidPattern);
}
let placeholder_offset_char =
char::try_from(placeholder_offset).map_err(|_| Error::InvalidPattern)?;
result.insert(0, placeholder_offset_char);
}
PatternItemCow::Placeholder(_) => {
return Err(Error::InvalidPattern);
}
}
}
if !seen_placeholder {
result.insert(0, '\0');
}
Ok(result.into_boxed_str())
}
fn empty() -> &'static Self::Store {
"\0"
}
}
#[doc(hidden)] #[derive(Debug)]
pub struct SinglePlaceholderPatternIterator<'a> {
store: &'a str,
placeholder_offset: usize,
current_offset: usize,
}
impl ExactSizeIterator for SinglePlaceholderPatternIterator<'_> {
fn len(&self) -> usize {
let placeholder_offset_char = match self.store.chars().next() {
Some(i) => i,
None => {
debug_assert!(false);
'\0'
}
};
let initial_offset = placeholder_offset_char.len_utf8();
let placeholder_offset = placeholder_offset_char as usize + initial_offset - 1;
let store_len = self.store.len();
if placeholder_offset < initial_offset {
if initial_offset < store_len {
1
} else {
0
}
} else if placeholder_offset == initial_offset {
if initial_offset < store_len {
2
} else {
1
}
} else if placeholder_offset < store_len {
3
} else {
2
}
}
}
impl<'a> Iterator for SinglePlaceholderPatternIterator<'a> {
type Item = PatternItem<'a, SinglePlaceholderKey>;
fn next(&mut self) -> Option<Self::Item> {
match self.current_offset.cmp(&self.placeholder_offset) {
Ordering::Less => {
let literal_str = match self.store.get(self.current_offset..self.placeholder_offset)
{
Some(s) => s,
None => {
debug_assert!(false, "offsets are in range");
""
}
};
self.current_offset = self.placeholder_offset;
Some(PatternItem::Literal(literal_str))
}
Ordering::Equal => {
self.placeholder_offset = 0;
Some(PatternItem::Placeholder(SinglePlaceholderKey::Singleton))
}
Ordering::Greater => {
let literal_str = match self.store.get(self.current_offset..) {
Some(s) => s,
None => {
debug_assert!(false, "offsets are in range");
""
}
};
if literal_str.is_empty() {
None
} else {
self.current_offset = self.store.len();
Some(PatternItem::Literal(literal_str))
}
}
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
let len = self.len();
(len, Some(len))
}
}