1use std::borrow::Borrow;
2
3use smallvec::SmallVec;
4
5use crate::{CharString, char_string::CHAR_STRING_INLINE_SIZE};
6
7#[must_use]
14pub fn copy_casing(
15 template: impl IntoIterator<Item = impl Borrow<char>>,
16 target: impl IntoIterator<Item = impl Borrow<char>>,
17) -> CharString {
18 target
19 .into_iter()
20 .scan(
21 (template.into_iter().get_casing(), Case::Lower),
22 |(template, prev_case), c| {
23 if c.borrow().is_alphabetic()
25 && let Some(template_case) = template.next()
26 {
27 *prev_case = template_case;
28 };
29 Some(prev_case.apply_to(*c.borrow()))
30 },
31 )
32 .flatten()
33 .collect()
34}
35
36#[derive(Debug, Clone, Copy, PartialEq, Eq)]
38pub enum Case {
39 Upper,
40 Lower,
41}
42
43impl Case {
44 pub fn apply_to(&self, char: char) -> impl Iterator<Item = char> + use<> {
50 match self {
51 Self::Upper => char.to_uppercase().collect::<SmallVec<[char; 2]>>(),
52 Self::Lower => char.to_lowercase().collect::<SmallVec<[char; 2]>>(),
53 }
54 .into_iter()
55 }
56}
57
58impl TryFrom<char> for Case {
59 type Error = ();
60
61 fn try_from(value: char) -> Result<Self, Self::Error> {
65 if value.is_uppercase() {
66 Ok(Self::Upper)
67 } else if value.is_lowercase() {
68 Ok(Self::Lower)
69 } else {
70 Err(())
71 }
72 }
73}
74
75pub trait CaseIterExt {
78 fn get_casing(self) -> impl Iterator<Item = Case>;
79 fn get_casing_unfiltered(self) -> SmallVec<[Option<Case>; CHAR_STRING_INLINE_SIZE]>;
80}
81impl<I: IntoIterator<Item = T>, T: Borrow<char>> CaseIterExt for I {
82 fn get_casing(self) -> impl Iterator<Item = Case> {
85 self.into_iter()
86 .filter_map(|char| (*char.borrow()).try_into().ok())
87 }
88
89 fn get_casing_unfiltered(self) -> SmallVec<[Option<Case>; CHAR_STRING_INLINE_SIZE]> {
93 self.into_iter()
94 .map(|c| Case::try_from(*c.borrow()).ok())
95 .collect()
96 }
97}