Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ braillify는 **2024년 개정된 한국 점자 규정**을 기반으로 설계

braillify는 **Rust 언어**로 개발되었으며, **Node.js**, **Rust**, **Python** 환경을 모두 지원합니다.

또한 **WebAssembly(wasm)**도 지원하여, 네트워크나 외부 연결 없이 자신의 PC에서 바로 실행 가능한 구조를 가지고 있습니다. 이를 통해 플랫폼에 구애받지 않고 더 자유롭고 유연한 활용이 가능합니다.
또한 **WebAssembly(wasm)** 도 지원하여, 네트워크나 외부 연결 없이 자신의 PC에서 바로 실행 가능한 구조를 가지고 있습니다. 이를 통해 플랫폼에 구애받지 않고 더 자유롭고 유연한 활용이 가능합니다.

원하는 플랫폼이 있다면 Devfive와 함께 braillify를 확장하고 발전시켜보세요.

Expand Down
6 changes: 3 additions & 3 deletions libs/braillify/src/char_shortcut.rs
Original file line number Diff line number Diff line change
@@ -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<char, &'static [u8]> = phf_map! {
'가' => &[decode_unicode('⠫')],
Expand Down Expand Up @@ -34,11 +34,11 @@ pub static SHORTCUT_MAP: phf::Map<char, &'static [u8]> = 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 })
}
}

Expand Down
10 changes: 5 additions & 5 deletions libs/braillify/src/char_struct.rs
Original file line number Diff line number Diff line change
@@ -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)]
Expand All @@ -12,10 +12,10 @@ pub struct KoreanChar {
}

impl KoreanChar {
pub fn new(c: char) -> Result<Self, String> {
pub fn new(c: char) -> Result<Self, BraillifyError> {
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] = [
Expand Down Expand Up @@ -63,7 +63,7 @@ pub enum CharType {
}

impl CharType {
pub fn new(c: char) -> Result<Self, String> {
pub fn new(c: char) -> Result<Self, BraillifyError> {
if c.is_ascii_alphabetic() {
return Ok(Self::English(c));
}
Expand Down Expand Up @@ -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() })
}
}

Expand Down
6 changes: 3 additions & 3 deletions libs/braillify/src/english.rs
Original file line number Diff line number Diff line change
@@ -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<char, u8> = phf_map! {
'a' => decode_unicode('⠁'),
Expand Down Expand Up @@ -31,9 +31,9 @@ pub static ENGLISH_MAP: phf::Map<char, u8> = phf_map! {
'z' => decode_unicode('⠵'),
};
/// 제28항 로마자는 「통일영어점자 규정」에 따라 다음과 같이 적는다.
pub fn encode_english(text: char) -> Result<u8, String> {
pub fn encode_english(text: char) -> Result<u8, BraillifyError> {
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 })
}
128 changes: 128 additions & 0 deletions libs/braillify/src/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
use std::fmt;

#[derive(Debug, Clone, PartialEq)]
pub enum BraillifyError {
InvalidCharacter { character: char, position: Option<usize>, context: String },
InvalidKoreanCharacter { character: char, position: Option<usize> },
InvalidKoreanChoseong { character: char, position: Option<usize> },
InvalidKoreanJungseong { character: char, position: Option<usize> },
InvalidKoreanJongseong { character: char, position: Option<usize> },
InvalidKoreanPart { character: char, position: Option<usize> },
InvalidEnglishCharacter { character: char, position: Option<usize> },
InvalidNumberCharacter { character: char, position: Option<usize> },
InvalidSymbolCharacter { character: char, position: Option<usize> },
InvalidMathSymbolCharacter { character: char, position: Option<usize> },
InvalidShortcutCharacter { character: char, position: Option<usize> },
InvalidFractionPart { part_name: String, character: char, position: Option<usize> },
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 {}

impl From<String> 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(),
}
}
}
36 changes: 21 additions & 15 deletions libs/braillify/src/fraction.rs
Original file line number Diff line number Diff line change
@@ -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<std::str::Chars>) {
Expand All @@ -12,18 +14,22 @@ fn consume_whitespace(iter: &mut std::iter::Peekable<std::str::Chars>) {
}
}

fn encode_number_string(s: &str, part_name: &str) -> Result<Vec<u8>, String> {
fn encode_number_string(s: &str, part_name: &str) -> Result<Vec<u8>, 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.push(crate::number::encode_number(c)?);
}
Ok(result)
}

pub fn encode_fraction(numerator: &str, denominator: &str) -> Result<Vec<u8>, String> {
pub fn encode_fraction(numerator: &str, denominator: &str) -> Result<Vec<u8>, BraillifyError> {
let mut result = vec![60];
result.extend(encode_number_string(denominator, "fraction denominator")?);
result.push(12);
Expand All @@ -32,7 +38,7 @@ pub fn encode_fraction(numerator: &str, denominator: &str) -> Result<Vec<u8>, St
Ok(result)
}

pub fn encode_fraction_in_context(numerator: &str, denominator: &str) -> Result<Vec<u8>, String> {
pub fn encode_fraction_in_context(numerator: &str, denominator: &str) -> Result<Vec<u8>, BraillifyError> {
let mut result = vec![60];
result.extend(encode_number_string(numerator, "fraction numerator")?);
result.push(56);
Expand All @@ -42,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<Vec<u8>, String> {
pub fn encode_mixed_fraction(whole: &str, numerator: &str, denominator: &str) -> Result<Vec<u8>, BraillifyError> {
let mut result = vec![60];
result.extend(encode_number_string(whole, "whole number")?);
result.extend(encode_fraction(numerator, denominator)?);
Expand Down Expand Up @@ -184,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]
Expand Down Expand Up @@ -215,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]
Expand All @@ -235,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]
Expand All @@ -255,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]
Expand Down
6 changes: 3 additions & 3 deletions libs/braillify/src/jauem/choseong.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::unicode::decode_unicode;
use crate::{error::BraillifyError, unicode::decode_unicode};
// choseong map
use phf::phf_map;

Expand All @@ -19,11 +19,11 @@ pub static CHOSEONG_MAP: phf::Map<char, u8> = phf_map! {
'ㅎ' => decode_unicode('⠚'),
};

pub fn encode_choseong(text: char) -> Result<u8, String> {
pub fn encode_choseong(text: char) -> Result<u8, BraillifyError> {
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 })
}
}

Expand Down
6 changes: 3 additions & 3 deletions libs/braillify/src/jauem/jongseong.rs
Original file line number Diff line number Diff line change
@@ -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<char, &'static [u8]> = phf_map! {
Expand Down Expand Up @@ -34,11 +34,11 @@ pub static JONGSEONG_MAP: phf::Map<char, &'static [u8]> = 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 {
Expand Down
3 changes: 2 additions & 1 deletion libs/braillify/src/korean_char.rs
Original file line number Diff line number Diff line change
@@ -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<Vec<u8>, String> {
pub fn encode_korean_char(korean: &KoreanChar) -> Result<Vec<u8>, BraillifyError> {
let mut result = Vec::new();
let (cho0, cho1) = split_korean_jauem(korean.cho)?;
if cho1.is_some() {
Expand Down
Loading
Loading