From b70d541e60f6e81c2d4d69e90b3c815ab5e55701 Mon Sep 17 00:00:00 2001 From: Han Date: Wed, 19 Nov 2025 23:35:52 +0900 Subject: [PATCH 1/4] Refactor error handling to use BraillifyError enum across multiple modules --- libs/braillify/src/char_shortcut.rs | 6 +- libs/braillify/src/char_struct.rs | 10 +- libs/braillify/src/english.rs | 6 +- libs/braillify/src/error.rs | 110 +++++++++++++++++++++ libs/braillify/src/fraction.rs | 14 ++- libs/braillify/src/jauem/choseong.rs | 6 +- libs/braillify/src/jauem/jongseong.rs | 6 +- libs/braillify/src/korean_char.rs | 3 +- libs/braillify/src/korean_part.rs | 6 +- libs/braillify/src/lib.rs | 42 +++++--- libs/braillify/src/math_symbol_shortcut.rs | 6 +- libs/braillify/src/moeum/jungsong.rs | 6 +- libs/braillify/src/number.rs | 6 +- libs/braillify/src/rule.rs | 8 +- libs/braillify/src/split.rs | 10 +- libs/braillify/src/symbol_shortcut.rs | 6 +- 16 files changed, 194 insertions(+), 57 deletions(-) create mode 100644 libs/braillify/src/error.rs diff --git a/libs/braillify/src/char_shortcut.rs b/libs/braillify/src/char_shortcut.rs index 93fae17..6f42b41 100644 --- a/libs/braillify/src/char_shortcut.rs +++ b/libs/braillify/src/char_shortcut.rs @@ -1,6 +1,6 @@ use phf::phf_map; -use crate::unicode::decode_unicode; +use crate::{error::BraillifyError, unicode::decode_unicode}; pub static SHORTCUT_MAP: phf::Map = phf_map! { '가' => &[decode_unicode('⠫')], @@ -34,11 +34,11 @@ pub static SHORTCUT_MAP: phf::Map = phf_map! { '청' => &[decode_unicode('⠰'), decode_unicode('⠻')], }; -pub fn encode_char_shortcut(text: char) -> Result<&'static [u8], String> { +pub fn encode_char_shortcut(text: char) -> Result<&'static [u8], BraillifyError> { if let Some(code) = SHORTCUT_MAP.get(&text) { Ok(code) } else { - Err("Invalid Korean char shortcut".to_string()) + Err(BraillifyError::InvalidShortcutCharacter { character: text, position: None }) } } diff --git a/libs/braillify/src/char_struct.rs b/libs/braillify/src/char_struct.rs index 564f3d3..ca9ec58 100644 --- a/libs/braillify/src/char_struct.rs +++ b/libs/braillify/src/char_struct.rs @@ -1,4 +1,4 @@ -use crate::{math_symbol_shortcut::is_math_symbol_char, symbol_shortcut::is_symbol_char, fraction::is_unicode_fraction}; +use crate::{error::BraillifyError, math_symbol_shortcut::is_math_symbol_char, symbol_shortcut::is_symbol_char, fraction::is_unicode_fraction}; /// Character in Korean #[derive(Debug)] @@ -12,10 +12,10 @@ pub struct KoreanChar { } impl KoreanChar { - pub fn new(c: char) -> Result { + pub fn new(c: char) -> Result { let code = c as u32; if !(0xAC00..=0xD7A3).contains(&code) { - return Err("Invalid Korean character".to_string()); + return Err(BraillifyError::InvalidKoreanCharacter { character: c, position: None }); } const CHOSEONG: [char; 19] = [ @@ -63,7 +63,7 @@ pub enum CharType { } impl CharType { - pub fn new(c: char) -> Result { + pub fn new(c: char) -> Result { if c.is_ascii_alphabetic() { return Ok(Self::English(c)); } @@ -92,7 +92,7 @@ impl CharType { if c.is_whitespace() { return Ok(Self::Space(c)); } - Err("Invalid character".to_string()) + Err(BraillifyError::InvalidCharacter { character: c, position: None, context: "Unknown character type".to_string() }) } } diff --git a/libs/braillify/src/english.rs b/libs/braillify/src/english.rs index f3726f2..e28ceef 100644 --- a/libs/braillify/src/english.rs +++ b/libs/braillify/src/english.rs @@ -1,6 +1,6 @@ use phf::phf_map; -use crate::unicode::decode_unicode; +use crate::{error::BraillifyError, unicode::decode_unicode}; pub static ENGLISH_MAP: phf::Map = phf_map! { 'a' => decode_unicode('⠁'), @@ -31,9 +31,9 @@ pub static ENGLISH_MAP: phf::Map = phf_map! { 'z' => decode_unicode('⠵'), }; /// 제28항 로마자는 「통일영어점자 규정」에 따라 다음과 같이 적는다. -pub fn encode_english(text: char) -> Result { +pub fn encode_english(text: char) -> Result { if let Some(code) = ENGLISH_MAP.get(&text.to_ascii_lowercase()) { return Ok(*code); } - Err("Invalid English character".to_string()) + Err(BraillifyError::InvalidEnglishCharacter { character: text, position: None }) } diff --git a/libs/braillify/src/error.rs b/libs/braillify/src/error.rs new file mode 100644 index 0000000..deee9a0 --- /dev/null +++ b/libs/braillify/src/error.rs @@ -0,0 +1,110 @@ +use std::fmt; + +#[derive(Debug, Clone)] +pub enum BraillifyError { + InvalidCharacter { character: char, position: Option, context: String }, + InvalidKoreanCharacter { character: char, position: Option }, + InvalidKoreanChoseong { character: char, position: Option }, + InvalidKoreanJungseong { character: char, position: Option }, + InvalidKoreanJongseong { character: char, position: Option }, + InvalidKoreanPart { character: char, position: Option }, + InvalidEnglishCharacter { character: char, position: Option }, + InvalidNumberCharacter { character: char, position: Option }, + InvalidSymbolCharacter { character: char, position: Option }, + InvalidMathSymbolCharacter { character: char, position: Option }, + InvalidShortcutCharacter { character: char, position: Option }, + InvalidFractionPart { part_name: String, character: char, position: Option }, + InputTooLong { length: usize, max_length: usize }, + FractionParseError { input: String, error: String }, + Other { message: String, context: String }, +} + +impl fmt::Display for BraillifyError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + BraillifyError::InvalidCharacter { character, position, context } => { + match position { + Some(pos) => write!(f, "Invalid character '{}' at position {}, context: {}", character, pos, context), + None => write!(f, "Invalid character '{}', context: {}", character, context), + } + }, + BraillifyError::InvalidKoreanCharacter { character, position } => { + match position { + Some(pos) => write!(f, "Invalid Korean character '{}' at position {}", character, pos), + None => write!(f, "Invalid Korean character '{}'", character), + } + }, + BraillifyError::InvalidKoreanChoseong { character, position } => { + match position { + Some(pos) => write!(f, "Invalid Korean choseong character '{}' at position {}", character, pos), + None => write!(f, "Invalid Korean choseong character '{}'", character), + } + }, + BraillifyError::InvalidKoreanJungseong { character, position } => { + match position { + Some(pos) => write!(f, "Invalid Korean jungseong character '{}' at position {}", character, pos), + None => write!(f, "Invalid Korean jungseong character '{}'", character), + } + }, + BraillifyError::InvalidKoreanJongseong { character, position } => { + match position { + Some(pos) => write!(f, "Invalid Korean jongseong character '{}' at position {}", character, pos), + None => write!(f, "Invalid Korean jongseong character '{}'", character), + } + }, + BraillifyError::InvalidKoreanPart { character, position } => { + match position { + Some(pos) => write!(f, "Invalid Korean part character '{}' at position {}", character, pos), + None => write!(f, "Invalid Korean part character '{}'", character), + } + }, + BraillifyError::InvalidEnglishCharacter { character, position } => { + match position { + Some(pos) => write!(f, "Invalid English character '{}' at position {}", character, pos), + None => write!(f, "Invalid English character '{}'", character), + } + }, + BraillifyError::InvalidNumberCharacter { character, position } => { + match position { + Some(pos) => write!(f, "Invalid number character '{}' at position {}", character, pos), + None => write!(f, "Invalid number character '{}'", character), + } + }, + BraillifyError::InvalidSymbolCharacter { character, position } => { + match position { + Some(pos) => write!(f, "Invalid symbol character '{}' at position {}", character, pos), + None => write!(f, "Invalid symbol character '{}'", character), + } + }, + BraillifyError::InvalidMathSymbolCharacter { character, position } => { + match position { + Some(pos) => write!(f, "Invalid math symbol character '{}' at position {}", character, pos), + None => write!(f, "Invalid math symbol character '{}'", character), + } + }, + BraillifyError::InvalidShortcutCharacter { character, position } => { + match position { + Some(pos) => write!(f, "Invalid shortcut character '{}' at position {}", character, pos), + None => write!(f, "Invalid shortcut character '{}'", character), + } + }, + BraillifyError::InvalidFractionPart { part_name, character, position } => { + match position { + Some(pos) => write!(f, "Invalid {} part (non-ascii digit): '{}' at position {}", part_name, character, pos), + None => write!(f, "Invalid {} part (non-ascii digit): '{}'", part_name, character), + } + }, + BraillifyError::InputTooLong { length, max_length } => { + write!(f, "Input text length ({}) exceeds maximum allowed length ({})", length, max_length) + }, + BraillifyError::FractionParseError { input, error } => { + write!(f, "Fraction parse error for '{}': {}", input, error) + }, + BraillifyError::Other { message, context } => { + write!(f, "Error: {} (context: {})", message, context) + }, + } + } +} + +impl std::error::Error for BraillifyError {} \ No newline at end of file diff --git a/libs/braillify/src/fraction.rs b/libs/braillify/src/fraction.rs index b642e53..e2853b3 100644 --- a/libs/braillify/src/fraction.rs +++ b/libs/braillify/src/fraction.rs @@ -1,5 +1,7 @@ use unicode_normalization::UnicodeNormalization; +use crate::error::BraillifyError; + const FRACTION_SLASH: char = '\u{2044}'; fn consume_whitespace(iter: &mut std::iter::Peekable) { @@ -12,13 +14,17 @@ fn consume_whitespace(iter: &mut std::iter::Peekable) { } } -fn encode_number_string(s: &str, part_name: &str) -> Result, String> { +fn encode_number_string(s: &str, part_name: &str) -> Result, BraillifyError> { let mut result = Vec::new(); - for c in s.chars() { + for (i, c) in s.chars().enumerate() { if !c.is_ascii_digit() { - return Err(format!("Invalid {} part (non-ascii digit): {}", part_name, c)); + return Err(BraillifyError::InvalidFractionPart { + part_name: part_name.to_string(), + character: c, + position: Some(i) + }); } - result.extend(crate::number::encode_number(c)); + result.extend(crate::number::encode_number(c)?); } Ok(result) } diff --git a/libs/braillify/src/jauem/choseong.rs b/libs/braillify/src/jauem/choseong.rs index 53fe414..8d330de 100644 --- a/libs/braillify/src/jauem/choseong.rs +++ b/libs/braillify/src/jauem/choseong.rs @@ -1,4 +1,4 @@ -use crate::unicode::decode_unicode; +use crate::{error::BraillifyError, unicode::decode_unicode}; // choseong map use phf::phf_map; @@ -19,11 +19,11 @@ pub static CHOSEONG_MAP: phf::Map = phf_map! { 'ㅎ' => decode_unicode('⠚'), }; -pub fn encode_choseong(text: char) -> Result { +pub fn encode_choseong(text: char) -> Result { if let Some(code) = CHOSEONG_MAP.get(&text) { Ok(*code) } else { - Err("Invalid Korean choseong character".to_string()) + Err(BraillifyError::InvalidKoreanChoseong { character: text, position: None }) } } diff --git a/libs/braillify/src/jauem/jongseong.rs b/libs/braillify/src/jauem/jongseong.rs index 559e1f7..03e8b1f 100644 --- a/libs/braillify/src/jauem/jongseong.rs +++ b/libs/braillify/src/jauem/jongseong.rs @@ -1,4 +1,4 @@ -use crate::unicode::decode_unicode; +use crate::{error::BraillifyError, unicode::decode_unicode}; use phf::phf_map; pub static JONGSEONG_MAP: phf::Map = phf_map! { @@ -34,11 +34,11 @@ pub static JONGSEONG_MAP: phf::Map = phf_map! { 'ㅎ' => &[decode_unicode('⠴')], }; -pub fn encode_jongseong(text: char) -> Result<&'static [u8], String> { +pub fn encode_jongseong(text: char) -> Result<&'static [u8], BraillifyError> { if let Some(code) = JONGSEONG_MAP.get(&text) { return Ok(code); } - Err("Invalid Korean jongseong character".to_string()) + Err(BraillifyError::InvalidKoreanJongseong { character: text, position: None }) } // pub fn decode_jongseong(code: u8) -> char { diff --git a/libs/braillify/src/korean_char.rs b/libs/braillify/src/korean_char.rs index 099e77a..48d1b5f 100644 --- a/libs/braillify/src/korean_char.rs +++ b/libs/braillify/src/korean_char.rs @@ -1,13 +1,14 @@ use crate::{ char_shortcut, char_struct::KoreanChar, + error::BraillifyError, jauem::{choseong::encode_choseong, jongseong::encode_jongseong}, moeum::jungsong::encode_jungsong, split::split_korean_jauem, utils::build_char, }; -pub fn encode_korean_char(korean: &KoreanChar) -> Result, String> { +pub fn encode_korean_char(korean: &KoreanChar) -> Result, BraillifyError> { let mut result = Vec::new(); let (cho0, cho1) = split_korean_jauem(korean.cho)?; if cho1.is_some() { diff --git a/libs/braillify/src/korean_part.rs b/libs/braillify/src/korean_part.rs index b785037..30a6d02 100644 --- a/libs/braillify/src/korean_part.rs +++ b/libs/braillify/src/korean_part.rs @@ -1,6 +1,6 @@ use phf::phf_map; -use crate::{moeum::jungsong::JUNGSEONG_MAP, unicode::decode_unicode}; +use crate::{error::BraillifyError, moeum::jungsong::JUNGSEONG_MAP, unicode::decode_unicode}; pub static KOREAN_PART_MAP: phf::Map = phf_map! { 'ㄱ' => &[decode_unicode('⠁')], @@ -36,12 +36,12 @@ pub static KOREAN_PART_MAP: phf::Map = phf_map! { }; /// 제8항 자음자나 모음자가 단독으로 쓰일 때에는 해당 글자 앞에 온표 =을 적어 나타내며, 자음자는 받침으로 적는다 -pub fn encode_korean_part(text: char) -> Result<&'static [u8], String> { +pub fn encode_korean_part(text: char) -> Result<&'static [u8], BraillifyError> { if let Some(code) = KOREAN_PART_MAP.get(&text) { return Ok(code); } if let Some(code) = JUNGSEONG_MAP.get(&text) { return Ok(code); } - Err("Invalid Korean part character".to_string()) + Err(BraillifyError::InvalidKoreanPart { character: text, position: None }) } diff --git a/libs/braillify/src/lib.rs b/libs/braillify/src/lib.rs index 6f702e0..2beba2a 100644 --- a/libs/braillify/src/lib.rs +++ b/libs/braillify/src/lib.rs @@ -6,6 +6,7 @@ use regex::Regex; use crate::{ char_struct::CharType, + error::BraillifyError, jauem::jongseong::encode_jongseong, korean_char::encode_korean_char, rule::{rule_11, rule_12}, @@ -20,6 +21,7 @@ static FRACTION_REGEX: Lazy = Lazy::new(|| { mod char_shortcut; mod char_struct; +mod error; #[cfg(feature = "cli")] pub mod cli; mod english; @@ -75,7 +77,7 @@ impl Encoder { self.needs_english_continuation = false; } - pub fn encode(&mut self, text: &str, result: &mut Vec) -> Result<(), String> { + pub fn encode(&mut self, text: &str, result: &mut Vec) -> Result<(), BraillifyError> { let words = text .split(' ') .filter(|word| !word.is_empty()) @@ -101,7 +103,7 @@ impl Encoder { remaining_words: &[&str], skip_count: &mut usize, result: &mut Vec, - ) -> Result<(), String> { + ) -> Result<(), BraillifyError> { if word.starts_with('$') && word.ends_with('$') { if let Some((whole, num, den)) = fraction::parse_latex_fraction(word) { if let Some(w) = whole { @@ -620,7 +622,7 @@ impl Encoder { Ok(()) } - pub fn finish(&mut self, result: &mut Vec) -> Result<(), String> { + pub fn finish(&mut self, result: &mut Vec) -> Result<(), BraillifyError> { // Handle any end-of-stream processing if self.triple_big_english { // Close triple big english if still active @@ -631,21 +633,37 @@ impl Encoder { } } -pub fn encode(text: &str) -> Result, String> { - // 한국어가 존재할 경우 english_indicator 가 true 가 됩니다. - let english_indicator = text - .split(' ') - .filter(|word| !word.is_empty()) - .any(|word| word.chars().any(utils::is_korean_char)); +pub fn encode(text: &str) -> Result, BraillifyError> { + let config = EncodingConfig::default(); + encode_with_config(text, config) +} + +pub fn encode_with_config(text: &str, config: EncodingConfig) -> Result, BraillifyError> { + // Apply the configuration's english_indicator setting based on the text content if needed + let mut final_config = config; + if final_config.english_indicator { + final_config.english_indicator = text + .split(' ') + .filter(|word| !word.is_empty()) + .any(|word| word.chars().any(utils::is_korean_char)); + } + + // Check max input length if limit is set + if final_config.max_input_length > 0 && text.len() > final_config.max_input_length { + return Err(BraillifyError::InputTooLong { + length: text.len(), + max_length: final_config.max_input_length + }); + } - let mut encoder = Encoder::new(english_indicator); + let mut encoder = Encoder::new(final_config); let mut result = Vec::new(); encoder.encode(text, &mut result)?; encoder.finish(&mut result)?; Ok(result) } -pub fn encode_to_unicode(text: &str) -> Result { +pub fn encode_to_unicode(text: &str) -> Result { let result = encode(text)?; Ok(result .iter() @@ -653,7 +671,7 @@ pub fn encode_to_unicode(text: &str) -> Result { .collect::()) } -pub fn encode_to_braille_font(text: &str) -> Result { +pub fn encode_to_braille_font(text: &str) -> Result { let result = encode(text)?; Ok(result .iter() diff --git a/libs/braillify/src/math_symbol_shortcut.rs b/libs/braillify/src/math_symbol_shortcut.rs index 2f28ed7..e7dba69 100644 --- a/libs/braillify/src/math_symbol_shortcut.rs +++ b/libs/braillify/src/math_symbol_shortcut.rs @@ -1,6 +1,6 @@ use phf::phf_map; -use crate::unicode::decode_unicode; +use crate::{error::BraillifyError, unicode::decode_unicode}; static SHORTCUT_MAP: phf::Map = phf_map! { '+' => &[decode_unicode('⠢')], @@ -12,11 +12,11 @@ static SHORTCUT_MAP: phf::Map = phf_map! { '<' => &[decode_unicode('⠔'),decode_unicode('⠔')], }; -pub fn encode_char_math_symbol_shortcut(text: char) -> Result<&'static [u8], String> { +pub fn encode_char_math_symbol_shortcut(text: char) -> Result<&'static [u8], BraillifyError> { if let Some(code) = SHORTCUT_MAP.get(&text) { Ok(code) } else { - Err("Invalid math symbol character".to_string()) + Err(BraillifyError::InvalidMathSymbolCharacter { character: text, position: None }) } } diff --git a/libs/braillify/src/moeum/jungsong.rs b/libs/braillify/src/moeum/jungsong.rs index 170f97f..f64d573 100644 --- a/libs/braillify/src/moeum/jungsong.rs +++ b/libs/braillify/src/moeum/jungsong.rs @@ -1,4 +1,4 @@ -use crate::unicode::decode_unicode; +use crate::{error::BraillifyError, unicode::decode_unicode}; use phf::phf_map; @@ -25,11 +25,11 @@ pub static JUNGSEONG_MAP: phf::Map = phf_map! { 'ㅙ' => &[decode_unicode('⠧'), decode_unicode('⠗')], 'ㅞ' => &[decode_unicode('⠏'), decode_unicode('⠗')], }; -pub fn encode_jungsong(text: char) -> Result<&'static [u8], String> { +pub fn encode_jungsong(text: char) -> Result<&'static [u8], BraillifyError> { if let Some(code) = JUNGSEONG_MAP.get(&text) { Ok(code) } else { - Err("Invalid Korean jungseong character".to_string()) + Err(BraillifyError::InvalidKoreanJungseong { character: text, position: None }) } } diff --git a/libs/braillify/src/number.rs b/libs/braillify/src/number.rs index ceaf983..8086a38 100644 --- a/libs/braillify/src/number.rs +++ b/libs/braillify/src/number.rs @@ -1,6 +1,6 @@ use phf::phf_map; -use crate::unicode::decode_unicode; +use crate::{error::BraillifyError, unicode::decode_unicode}; // 1 2 3 4 5 6 7 8 9 0 // #a #b #c #d #e #f #g #h @@ -17,9 +17,9 @@ pub static NUMBER_MAP: phf::Map = phf_map! { '0' => decode_unicode('⠚'), }; -pub fn encode_number(text: char) -> Result { +pub fn encode_number(text: char) -> Result { if let Some(code) = NUMBER_MAP.get(&text) { return Ok(*code); } - Err("Invalid number character".to_string()) + Err(BraillifyError::InvalidNumberCharacter { character: text, position: None }) } diff --git a/libs/braillify/src/rule.rs b/libs/braillify/src/rule.rs index 123284d..4fe0045 100644 --- a/libs/braillify/src/rule.rs +++ b/libs/braillify/src/rule.rs @@ -1,7 +1,7 @@ -use crate::char_struct::{CharType, KoreanChar}; +use crate::{char_struct::{CharType, KoreanChar}, error::BraillifyError}; /// 5절 11항 - 모음자에 ‘예’가 붙어 나올 때에는 그 사이에 구분표 ⠤을 적어 나타낸다. -pub fn rule_11(current: &KoreanChar, next: char, result: &mut Vec) -> Result<(), String> { +pub fn rule_11(current: &KoreanChar, next: char, result: &mut Vec) -> Result<(), BraillifyError> { if let CharType::Korean(korean) = CharType::new(next)? && current.jong.is_none() && korean.cho == 'ㅇ' && korean.jung == 'ㅖ' { result.push(36); @@ -9,8 +9,8 @@ pub fn rule_11(current: &KoreanChar, next: char, result: &mut Vec) -> Result Ok(()) } -/// 5절 12항 - ‘ㅑ, ㅘ, ㅜ, ㅝ’에 ‘애’가 붙어 나올 때에는 두 모음자 사이에 구분표 ⠤을 적어 나타낸다. -pub fn rule_12(current: &KoreanChar, next: char, result: &mut Vec) -> Result<(), String> { +/// 5절 12항 - ‘ㅑ, ㅘ, ㅜ, ㅝ’에 ‘애’가 붽어 나올 때에는 두 모음자 사이에 구분표 ⠤을 적어 나타낸다. +pub fn rule_12(current: &KoreanChar, next: char, result: &mut Vec) -> Result<(), BraillifyError> { if let CharType::Korean(korean) = CharType::new(next)? && current.jong.is_none() && ['ㅑ', 'ㅘ', 'ㅜ', 'ㅝ'].contains(¤t.jung) diff --git a/libs/braillify/src/split.rs b/libs/braillify/src/split.rs index c9c6899..9f9108d 100644 --- a/libs/braillify/src/split.rs +++ b/libs/braillify/src/split.rs @@ -1,5 +1,7 @@ use phf::phf_map; +use crate::error::BraillifyError; + #[derive(Debug, PartialEq)] pub enum KoreanChar { Choseong(char), @@ -51,13 +53,13 @@ pub static KOREAN_JAUEM_MAP: phf::Map)> = phf_map! { }; /// 자음을 분리합니다. -pub fn split_korean_jauem(text: char) -> Result<(char, Option), String> { +pub fn split_korean_jauem(text: char) -> Result<(char, Option), BraillifyError> { if let Some((cho, jong)) = KOREAN_JAUEM_MAP.get(&text) { return Ok((*cho, *jong)); } - Err("Invalid Korean character".to_string()) + Err(BraillifyError::InvalidKoreanCharacter { character: text, position: None }) } -pub fn split_korean_char(text: char) -> Result, String> { +pub fn split_korean_char(text: char) -> Result, BraillifyError> { // check korean char let code = text as u32; if (0x3131..=0x314E).contains(&code) { @@ -67,7 +69,7 @@ pub fn split_korean_char(text: char) -> Result, String> { return Ok(vec![KoreanChar::Jungseong(text)]); } if !(0xAC00..=0xD7A3).contains(&code) { - return Err("Invalid Korean character".to_string()); + return Err(BraillifyError::InvalidKoreanCharacter { character: text, position: None }); } const CHOSEONG: [char; 19] = [ diff --git a/libs/braillify/src/symbol_shortcut.rs b/libs/braillify/src/symbol_shortcut.rs index d6dd548..9bd6c13 100644 --- a/libs/braillify/src/symbol_shortcut.rs +++ b/libs/braillify/src/symbol_shortcut.rs @@ -1,6 +1,6 @@ use phf::phf_map; -use crate::unicode::decode_unicode; +use crate::{error::BraillifyError, unicode::decode_unicode}; static SHORTCUT_MAP: phf::Map = phf_map! { '"' => &[decode_unicode('⠦')], @@ -54,11 +54,11 @@ static ENGLISH_SYMBOL_MAP: phf::Map = phf_map! { ',' => &[decode_unicode('⠂')], }; -pub fn encode_char_symbol_shortcut(text: char) -> Result<&'static [u8], String> { +pub fn encode_char_symbol_shortcut(text: char) -> Result<&'static [u8], BraillifyError> { if let Some(code) = SHORTCUT_MAP.get(&text) { Ok(code) } else { - Err("Invalid symbol character".to_string()) + Err(BraillifyError::InvalidSymbolCharacter { character: text, position: None }) } } From ce746433a3f897e673c1d1f6faf17ab3f3ca318d Mon Sep 17 00:00:00 2001 From: Han Date: Sun, 23 Nov 2025 21:37:39 +0900 Subject: [PATCH 2/4] feat: introduce `EncodingConfig` for encoding parameters and refactor error handling to use `BraillifyError` consistently. --- Cargo.lock | 2 +- libs/braillify/src/error.rs | 22 +++++++++++-- libs/braillify/src/fraction.rs | 24 +++++++------- libs/braillify/src/lib.rs | 37 +++++++++++++++------- libs/braillify/src/math_symbol_shortcut.rs | 4 +-- libs/braillify/src/split.rs | 4 +-- 6 files changed, 62 insertions(+), 31 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fb01a08..a57cfac 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -111,7 +111,7 @@ checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" [[package]] name = "braillify" -version = "1.0.3" +version = "1.0.4" dependencies = [ "anyhow", "assert_cmd", diff --git a/libs/braillify/src/error.rs b/libs/braillify/src/error.rs index deee9a0..4455be0 100644 --- a/libs/braillify/src/error.rs +++ b/libs/braillify/src/error.rs @@ -1,6 +1,6 @@ use std::fmt; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub enum BraillifyError { InvalidCharacter { character: char, position: Option, context: String }, InvalidKoreanCharacter { character: char, position: Option }, @@ -107,4 +107,22 @@ impl fmt::Display for BraillifyError { } } -impl std::error::Error for BraillifyError {} \ No newline at end of file +impl std::error::Error for BraillifyError {} + +impl From for BraillifyError { + fn from(message: String) -> Self { + BraillifyError::Other { + message, + context: String::new(), + } + } +} + +impl From<&str> for BraillifyError { + fn from(message: &str) -> Self { + BraillifyError::Other { + message: message.to_string(), + context: String::new(), + } + } +} \ No newline at end of file diff --git a/libs/braillify/src/fraction.rs b/libs/braillify/src/fraction.rs index e2853b3..f97eaf8 100644 --- a/libs/braillify/src/fraction.rs +++ b/libs/braillify/src/fraction.rs @@ -24,12 +24,12 @@ fn encode_number_string(s: &str, part_name: &str) -> Result, BraillifyEr position: Some(i) }); } - result.extend(crate::number::encode_number(c)?); + result.push(crate::number::encode_number(c)?); } Ok(result) } -pub fn encode_fraction(numerator: &str, denominator: &str) -> Result, String> { +pub fn encode_fraction(numerator: &str, denominator: &str) -> Result, BraillifyError> { let mut result = vec![60]; result.extend(encode_number_string(denominator, "fraction denominator")?); result.push(12); @@ -38,7 +38,7 @@ pub fn encode_fraction(numerator: &str, denominator: &str) -> Result, St Ok(result) } -pub fn encode_fraction_in_context(numerator: &str, denominator: &str) -> Result, String> { +pub fn encode_fraction_in_context(numerator: &str, denominator: &str) -> Result, BraillifyError> { let mut result = vec![60]; result.extend(encode_number_string(numerator, "fraction numerator")?); result.push(56); @@ -48,7 +48,7 @@ pub fn encode_fraction_in_context(numerator: &str, denominator: &str) -> Result< Ok(result) } -pub fn encode_mixed_fraction(whole: &str, numerator: &str, denominator: &str) -> Result, String> { +pub fn encode_mixed_fraction(whole: &str, numerator: &str, denominator: &str) -> Result, BraillifyError> { let mut result = vec![60]; result.extend(encode_number_string(whole, "whole number")?); result.extend(encode_fraction(numerator, denominator)?); @@ -190,7 +190,7 @@ mod tests { fn test_encode_number_string_invalid_non_digit() { let result = encode_number_string("a", "test"); assert!(result.is_err()); - assert!(result.unwrap_err().contains("Invalid test part")); + assert!(result.unwrap_err().to_string().contains("Invalid test part")); } #[test] @@ -221,14 +221,14 @@ mod tests { fn test_encode_fraction_invalid_numerator() { let result = encode_fraction("a", "4"); assert!(result.is_err()); - assert!(result.unwrap_err().contains("numerator")); + assert!(result.unwrap_err().to_string().contains("numerator")); } #[test] fn test_encode_fraction_invalid_denominator() { let result = encode_fraction("3", "b"); assert!(result.is_err()); - assert!(result.unwrap_err().contains("denominator")); + assert!(result.unwrap_err().to_string().contains("denominator")); } #[test] @@ -241,14 +241,14 @@ mod tests { fn test_encode_fraction_in_context_invalid_numerator() { let result = encode_fraction_in_context("x", "3"); assert!(result.is_err()); - assert!(result.unwrap_err().contains("numerator")); + assert!(result.unwrap_err().to_string().contains("numerator")); } #[test] fn test_encode_fraction_in_context_invalid_denominator() { let result = encode_fraction_in_context("2", "y"); assert!(result.is_err()); - assert!(result.unwrap_err().contains("denominator")); + assert!(result.unwrap_err().to_string().contains("denominator")); } #[test] @@ -261,21 +261,21 @@ mod tests { fn test_encode_mixed_fraction_invalid_whole() { let result = encode_mixed_fraction("a", "1", "6"); assert!(result.is_err()); - assert!(result.unwrap_err().contains("whole")); + assert!(result.unwrap_err().to_string().contains("whole")); } #[test] fn test_encode_mixed_fraction_invalid_numerator() { let result = encode_mixed_fraction("3", "b", "6"); assert!(result.is_err()); - assert!(result.unwrap_err().contains("numerator")); + assert!(result.unwrap_err().to_string().contains("numerator")); } #[test] fn test_encode_mixed_fraction_invalid_denominator() { let result = encode_mixed_fraction("3", "1", "c"); assert!(result.is_err()); - assert!(result.unwrap_err().contains("denominator")); + assert!(result.unwrap_err().to_string().contains("denominator")); } #[test] diff --git a/libs/braillify/src/lib.rs b/libs/braillify/src/lib.rs index 2beba2a..01600b7 100644 --- a/libs/braillify/src/lib.rs +++ b/libs/braillify/src/lib.rs @@ -41,6 +41,21 @@ mod utils; mod word_shortcut; mod fraction; +#[derive(Debug, Clone)] +pub struct EncodingConfig { + pub english_indicator: bool, + pub max_input_length: usize, +} + +impl Default for EncodingConfig { + fn default() -> Self { + Self { + english_indicator: true, + max_input_length: 0, + } + } +} + pub struct Encoder { is_english: bool, triple_big_english: bool, @@ -639,24 +654,22 @@ pub fn encode(text: &str) -> Result, BraillifyError> { } pub fn encode_with_config(text: &str, config: EncodingConfig) -> Result, BraillifyError> { - // Apply the configuration's english_indicator setting based on the text content if needed - let mut final_config = config; - if final_config.english_indicator { - final_config.english_indicator = text + let mut final_english_indicator = config.english_indicator; + if final_english_indicator { + final_english_indicator = text .split(' ') .filter(|word| !word.is_empty()) .any(|word| word.chars().any(utils::is_korean_char)); } - - // Check max input length if limit is set - if final_config.max_input_length > 0 && text.len() > final_config.max_input_length { - return Err(BraillifyError::InputTooLong { - length: text.len(), - max_length: final_config.max_input_length + + if config.max_input_length > 0 && text.len() > config.max_input_length { + return Err(BraillifyError::InputTooLong { + length: text.len(), + max_length: config.max_input_length }); } - - let mut encoder = Encoder::new(final_config); + + let mut encoder = Encoder::new(final_english_indicator); let mut result = Vec::new(); encoder.encode(text, &mut result)?; encoder.finish(&mut result)?; diff --git a/libs/braillify/src/math_symbol_shortcut.rs b/libs/braillify/src/math_symbol_shortcut.rs index e7dba69..ab455f9 100644 --- a/libs/braillify/src/math_symbol_shortcut.rs +++ b/libs/braillify/src/math_symbol_shortcut.rs @@ -71,8 +71,8 @@ mod test { &[decode_unicode('⠔'), decode_unicode('⠔')] ); assert_eq!( - encode_char_math_symbol_shortcut('a').unwrap_err(), - "Invalid math symbol character" + encode_char_math_symbol_shortcut('a'), + Err(BraillifyError::InvalidMathSymbolCharacter { character: 'a', position: None }) ); } } diff --git a/libs/braillify/src/split.rs b/libs/braillify/src/split.rs index 9f9108d..97f3a79 100644 --- a/libs/braillify/src/split.rs +++ b/libs/braillify/src/split.rs @@ -189,11 +189,11 @@ mod tests { fn test_split_wrong() { assert_eq!( split_korean_char('a'), - Err("Invalid Korean character".to_string()) + Err(BraillifyError::InvalidKoreanCharacter { character: 'a', position: None }) ); assert_eq!( split_korean_char('1'), - Err("Invalid Korean character".to_string()) + Err(BraillifyError::InvalidKoreanCharacter { character: '1', position: None }) ); } } From b45be912685fb7ad6471131967bd142d90b41afe Mon Sep 17 00:00:00 2001 From: Han Date: Sun, 30 Nov 2025 04:37:32 +0900 Subject: [PATCH 3/4] resolve error --- packages/node/src/lib.rs | 6 +++--- packages/python/src/lib.rs | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/node/src/lib.rs b/packages/node/src/lib.rs index 24881aa..95660d8 100644 --- a/packages/node/src/lib.rs +++ b/packages/node/src/lib.rs @@ -4,15 +4,15 @@ use wasm_bindgen::prelude::*; #[wasm_bindgen(js_name = "encode")] pub fn encode(text: &str) -> Result, String> { - braillify::encode(text) + braillify::encode(text).map_err(|e| format!("{:?}", e)) } #[wasm_bindgen(js_name = "translateToUnicode")] pub fn translate_to_unicode(text: &str) -> Result { - braillify::encode_to_unicode(text) + braillify::encode_to_unicode(text).map_err(|e| format!("{:?}", e)) } #[wasm_bindgen(js_name = "translateToBrailleFont")] pub fn translate_to_braille_font(text: &str) -> Result { - braillify::encode_to_braille_font(text) + braillify::encode_to_braille_font(text).map_err(|e| format!("{:?}", e)) } diff --git a/packages/python/src/lib.rs b/packages/python/src/lib.rs index 150bace..152fd7d 100644 --- a/packages/python/src/lib.rs +++ b/packages/python/src/lib.rs @@ -6,17 +6,17 @@ use pyo3::prelude::*; #[pyfunction] fn encode(text: &str) -> PyResult> { - braillify_core::encode(text).map_err(PyErr::new::) + braillify_core::encode(text).map_err(|e| PyValueError::new_err(format!("{:?}", e))) } #[pyfunction] fn translate_to_unicode(text: &str) -> PyResult { - braillify_core::encode_to_unicode(text).map_err(PyErr::new::) + braillify_core::encode_to_unicode(text).map_err(|e| PyValueError::new_err(format!("{:?}", e))) } #[pyfunction] fn translate_to_braille_font(text: &str) -> PyResult { - braillify_core::encode_to_braille_font(text).map_err(PyErr::new::) + braillify_core::encode_to_braille_font(text).map_err(|e| PyValueError::new_err(format!("{:?}", e))) } #[pyfunction] From 51d653ba83b323d4167e5806e7c62e918ce5ff1f Mon Sep 17 00:00:00 2001 From: Hanjin Kim Date: Sun, 30 Nov 2025 04:43:13 +0900 Subject: [PATCH 4/4] Fix formatting in README.md Corrected a minor formatting issue in the README. (bolding issue) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 8be24be..7c810af 100644 --- a/README.md +++ b/README.md @@ -63,7 +63,7 @@ braillify는 **2024년 개정된 한국 점자 규정**을 기반으로 설계 braillify는 **Rust 언어**로 개발되었으며, **Node.js**, **Rust**, **Python** 환경을 모두 지원합니다. -또한 **WebAssembly(wasm)**도 지원하여, 네트워크나 외부 연결 없이 자신의 PC에서 바로 실행 가능한 구조를 가지고 있습니다. 이를 통해 플랫폼에 구애받지 않고 더 자유롭고 유연한 활용이 가능합니다. +또한 **WebAssembly(wasm)** 도 지원하여, 네트워크나 외부 연결 없이 자신의 PC에서 바로 실행 가능한 구조를 가지고 있습니다. 이를 통해 플랫폼에 구애받지 않고 더 자유롭고 유연한 활용이 가능합니다. 원하는 플랫폼이 있다면 Devfive와 함께 braillify를 확장하고 발전시켜보세요.