use crate::char_struct::CharType;
use crate::rules::RuleMeta;
use crate::rules::context::RuleContext;
use crate::rules::traits::{BrailleRule, Phase, RuleResult};
pub static META: RuleMeta = RuleMeta {
section: "58",
subsection: None,
name: "blank_marks",
standard_ref: "2024 Korean Braille Standard, Ch.6 Sec.13 Art.58",
description: "Blank marks □: prefix ⠸ + count × ⠶ + suffix ⠇",
};
const BLANK_MARK: char = '□';
const PREFIX: u8 = 56; const MARK: u8 = 54; const SUFFIX: u8 = 7;
pub struct Rule58;
impl BrailleRule for Rule58 {
fn meta(&self) -> &'static RuleMeta {
&META
}
fn phase(&self) -> Phase {
Phase::CoreEncoding
}
fn priority(&self) -> u16 {
70 }
fn matches(&self, ctx: &RuleContext) -> bool {
if !matches!(ctx.char_type, CharType::Symbol(c) if *c == BLANK_MARK) {
return false;
}
let is_lone =
ctx.word_len() == 1 && ctx.prev_word.is_empty() && ctx.remaining_words.is_empty();
if is_lone {
return false;
}
let count = ctx.word_chars[ctx.index..]
.iter()
.take_while(|&&c| c == BLANK_MARK)
.count();
count >= 2
}
fn apply(&self, ctx: &mut RuleContext) -> Result<RuleResult, String> {
let count = ctx.word_chars[ctx.index..]
.iter()
.take_while(|&&c| c == BLANK_MARK)
.count();
ctx.emit(PREFIX);
for _ in 0..count {
ctx.emit(MARK);
}
ctx.emit(SUFFIX);
if count > 1 {
*ctx.skip_count = count - 1;
}
Ok(RuleResult::Consumed)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn multiple_blank_marks() {
let result = crate::encode_to_unicode("□□□").unwrap();
assert_eq!(result, "⠸⠶⠶⠶⠇");
}
#[test]
fn meta_is_correct() {
assert_eq!(META.section, "58");
assert_eq!(META.name, "blank_marks");
}
}