use crate::char_struct::CharType;
use crate::rules::RuleMeta;
use crate::rules::context::RuleContext;
use crate::rules::traits::{BrailleRule, Phase, RuleResult};
use crate::symbol_shortcut;
pub static META: RuleMeta = RuleMeta {
section: "60",
subsection: None,
name: "asterisk_spacing",
standard_ref: "2024 Korean Braille Standard, Ch.6 Sec.13 Art.60",
description: "Asterisk (*) requires surrounding spaces",
};
pub struct Rule60;
impl BrailleRule for Rule60 {
fn meta(&self) -> &'static RuleMeta {
&META
}
fn phase(&self) -> Phase {
Phase::CoreEncoding
}
fn priority(&self) -> u16 {
400 }
fn matches(&self, ctx: &RuleContext) -> bool {
matches!(ctx.char_type, CharType::Symbol(c) if *c == '*')
}
fn apply(&self, ctx: &mut RuleContext) -> Result<RuleResult, String> {
if ctx.index == 0 && ctx.word_len() == 1 && !ctx.prev_word.is_empty() {
ctx.emit(0); }
let encoded = symbol_shortcut::encode_char_symbol_shortcut('*')?;
ctx.emit_slice(encoded);
Ok(RuleResult::Consumed)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn meta_is_correct() {
assert_eq!(META.section, "60");
assert_eq!(META.name, "asterisk_spacing");
}
#[test]
fn apply_skips_non_korean() {
let mut owned = crate::test_helpers::CtxOwned::for_text("A", false);
let mut ctx = owned.ctx_at(0);
let _ = Rule60.apply(&mut ctx).unwrap();
}
#[test]
fn rule60_apply_standalone_asterisk_after_word_prepends_space() {
let mut owned = crate::test_helpers::CtxOwned::for_text("*", false).with_prev_word("가");
let mut ctx = owned.ctx_at(0);
let outcome = Rule60.apply(&mut ctx).unwrap();
assert!(matches!(outcome, RuleResult::Consumed));
assert_eq!(owned.result[0], 0);
assert!(owned.result.len() > 1);
}
}