use crate::char_struct::CharType;
use crate::math_symbol_shortcut;
use crate::rules::RuleMeta;
use crate::rules::context::RuleContext;
use crate::rules::traits::{BrailleRule, Phase, RuleResult};
use crate::utils;
pub static META: RuleMeta = RuleMeta {
section: "math",
subsection: None,
name: "math_symbol_encoding",
standard_ref: "2024 Korean Braille Standard (math symbols)",
description: "Math symbols with Korean spacing rules",
};
const JOSA: &[&str] = &["과", "와", "이다", "하고", "이랑", "와", "랑", "아니다"];
pub struct RuleMath;
impl BrailleRule for RuleMath {
fn meta(&self) -> &'static RuleMeta {
&META
}
fn phase(&self) -> Phase {
Phase::CoreEncoding
}
fn matches(&self, ctx: &RuleContext) -> bool {
matches!(ctx.char_type, CharType::MathSymbol(_))
}
fn apply(&self, ctx: &mut RuleContext) -> Result<RuleResult, String> {
let CharType::MathSymbol(c) = ctx.char_type else {
return Ok(RuleResult::Skip);
};
let prev_has_korean = ctx.word_chars[..ctx.index]
.iter()
.any(|c| utils::is_korean_char(*c));
let next_korean_is_non_josa = {
let mut korean = Vec::new();
for wc in &ctx.word_chars[ctx.index + 1..] {
if utils::is_korean_char(*wc) {
korean.push(*wc);
} else if !korean.is_empty() {
break;
}
}
if korean.is_empty() {
false
} else {
let s: String = korean.into_iter().collect();
!JOSA.contains(&s.as_str())
}
};
let pad_spaces = prev_has_korean && next_korean_is_non_josa;
if pad_spaces {
ctx.emit(0);
}
let encoded = math_symbol_shortcut::encode_char_math_symbol_shortcut(*c)?;
ctx.emit_slice(encoded);
if pad_spaces {
ctx.emit(0);
}
Ok(RuleResult::Consumed)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn apply_exercise() {
let mut owned = crate::test_helpers::CtxOwned::for_text("A", false);
let mut ctx = owned.ctx_at(0);
let _ = RuleMath.apply(&mut ctx);
}
#[test]
fn matches_does_not_panic() {
let mut owned = crate::test_helpers::CtxOwned::for_text("A", false);
let ctx = owned.ctx_at(0);
let _ = RuleMath.matches(&ctx);
}
}