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
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
//! # TossiCat Core
//!
//! ## 토시를 같이 입력된 단어에 맞춰 변환해 주는 모듈
//!
//! 이 모듈은 임의의 단어와 그 단어에 붙일 조사(즉 토시)를 입력하면,
//! 입력한 조사를 같이 입력한 단어에 자연스러운 형태로 바꿔서
//! 적절하게 변환하는 라이브러리입니다.
//!
//! 변환할 토시가 여러개 들어 있는 문장을 아래와 같은 형식으로 입력하면,
//!
//! `"{철수, 은} {영희,   과} {밥,  를} 먹습니다."`
//!
//! 다음과 같이 변경해 줍니다.
//!
//!  `"철수는 영희처럼 밥을 먹습니다."`
//!
//! 이것은 `modify_sentence()` 함수가 맡습니다. 아래와 같이 사용하시면 됩니다.
//!
//! ```
//! use tossicat::modify_sentence;
//!
//! let test = "{철수, 은} {영희, 과} {밥, 를} 먹습니다.";
//! let result = Ok("철수는 영희와 밥을 먹습니다.".to_string());
//! assert_eq!(result, modify_sentence(test));
//! ```
//!
//! 앞에서처럼 문장을 다루는 것이 아니라 단순하게 특정 단어에 특정 토시를 붙일 때
//! 어떻게 변환해야 하는 것인지를 알고 싶은 경우도 있습니다. 이런 경우에 사용할 함수는
//! `postfix()`입니다.
//!
//! ```
//! use tossicat::postfix;
//!
//! let result = Ok("사과를".to_string());
//! assert_eq!(result, postfix("사과", "을"));
//! ```
//!
//! 현재 단어의 마지막 글자가 한글과 숫자라면 아무런 문제 없이 같이 입력된 토시를 적절하게
//! 변경해줄 수 있습니다. 그런데 입력된 단어 전체가 영어와 같은 외국어 단어로 되어 있거나
//! 마지막 글자가 외국어라면, 에러를 발생하지 않고
//! 아래와 같이 토시를 병기해 처리합니다. 따라서 외국어를 사용하는데도 문제가 발생하지
//! 않습니다.
//!
//! ```
//! use tossicat::postfix;
//!
//! let test = postfix("apple", "을");
//! let result = Ok("apple(을)를".to_string());
//! assert_eq!(test, result);
//!
//! use tossicat::modify_sentence;
//!
//! let test = "{철수, 은} {apple, 를} 먹습니다.";
//! let result = Ok("철수는 apple(을)를 먹습니다.".to_string());
//! assert_eq!(result, modify_sentence(test));
//! ```
//!
//! 이 두 개의 함수가 이 라이브러리의 가장 중요한 기능입니다.
//! 이 라이브러리에서 구현하고 있는 중요한 함수는 다음과 같습니다.
//!
//! - `modify_sentence()`: 입력된 문장에 포함된 1개 이상의 토시를 같이 입력된 단어에 맞게 전환해
//! 입력된 문장을 바꿔 반환하는 함수
//! - `postfix()`: 입력된 토시를 같이 입력된 단어에 맞게 변환해, 입력된 단어와 합쳐 반환하는 함수
//!
//! 위의 기능을 구현하기 위해서 작성한 몇개의 함수도 같이 공개합니다.
//! 자세한 내용은 각 함수 설명을 참고하세요.

pub mod bracket;
mod error;
mod filter;
mod hangeul;
mod identifier;
mod number;
mod tossi;
mod transfer;
mod verifier;

use error::{ParseError, ValueError};
use identifier::{Tossi, TossiKind};

// bracket 모듈에 있습니다.
// tests/bracket.rs 에서 test 합니다.

/// ## 변경할 토시가 여러 개 들어 있는 문장을 적절한 토시로 변경해 문장을 반환하는 함수  
///
/// 아래 보기 같이 변결할 단어와 토시가 중괄호로 여러 개를 감싸고 있는 문장에서
/// 토시를 같이 입력된 단어에 맞게 적절한 토시로 일괄적으로 변경해서 아래 변환 결과처럼
/// 중괄호, `{,}`를 제거해 완전한 문장으로 바꿔 변환해 줍니다.
///
/// 보기: `"{철수, 은} {영희, 처럼} {밥,  를} 먹습니다.";`
///
/// 변환 결과: `"철수는 영희처럼 밥을 먹습니다.";`
///
/// 구체적인 사용 방법은 다음과 같습니다.
///
/// ```rust
/// use tossicat::modify_sentence;
///
/// let test = "{철수, 은} {영희, 처럼} {밥,  를} 먹습니다.";
/// let result = Ok("철수는 영희처럼 밥을 먹습니다.".to_string());
/// assert_eq!(result, modify_sentence(test));
/// ```

