#[cfg(not(target_arch = "wasm32"))]
use unidecode::unidecode;
pub trait StringStrategy: Clone + Default {
fn new() -> Self;
fn prepare(&self, s: &str) -> String;
fn len(&self, s: &str) -> usize;
fn remove(&self, s: &str, index: usize) -> String;
fn slice(&self, s: &str, start: usize, end: usize) -> String;
fn suffix(&self, s: &str, start: usize) -> String;
fn at(&self, s: &str, i: isize) -> Option<char>;
}
#[cfg(not(target_arch = "wasm32"))]
#[derive(Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Default)]
pub struct AsciiStringStrategy {}
#[cfg(not(target_arch = "wasm32"))]
impl StringStrategy for AsciiStringStrategy {
fn new() -> Self {
Self {}
}
fn prepare(&self, s: &str) -> String {
unidecode(s)
}
fn len(&self, s: &str) -> usize {
s.len()
}
fn remove(&self, s: &str, index: usize) -> String {
let mut x = s.to_string();
x.remove(index);
x
}
fn slice(&self, s: &str, start: usize, end: usize) -> String {
s[start..end].to_string()
}
fn suffix(&self, s: &str, start: usize) -> String {
self.slice(s, start, s.len())
}
fn at(&self, s: &str, i: isize) -> Option<char> {
if i < 0 || i >= s.len() as isize {
return None;
}
Some(s.as_bytes()[i as usize] as char)
}
}
pub type UnicodeiStringStrategy = UnicodeStringStrategy;
#[derive(Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Default)]
pub struct UnicodeStringStrategy {}
impl StringStrategy for UnicodeStringStrategy {
fn new() -> Self {
Self {}
}
fn prepare(&self, s: &str) -> String {
s.to_string()
}
fn len(&self, s: &str) -> usize {
s.chars().count()
}
fn remove(&self, s: &str, index: usize) -> String {
s.chars()
.enumerate()
.filter(|(ii, _)| ii != &index)
.map(|(_, ch)| ch)
.collect()
}
fn slice(&self, s: &str, start: usize, end: usize) -> String {
s.chars().skip(start).take(end - start).collect()
}
fn suffix(&self, s: &str, start: usize) -> String {
s.chars().skip(start).collect::<String>()
}
fn at(&self, s: &str, i: isize) -> Option<char> {
if i < 0 {
return None;
}
s.chars().nth(i as usize)
}
}
#[cfg(not(target_arch = "wasm32"))]
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn prepare() {
assert_eq!(AsciiStringStrategy::new().prepare("čičina"), "cicina");
}
#[test]
fn ascii_slice_prefix() {
assert_eq!(AsciiStringStrategy::new().slice("daleko", 0, 3), "dal");
}
#[test]
fn ascii_slice_suffix() {
assert_eq!(AsciiStringStrategy::new().slice("daleko", 3, 6), "eko");
}
#[test]
fn ascii_remove() {
assert_eq!(AsciiStringStrategy::new().remove("daleko", 2), "daeko");
}
#[test]
fn ascii_at_negative() {
assert_eq!(AsciiStringStrategy::new().at("daleko", -2), None);
}
#[test]
fn ascii_at_correct() {
assert_eq!(AsciiStringStrategy::new().at("daleko", 3), Some('e'));
}
#[test]
fn ascii_at_over_limit() {
assert_eq!(AsciiStringStrategy::new().at("daleko", 6), None);
}
#[test]
fn unicodei_strategy() {
assert_eq!(UnicodeiStringStrategy::new().prepare("ciccio"), "ciccio");
}
#[test]
fn unicode_len_multibyte() {
assert_eq!(UnicodeStringStrategy::new().len("héllo"), 5);
assert_eq!(UnicodeStringStrategy::new().len("日本語"), 3);
}
#[test]
fn unicode_slice_multibyte() {
assert_eq!(UnicodeStringStrategy::new().slice("héllo", 0, 3), "hél");
assert_eq!(UnicodeStringStrategy::new().slice("日本語", 1, 3), "本語");
}
#[test]
fn unicode_remove_multibyte() {
assert_eq!(UnicodeStringStrategy::new().remove("héllo", 1), "hllo");
assert_eq!(UnicodeStringStrategy::new().remove("日本語", 0), "本語");
}
#[test]
fn unicode_at_multibyte() {
assert_eq!(UnicodeStringStrategy::new().at("héllo", 1), Some('é'));
assert_eq!(UnicodeStringStrategy::new().at("日本語", 2), Some('語'));
assert_eq!(UnicodeStringStrategy::new().at("日本語", 3), None);
assert_eq!(UnicodeStringStrategy::new().at("日本語", -1), None);
}
#[test]
fn unicode_suffix_multibyte() {
assert_eq!(UnicodeStringStrategy::new().suffix("héllo", 2), "llo");
assert_eq!(UnicodeStringStrategy::new().suffix("日本語", 1), "本語");
}
}