use crate::jauem::jongseong;
use crate::rules::RuleMeta;
use crate::rules::context::RuleContext;
use crate::rules::traits::{BrailleRule, Phase, RuleResult};
pub static META_3: RuleMeta = RuleMeta {
section: "3",
subsection: None,
name: "basic_jongseong",
standard_ref: "2024 Korean Braille Standard, Ch.1 Sec.2 Art.3",
description: "Encode 14 basic final consonants (jongseong) to braille",
};
#[cfg(test)]
fn apply(jong: char) -> Result<&'static [u8], String> {
jongseong::encode_jongseong(jong)
}
pub struct Rule3;
impl BrailleRule for Rule3 {
fn meta(&self) -> &'static RuleMeta {
&META_3
}
fn phase(&self) -> Phase {
Phase::CoreEncoding
}
fn priority(&self) -> u16 {
210 }
fn matches(&self, ctx: &RuleContext) -> bool {
ctx.as_korean().is_some_and(|k| k.jong.is_some())
}
fn apply(&self, ctx: &mut RuleContext) -> Result<RuleResult, String> {
let Some(korean) = ctx.as_korean() else {
return Ok(RuleResult::Skip);
};
if let Some(jong) = korean.jong {
let encoded = jongseong::encode_jongseong(jong)?;
ctx.emit_slice(encoded);
}
Ok(RuleResult::Continue)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::unicode::decode_unicode;
#[test]
fn encodes_basic_jongseong() {
let cases = vec![
('ㄱ', vec![decode_unicode('⠁')]),
('ㄴ', vec![decode_unicode('⠒')]),
('ㄷ', vec![decode_unicode('⠔')]),
('ㄹ', vec![decode_unicode('⠂')]),
('ㅁ', vec![decode_unicode('⠢')]),
('ㅂ', vec![decode_unicode('⠃')]),
('ㅅ', vec![decode_unicode('⠄')]),
('ㅇ', vec![decode_unicode('⠶')]),
('ㅈ', vec![decode_unicode('⠅')]),
('ㅊ', vec![decode_unicode('⠆')]),
('ㅋ', vec![decode_unicode('⠖')]),
('ㅌ', vec![decode_unicode('⠦')]),
('ㅍ', vec![decode_unicode('⠲')]),
('ㅎ', vec![decode_unicode('⠴')]),
];
for (jong, expected) in cases {
let result = apply(jong).unwrap();
assert_eq!(result, &expected[..], "Failed for jongseong: {}", jong);
}
}
#[test]
fn encodes_double_jongseong_gg() {
let result = apply('ㄲ').unwrap();
assert_eq!(result, &[decode_unicode('⠁'), decode_unicode('⠁')]);
}
#[test]
fn encodes_double_jongseong_ss() {
let result = apply('ㅆ').unwrap();
assert_eq!(result, &[decode_unicode('⠌')]);
}
#[test]
fn encodes_compound_jongseong() {
let cases = vec![
('ㄳ', vec![decode_unicode('⠁'), decode_unicode('⠄')]),
('ㄵ', vec![decode_unicode('⠒'), decode_unicode('⠅')]),
('ㄶ', vec![decode_unicode('⠒'), decode_unicode('⠴')]),
('ㄺ', vec![decode_unicode('⠂'), decode_unicode('⠁')]),
('ㅄ', vec![decode_unicode('⠃'), decode_unicode('⠄')]),
];
for (jong, expected) in cases {
let result = apply(jong).unwrap();
assert_eq!(
result,
&expected[..],
"Failed for compound jongseong: {}",
jong
);
}
}
#[test]
fn invalid_returns_error() {
assert!(apply('A').is_err());
assert!(apply('가').is_err());
}
#[test]
fn golden_test_alignment() {
let cases = vec![("국보", "⠈⠍⠁⠘⠥"), ("놋그릇", "⠉⠥⠄⠈⠪⠐⠪⠄")];
for (input, expected) in cases {
let result = crate::encode_to_unicode(input).unwrap();
assert_eq!(result, expected, "Rule 3 golden test failed for: {}", input);
}
}
}