1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
#![warn(missing_docs)]
//! Funtionality for converting between roman character and japanese kana.
//! 
//! Uses kana spelling, Wapuro romaji as input, and produces
//! either Katakana or Hiragana as output.
//! 
mod set;
mod parser;
pub mod error;

use set::KanaType::{Hiragana, Katakana};
use set::{CharacterSet};
use error::Error;

/// Transform wapuro romaji string to katakana string.
/// 
/// Valid input is ASCII only.
/// 
/// # Examples
/// 
/// ```rust
/// # fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
/// let romaji = "kokonattsu";
/// let katakana = kanabake::to_katakana(romaji)?;
/// 
/// assert_eq!(katakana, "ココナッツ");
/// # Ok(())
/// # }
/// ```
/// 
/// # Error
/// 
pub fn to_katakana(input: &str) -> Result<String, Error> {
    match validate_input(input) {
        Ok(uppercase_input) => {
            let set = CharacterSet::new(Katakana);
            internal_transform(&set, uppercase_input)
        },
        Err(why) => Err(why)
    }
}

/// Transform wapuro romaji string to hiragana string.
/// 
/// Valid input is ASCII only.
/// 
/// # Example
/// 
/// ```rust
/// # fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
/// let romaji = "konnichiha";
/// let hiragana = kanabake::to_hiragana(romaji)?;
/// 
/// assert_eq!(hiragana, "こんにちは");
/// # Ok(())
/// # }
/// ```
pub fn to_hiragana(input: &str) -> Result<String, Error> {
    match validate_input(input) {
        Ok(uppercase_input) => {
            let set = CharacterSet::new(Hiragana);
            internal_transform(&set, uppercase_input)
        },
        Err(why) => Err(why)
    }
}

/// Validate if input can be transformed to kana.
/// 
/// # Example
/// ```rust
/// # fn main() {
/// assert_eq!(kanabake::is_valid("konnichiha"), true);
/// assert_eq!(kanabake::is_valid("könnichiha"), false);
/// # }
pub fn is_valid(input: &str) -> bool {
    if let Ok(uppercase_input) = validate_input(input) {
        match tokenize(&uppercase_input) {
            Ok(_) => return true,
            Err(_) => return false,
        }   
    }

    false
}

// fn is_hiragana(input: &str) -> bool {

// }

// fn is_katakana(input: &str) -> bool {

// }

fn validate_input(input: &str) -> Result<String, Error> {
    if input.is_empty() {
        return Ok(String::new())
    }

    if !input.is_ascii() {
        return Err(Error::new("non-ASCII character in input"))
    }

    Ok(input.trim().to_ascii_uppercase())
}

fn internal_transform(set: &CharacterSet, uppercase_input: String) -> Result<String, Error> {
    let tokens = tokenize(&uppercase_input)?;
    let mut word = String::new();
    for token in tokens {
        match set.get(&token) {
            Some(kana) => word.push_str(kana),
            None => return Err(Error::new(&format!("token {} is not in map", token))),
        }
    }

    Ok(word)
}

fn tokenize(input: &str) -> Result<Vec<&str>, Error> {
    let parser = parser::Parser::new();
    match parser.parse(input) {
        Ok(tokens) => Ok(tokens),
        Err(why) => Err(why)
    }
}