kakasi 0.1.0

Romanize hiragana, katakana and kanji (Japanese text)
Documentation
use crate::types::CharType;

pub const KANJI_DICT: &[u8] = include_bytes!("./kanji_dict.bin");

pub static CLETTERS: phf::Map<u8, &[char]> = phf::phf_map!(
    b'a' => &['', '', '', '', ''],
    b'i' => &['', '', '', ''],
    b'u' => &['', '', ''],
    b'e' => &['', '', '', ''],
    b'o' => &['', '', ''],
    b'k' => &['', '', '', '', '', '', '', ''],
    b'g' => &['', '', '', '', '', ''],
    b's' => &['', '', '', '', '', ''],
    b'z' => &['', '', '', '', '', ''],
    b'j' => &['', '', '', '', '', ''],
    b't' => &['', '', '', '', '', ''],
    b'd' => &['', '', '', '', '', ''],
    b'c' => &['', ''],
    b'n' => &['', '', '', '', '', ''],
    b'h' => &['', '', '', '', '', ''],
    b'b' => &['', '', '', '', '', ''],
    b'f' => &['', ''],
    b'p' => &['', '', '', '', '', ''],
    b'm' => &['', '', '', '', ''],
    b'y' => &['', '', '', '', '', ''],
    b'r' => &['', '', '', '', ''],
    b'w' => &['', '', '', '', '', ''],
    b'v' => &[''],
);

pub static PCT_DICT: phf::Map<char, (char, CharType)> = phf::phf_map!(
    '' => (',', CharType::TrailingPunct),
    '' => ('.', CharType::TrailingPunct),
    '' => ('"', CharType::LeadingPunct),
    '' => ('"', CharType::TrailingPunct),
    '' => ('"', CharType::LeadingPunct),
    '' => ('"', CharType::TrailingPunct),
    '' => ('~', CharType::JoiningPunct),
    '' => (' ', CharType::Whitespace),
    '' => ('<', CharType::LeadingPunct),
    '' => ('>', CharType::TrailingPunct),
    '' => ('«', CharType::LeadingPunct),
    '' => ('»', CharType::TrailingPunct),
    '' => ('[', CharType::LeadingPunct),
    '' => (']', CharType::TrailingPunct),
    '' => ('(', CharType::LeadingPunct),
    '' => (')', CharType::TrailingPunct),
    '' => ('[', CharType::LeadingPunct),
    '' => (']', CharType::TrailingPunct),
    '' => ('(', CharType::LeadingPunct),
    '' => (')', CharType::TrailingPunct),
    '' => ('"', CharType::LeadingPunct),
    '' => ('"', CharType::TrailingPunct),
    '' => (':', CharType::TrailingPunct),
    '' => (';', CharType::TrailingPunct),
    '' => ('!', CharType::TrailingPunct),
    '' => ('?', CharType::TrailingPunct),
    '' => ('?', CharType::LeadingPunct),
    '' => (')', CharType::TrailingPunct),
    '' => (']', CharType::TrailingPunct),
    '' => ('}', CharType::TrailingPunct),
    '' => ('(', CharType::LeadingPunct),
    '' => ('[', CharType::LeadingPunct),
    '' => ('{', CharType::LeadingPunct),
    '_' => ('{', CharType::JoiningPunct),

    ':' => (':', CharType::TrailingPunct),
    ';' => (';', CharType::TrailingPunct),
    '!' => ('!', CharType::TrailingPunct),
    '?' => ('?', CharType::TrailingPunct),
    '#' => ('#', CharType::JoiningPunct),
    ')' => (')', CharType::TrailingPunct),
    ']' => (']', CharType::TrailingPunct),
    '}' => ('}', CharType::TrailingPunct),
    '(' => ('(', CharType::LeadingPunct),
    '[' => ('[', CharType::LeadingPunct),
    '{' => ('{', CharType::LeadingPunct),
    '_' => ('_', CharType::JoiningPunct),
);

pub const HIRAGANA: (u32, u32) = (0x3041, 0x3096);
pub const KATAKANA: (u32, u32) = (0x30A1, 0x30FA);
pub const KANJI: (u32, u32) = (0x4E00, 0x9FAF);

pub const ITERATION_MARK: char = '';
pub const PROLONGED_SOUND_MARK: char = '';

const CJK_SYMBOLS_PUNCTUATION: (u32, u32) = (0x3000, 0x303F);
const KANA_PUNCTUATION: (u32, u32) = (0xFF61, 0xFF65);
const KATAKANA_PUNCTUATION: (u32, u32) = (0x30FB, 0x30FC);
pub const ZENKAKU_PUNCTUATION_1: (u32, u32) = (0xFF01, 0xFF0F);
pub const ZENKAKU_PUNCTUATION_2: (u32, u32) = (0xFF1A, 0xFF1F);
pub const ZENKAKU_PUNCTUATION_3: (u32, u32) = (0xFF3B, 0xFF3F);
pub const ZENKAKU_PUNCTUATION_4: (u32, u32) = (0xFF5B, 0xFF60);

const JA_PUNCTUATION_RANGES: [(u32, u32); 7] = [
    CJK_SYMBOLS_PUNCTUATION,
    KANA_PUNCTUATION,
    KATAKANA_PUNCTUATION,
    ZENKAKU_PUNCTUATION_1,
    ZENKAKU_PUNCTUATION_2,
    ZENKAKU_PUNCTUATION_3,
    ZENKAKU_PUNCTUATION_4,
];

const FW_PUNCTUATION_RANGES: [(u32, u32); 4] = [
    ZENKAKU_PUNCTUATION_1,
    ZENKAKU_PUNCTUATION_2,
    ZENKAKU_PUNCTUATION_3,
    ZENKAKU_PUNCTUATION_4,
];

pub fn is_char_in_range(c: char, range: (u32, u32)) -> bool {
    range.0 <= c as u32 && c as u32 <= range.1
}

pub fn is_char_japanese_punctuation(c: char) -> bool {
    JA_PUNCTUATION_RANGES
        .iter()
        .any(|r| is_char_in_range(c, *r))
}

pub fn is_char_fwidth_punctuation(c: char) -> bool {
    FW_PUNCTUATION_RANGES
        .iter()
        .any(|r| is_char_in_range(c, *r))
}

pub fn capitalize_first_c(text: &str) -> String {
    let mut done = false;
    let res = text
        .chars()
        .map(|c| {
            if !done && c.is_alphanumeric() {
                done = true;
                c.to_ascii_uppercase()
            } else {
                c
            }
        })
        .collect::<String>();
    res
}

pub fn ensure_trailing_space(text: &mut String, ts: bool) {
    if text.is_empty() || text.ends_with('\n') {
        return;
    }

    if text.ends_with(' ') {
        if !ts {
            text.pop();
        }
    } else if ts {
        text.push(' ');
    }
}