use std::collections::HashMap;
pub trait CharacterWidth {
fn to_half_width(&self) -> String;
fn to_full_width(&self) -> String;
fn to_half_width_ext(&self, ignore: Vec<&str>, alpha: bool, symbols: bool, kana: bool, hangul: bool) -> Result<String, String>;
fn to_full_width_ext(&self, ignore: Vec<&str>, alpha: bool, symbols: bool, kana: bool, hangul: bool) -> Result<String, String>;
}
impl<T: AsRef<str>> CharacterWidth for T {
fn to_half_width(&self) -> String {
let map = create_map(false);
self.replace_characters(&map)
}
fn to_full_width(&self) -> String {
let map = create_map(true);
self.replace_characters(&map)
}
fn to_half_width_ext(&self, ignore: Vec<&str>, alpha: bool, symbols: bool, kana: bool, hangul: bool) -> Result<String, String> {
let map = create_map_ext(ignore, alpha, symbols, kana, hangul, false)?;
Ok(self.replace_characters(&map))
}
fn to_full_width_ext(&self, ignore: Vec<&str>, alpha: bool, symbols: bool, kana: bool, hangul: bool) -> Result<String, String> {
let map = create_map_ext(ignore, alpha, symbols, kana, hangul, true)?;
Ok(self.replace_characters(&map))
}
}
trait ReplaceCharacters {
fn replace_characters(&self, map: &HashMap<String, String>) -> String;
}
impl<T: AsRef<str>> ReplaceCharacters for T {
fn replace_characters(&self, map: &HashMap<String, String>) -> String {
let mut sorted_map: Vec<_> = map.iter().collect();
sorted_map.sort_by(|a, b| b.0.len().cmp(&a.0.len()));
sorted_map.iter().fold(self.as_ref().to_string(), |acc, (k, v)| {
acc.replace(k.as_str(), v.as_str())
})
}
}
fn create_map(reverse: bool) -> HashMap<String, String> {
let mut map = HashMap::new();
map.extend(create_alpha_numeric_map(reverse));
map.extend(create_symbols_map(reverse));
map.extend(create_kana_map(reverse));
map.extend(create_hangul_map(reverse));
map
}
fn create_map_ext(
ignore: Vec<&str>,
alpha: bool,
symbols: bool,
kana: bool,
hangul: bool,
reverse: bool,
) -> Result<HashMap<String, String>, String> {
let mut map = HashMap::new();
if alpha { map.extend(create_alpha_numeric_map(reverse)); }
if symbols { map.extend(create_symbols_map(reverse)); }
if kana { map.extend(create_kana_map(reverse)); }
if hangul { map.extend(create_hangul_map(reverse)); }
for i in ignore.iter() {
match map.remove(*i) {
Some(_) => (),
None => return Err(format!("'{}' is not a valid character to ignore.", i)),
}
}
Ok(map)
}
fn create_alpha_numeric_map(reverse: bool) -> HashMap<String, String> {
let mut map = HashMap::new();
let full_width = vec![
"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F", "G",
"H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X",
"Y", "Z", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o",
"p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z",
];
let half_width = vec![
"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F", "G", "H", "I",
"J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "a", "b",
"c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u",
"v", "w", "x", "y", "z",
];
for (i, j) in full_width.iter().zip(half_width.iter()) {
match reverse {
true => map.insert(j.to_string(), i.to_string()),
false => map.insert(i.to_string(), j.to_string()),
};
}
map
}
fn create_symbols_map(reverse: bool) -> HashMap<String, String> {
let mut map = HashMap::new();
let full_width = vec![
"!", """, "#", "$", "%", "&", "'", "(", ")", "*", "+", ",", "-", ".", "/",
":", ";", "<", "=", ">", "?", "@", "[", "\", "]", "^", "_", "`", "{", "|",
"}", "~", "。", "、", "¢", "£", "¬", " ̄", "¦", "¥", "₩", " ",
];
let half_width = vec![
"!", "\"", "#", "$", "%", "&", "'", "(", ")", "*", "+", ",", "-", ".", "/",
":", ";", "<", "=", ">", "?", "@", "[", "\\", "]", "^", "_", "`", "{", "|",
"}", "~", "。", "、", "¢", "£", "¬", "¯", "¦", "¥", "₩", " ",
];
for (i, j) in full_width.iter().zip(half_width.iter()) {
match reverse {
true => map.insert(j.to_string(), i.to_string()),
false => map.insert(i.to_string(), j.to_string()),
};
}
map
}
fn create_kana_map(reverse: bool) -> HashMap<String, String> {
let mut map = HashMap::new();
let mut full_width = vec![
"ァ", "ア", "ィ", "イ", "ゥ", "ウ", "ェ", "エ", "ォ", "オ", "カ", "ガ", "キ", "ギ", "ク",
"グ", "ケ", "ゲ", "コ", "ゴ", "サ", "ザ", "シ", "ジ", "ス", "ズ", "セ", "ゼ", "ソ", "ゾ", "タ",
"ダ", "チ", "ヂ", "ッ", "ツ", "ヅ", "テ", "デ", "ト", "ド", "ナ", "ニ", "ヌ", "ネ", "ノ", "ハ",
"バ", "パ", "ヒ", "ビ", "ピ", "フ", "ブ", "プ", "ヘ", "ベ", "ペ", "ホ", "ボ", "ポ", "マ", "ミ",
"ム", "メ", "モ", "ャ", "ヤ", "ュ", "ユ", "ョ", "ヨ", "ラ", "リ", "ル", "レ", "ロ", "ヮ", "ワ",
"ヲ", "ン", "ヴ", "ヷ", "ヺ", "・", "ー", "「", "」",
];
let mut half_width = vec![
"ァ", "ア", "ィ", "イ", "ゥ", "ウ", "ェ", "エ", "ォ", "オ", "カ", "ガ", "キ", "ギ", "ク",
"グ", "ケ", "ゲ", "コ", "ゴ", "サ", "ザ", "シ", "ジ", "ス", "ズ", "セ", "ゼ", "ソ", "ゾ", "タ",
"ダ", "チ", "ヂ", "ッ", "ツ", "ヅ", "テ", "デ", "ト", "ド", "ナ", "ニ", "ヌ", "ネ", "ノ", "ハ",
"バ", "パ", "ヒ", "ビ", "ピ", "フ", "ブ", "プ", "ヘ", "ベ", "ペ", "ホ", "ボ", "ポ", "マ", "ミ",
"ム", "メ", "モ", "ャ", "ヤ", "ュ", "ユ", "ョ", "ヨ", "ラ", "リ", "ル", "レ", "ロ", "ワ", "ワ",
"ヲ", "ン", "ヴ", "ヷ", "ヺ", "・", "ー", "「", "」",
];
if !reverse {
full_width.push("ヵ");
half_width.push("カ");
full_width.push("ヶ");
half_width.push("ケ");
}
for (i, j) in full_width.iter().zip(half_width.iter()) {
match reverse {
true => map.insert(j.to_string(), i.to_string()),
false => map.insert(i.to_string(), j.to_string()),
};
}
map
}
fn create_hangul_map(reverse: bool) -> HashMap<String, String> {
let mut map = HashMap::new();
let full_width = vec![
"ㄱ", "ㄲ", "ㄳ", "ㄴ", "ㄵ", "ㄶ", "ㄷ", "ㄸ", "ㄹ", "ㄺ", "ㄻ", "ㄼ", "ㄽ", "ㄾ", "ㄿ", "ㅀ", "ㅁ", "ㅂ", "ㅃ", "ㅄ", "ㅅ", "ㅆ", "ㅇ", "ㅈ", "ㅉ", "ㅊ", "ㅋ", "ㅌ", "ㅍ", "ㅎ", "ㅏ", "ㅐ", "ㅑ", "ㅒ", "ㅓ", "ㅔ", "ㅕ", "ㅖ", "ㅗ", "ㅘ", "ㅙ", "ㅚ", "ㅛ", "ㅜ", "ㅝ", "ㅞ", "ㅟ", "ㅠ", "ㅡ", "ㅢ", "ㅣ", ];
let half_width = vec![
"ᄀ", "ᄁ", "ᆪ", "ᄂ", "ᆬ", "ᆭ", "ᄃ", "ᄄ", "ᄅ", "ᆰ", "ᆱ", "ᆲ", "ᆳ", "ᆴ", "ᆵ", "ᄚ", "ᄆ", "ᄇ", "ᄈ", "ᄡ", "ᄉ", "ᄊ", "ᄋ", "ᄌ", "ᄍ", "ᄎ", "ᄏ", "ᄐ", "ᄑ", "ᄒ", "ᅡ", "ᅢ", "ᅣ", "ᅤ", "ᅥ", "ᅦ", "ᅧ", "ᅨ", "ᅩ", "ᅪ", "ᅫ", "ᅬ", "ᅭ", "ᅮ", "ᅯ", "ᅰ", "ᅱ", "ᅲ", "ᅳ", "ᅴ", "ᅵ", ];
for (i, j) in full_width.iter().zip(half_width.iter()) {
match reverse {
true => map.insert(j.to_string(), i.to_string()),
false => map.insert(i.to_string(), j.to_string()),
};
}
map
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_alpha_numeric_map() {
let full_width = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
let half_width = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
for (full, half) in full_width.chars().zip(half_width.chars()) {
assert_eq!(full.to_string().to_half_width(), half.to_string());
assert_eq!(half.to_string().to_full_width(), full.to_string());
}
}
#[test]
fn test_symbols_map() {
let full_width = "!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~。、¢£¬ ̄¦¥₩ ";
let half_width = "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~。、¢£¬¯¦¥₩ ";
for (full, half) in full_width.chars().zip(half_width.chars()) {
assert_eq!(full.to_string().to_half_width(), half.to_string());
assert_eq!(half.to_string().to_full_width(), full.to_string());
}
}
#[test]
fn test_kana_map() {
let full_width = "ァアィイゥウェエォオカキクケコサシスセソタチッツテトナニヌネノハヒフヘホマミムメモャヤュユョヨラリルレロヮワヲン・ー「」ヵヶ";
let half_width = "ァアィイゥウェエォオカキクケコサシスセソタチッツテトナニヌネノハヒフヘホマミムメモャヤュユョヨラリルレロワワヲン・ー「」カケ";
for (full, half) in full_width.chars().zip(half_width.chars()) {
if full == 'ヮ' || half == 'ワ' {
assert_eq!(full.to_string().to_half_width(), "ワ".to_string());
assert_eq!(half.to_string().to_full_width(), "ワ".to_string());
continue;
}
if full == 'ヵ' || half == 'カ' {
assert_eq!(full.to_string().to_half_width(), "カ".to_string());
assert_eq!(half.to_string().to_full_width(), "カ".to_string());
continue;
}
if full == 'ヶ' || half == 'ケ' {
assert_eq!(full.to_string().to_half_width(), "ケ".to_string());
assert_eq!(half.to_string().to_full_width(), "ケ".to_string());
continue;
}
assert_eq!(full.to_string().to_half_width(), half.to_string());
assert_eq!(half.to_string().to_full_width(), full.to_string());
}
let special_full_width = "ガギグゲゴザジズゼゾダヂヅデドバパビピブプベペボポヴヷヺ";
let special_half_width = vec![
"ガ", "ギ", "グ", "ゲ", "ゴ", "ザ", "ジ", "ズ", "ゼ", "ゾ", "ダ", "ヂ", "ヅ", "デ", "ド",
"バ", "パ", "ビ", "ピ", "ブ", "プ", "ベ", "ペ", "ボ", "ポ", "ヴ", "ヷ", "ヺ"
];
for (full, half) in special_full_width.chars().zip(special_half_width.iter()) {
assert_eq!(full.to_string().to_half_width(), half.to_string());
assert_eq!(half.to_string().to_full_width(), full.to_string());
}
}
#[test]
fn test_hangul_map() {
let full_width = "ㄱㄲㄳㄴㄵㄶㄷㄸㄹㄺㄻㄼㄽㄾㄿㅀㅁㅂㅃㅄㅅㅆㅇㅈㅉㅊㅋㅌㅍㅎㅏㅐㅑㅒㅓㅔㅕㅖㅗㅘㅙㅚㅛㅜㅝㅞㅟㅠㅡㅢㅣ";
let half_width = "ᄀᄁᆪᄂᆬᆭᄃᄄᄅᆰᆱᆲᆳᆴᆵᄚᄆᄇᄈᄡᄉᄊᄋᄌᄍᄎᄏᄐᄑ하ᅢᅣᅤᅥᅦᅧᅨᅩᅪᅫᅬᅭᅮᅯᅰᅱᅲᅳᅴᅵ";
for (full, half) in full_width.chars().zip(half_width.chars()) {
assert_eq!(full.to_string().to_half_width(), half.to_string());
assert_eq!(half.to_string().to_full_width(), full.to_string());
}
}
#[test]
fn test_character_width_trait() {
let full_width_string = "Hello World!";
let half_width_string = "Hello World!";
assert_eq!(full_width_string.to_half_width(), half_width_string);
assert_eq!(
full_width_string.to_half_width_ext(vec![], false, true, true, true).unwrap(),
"Hello World!"
);
assert_eq!(
full_width_string.to_half_width_ext(vec![], true, false, true, true).unwrap(),
"Hello World!"
);
let full_width_string = "カタカナ";
let half_width_string = "カタカナ";
assert_eq!(full_width_string.to_half_width(), half_width_string);
assert_eq!(
full_width_string.to_half_width_ext(vec![], true, true, false, true).unwrap(),
full_width_string
);
let full_width_string = "ㅈㅉ";
let half_width_string = "ᄌᄍ";
assert_eq!(full_width_string.to_half_width(), half_width_string);
assert_eq!(
full_width_string.to_half_width_ext(vec![], true, true, true, false).unwrap(),
full_width_string
);
let full_width_string = "Hello World!カタカナㅈㅉ";
let half_width_string = "Hello World!カタカナᄌᄍ";
assert_eq!(full_width_string.to_half_width(), half_width_string);
assert_eq!(
full_width_string.to_half_width_ext(vec![], false, false, false, false).unwrap(),
full_width_string
);
let full_width_string = "Hello World!カタカナㅈㅉ";
let half_width_string = "Hello World!カタカナㅈᄍ";
assert_eq!(
full_width_string.to_half_width_ext(vec!["!", "e", "カ", "ㅈ"], true, true, true, true).unwrap(),
half_width_string
);
assert!(full_width_string.to_half_width_ext(vec!["!"], true, true, true, true).is_err());
assert!(half_width_string.to_full_width_ext(vec!["!"], true, true, true, true).is_err());
}
#[test]
fn test_replace_characters_trait() {
let mut map = HashMap::new();
map.insert("a".to_string(), "b".to_string());
let string = "abcdefg";
assert_eq!(string.replace_characters(&map), "bbcdefg");
}
}