pub fn modify_sentence(string: &str) -> Result<String, ParseError> {
    // let mut original_copy = string;
    let mut sentence = String::from(string);
    // let temp = bracket::modify_pairs(string);
    let temp = match bracket::modify_pairs(string) {
        Ok(temp) => temp,
        Err(e) => return Err(ParseError::new(e)),
    };
    let mut temp_tossi_num: Vec<bool> = vec![];
    for item in temp {
        let result = postfix(&item.1, &item.2);
        temp_tossi_num.push(true);
        let original = "{".to_string() + &item.0 + "}";
        match result {
            Ok(n) => sentence = sentence.replace(&original, &n),
            Err(e) => {
                return Err(ParseError::new(error::ParseErrorType::InvalidValue(
                    e.error,
                )))
            }
        }
    }
    Ok(sentence)
}

// hangeul 모듈에 있습니다.
// tests/hangeul.rs 에서 test 합니다.

/// ## 초, 중, 종성을 한글 한 글자로 합쳐주는 함수
///
/// 이 함수는 기본적으로 입력된 것이 종성까지 가지고 있는다고 가정하고 작성하였습니다.
/// 사용하기 위해서는 종성이 없는 경우에도 다음과 같이 종성 자리에 ` `를 넣어야 합니다.
/// 만약 종성이 없이 글자를 만들려고 한다면  `['ㅈ','ㅏ',' ']`처럼
/// 공백으로 종성을 넣어야 합니다. 만악 `['ㅈ','ㅏ','']` 처럼
/// 한다면 에러가 발생합니다.
///
/// ```rust
/// use tossicat::join_phonemes;
/// assert_eq!('글', join_phonemes(['ㄱ','ㅡ','ㄹ']));
/// assert_eq!('자', join_phonemes(['ㅈ','ㅏ',' ']));
/// ```

pub fn join_phonemes(word: [char; 3]) -> char {
    hangeul::join_phonemes(word)
}

// hangeul 모듈에 있습니다.
// tests/hangeul.rs 에서 test 합니다.

/// ## 입력된 한 글자에서 그 글자의 종성을 바꿔주는 함수
/// 이 함수는 입력된 한글 한 글자에서 입력된 값으로 종성을 바꿔 반환한다.
/// 이때 입력된 한 글자가 한글이 아닌 경우와
/// 바꾸기 위해 입력한 자모가 한글 종성 자모에 쓰일 수 없는 것이면
/// 입력된 글자 그대로를 반환한다.
/// ```rust
///    let temp = '정';
///    assert_eq!('점', tossicat::modify_finall_jamo(temp, 'ㅁ'));
///    let temp = '감';
///    assert_eq!('강', tossicat::modify_finall_jamo(temp, 'ㅇ'));
/// ```

pub fn modify_finall_jamo(letter: char, jamo: char) -> char {
    hangeul::modify_finall_jamo(letter, jamo)
}

// hangeul 모듈에 있습니다.
// tests/hangeul.rs 에서 test 합니다.

/// ## 한글 한 글자를 초, 중, 종성으로 분리해 주는 함수
///
/// 이 함수는 기본적으로 입력된 것이 종성이 없는 경우에도
/// 종성을 스페이스, 즉 `' '`으로 반환합니다.
/// 예를 들어 만약 종성이 없는 경우에는 ' '으로 치환됩니다.
/// 아래 2번째 예를 참고하세요.
///
/// ```rust
/// use tossicat::split_phonemes;
/// assert_eq!(['ㄱ','ㅡ','ㄹ'], split_phonemes('글'));
/// assert_eq!(['ㅈ','ㅏ',' '], split_phonemes('자'));
/// ```

pub fn split_phonemes(word: char) -> [char; 3] {
    hangeul::split_phonemes(word)
}

// filter 모듈
// tests/filter.rs 에서 test 합니다.

/// ## 입력된 문자열에서 마지막 글자를 찾아 주는함수
///
/// 만약 입력된 문자가 한글이 아니면 `N`을 반환합니다.
/// 아래 2번째 예를 참고하세요.
/// 숫자는 한글로 변환해서 마지막 글자를 뽑아냅니다.
/// 아래 3번째 예를 참고하세요.
/// 마지막 문자만을 뽑아내는 것이기 때문에 마지막 글자만 한글이나 숫자이면 됩니다.
///
/// ```rust
/// use tossicat::find_last_letter;
/// assert_eq!('자', find_last_letter("글자"));
/// assert_eq!('N', find_last_letter("apple"));
/// assert_eq!('삼', find_last_letter("123"));
/// ```

pub fn find_last_letter(word: &str) -> char {
    filter::find_last_letter(word)
}

// filter 모듈
// tests/filter.rs 에 test 합니다.

/// ## 종성만 찾아서 도출해주는 함수
///
/// 이 함수는 특정 문자열에서 마지막 글자의 종성만 도출합니다.
/// 만약 마지막 글자가 종성이 없는 경우에는 스페이스, 즉 `' '`을 반환합니다.
/// 예를 들어 만약 종성이 없는 경우에는 ' '으로 치환됩니다.
/// 아래 2번째 예를 참고하세요.
/// 만약 입력된 문자가 한글이 아니면 `N`을 반환합니다.
/// 아래 4번째 예를 참고하세요.
///
/// ```rust
/// use tossicat::guess_final_letter;
/// assert_eq!('ㄹ', guess_final_letter("글"));
/// assert_eq!(' ', guess_final_letter("글자"));
/// assert_eq!('ㅇ', guess_final_letter("몸빵"));
/// assert_eq!('N', guess_final_letter("apple"));
/// ```

pub fn guess_final_letter(word: &str) -> char {
    filter::guess_final_letter(word)
}

// number 모듈
// tests/number.rs 에서 test 한다.

/// ## 숫자를 한글 표기법으로 바꿔주는 함수
///
/// 입력된 숫자를 단위를 포함해서 한글로 읽는 한글 표기법으로 바꿔줍니다.
///
/// ```rust
/// use tossicat::change_num_to_hangeul;
/// assert_eq!("천사", change_num_to_hangeul("1004"));
/// assert_eq!("만이천삼백사십오", change_num_to_hangeul("12345"));
/// assert_eq!("십억", change_num_to_hangeul("1000000000"));
/// ```

pub fn change_num_to_hangeul(word: &str) -> String {
    number::change_num_to_hangeul(word)
}

/// ## 입력된 토시를 같이 입력된 단어에 맞게 변환해, 입력된 단어와 합쳐 반환하는 함수
///
/// 아래와 같은 형식으로 입력된 것 중 두 번째로 입력된 토시를
/// 첫 번째로 입력된 단어에 맞쳐 적절하게 변형해 입력된 단어와 합쳐서
/// 반환하는 함수
///
/// ```rust
/// use tossicat::postfix;
/// postfix("집", "으로");
/// postfix("집", "로");
/// postfix("집", "(으)로");
/// ```
pub fn postfix(word: &str, tossi: &str) -> Result<String, ValueError> {
    match verifier::verify_value(word, tossi) {
        Err(e) => Err(ValueError::new(e)),
        Ok(()) => {
            let temp = Tossi::new(tossi);
            match temp.kind {
                TossiKind::Others => Ok(word.to_string() + tossi),
                // 아래에서 더하는 것이랑 안 더하는 것 차이는 만약 입력된 단어가 변하는
                // 경우에는 단어 자체로 변해서 반환하기 때문에 또 단어를 더하면 중복이
                // 발생하게 됩니다. 그래서 더하면 안 됩니다.
                TossiKind::Ka => Ok(transfer::tossi(word, temp)),
                TossiKind::Indeul => Ok(transfer::tossi(word, temp)),
                TossiKind::Injeuk => Ok(transfer::tossi(word, temp)),
                TossiKind::Illang => Ok(transfer::tossi(word, temp)),
                _ => Ok(word.to_string() + &transfer::tossi(word, temp)),
            }
        }
    }
}