use std::sync::OnceLock;
use hyphenation::{Hyphenator, Language, Load, Standard};
use super::contraction::{ContractionMatch, ContractionRule};
use super::rule_10_4::StrongGroupsignRule;
fn dictionary() -> Option<&'static Standard> {
static DICT: OnceLock<Option<Standard>> = OnceLock::new();
DICT.get_or_init(|| Standard::from_embedded(Language::EnglishUS).ok())
.as_ref()
}
fn splits_between(word: &[char], pos: usize) -> bool {
let s: String = word.iter().collect();
if !s.is_ascii() {
return false;
}
dictionary().is_some_and(|d| d.hyphenate(&s).breaks.contains(&(pos + 1)))
}
fn is_bridging_digraph(a: char, b: char) -> bool {
matches!((a, b), ('t', 'h') | ('w', 'h') | ('s', 'h') | ('g', 'h'))
}
pub struct BridgeAwareStrongGroupsignRule;
impl ContractionRule for BridgeAwareStrongGroupsignRule {
fn try_match(&self, word: &[char], pos: usize) -> Option<ContractionMatch> {
let m = StrongGroupsignRule.try_match(word, pos)?;
if m.consumed == 2
&& is_bridging_digraph(word[pos], word[pos + 1])
&& splits_between(word, pos)
{
return None;
}
Some(m)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::unicode::decode_unicode;
fn try_at(word: &str, pos: usize) -> Option<(Vec<u8>, usize)> {
let chars: Vec<char> = word.chars().collect();
BridgeAwareStrongGroupsignRule
.try_match(&chars, pos)
.map(|m| (m.cells, m.consumed))
}
#[rstest::rstest]
#[case::sweetheart("sweetheart", 4)] #[case::lighthouse("lighthouse", 4)] #[case::mishandle("mishandle", 2)] fn bridging_digraph_suppressed(#[case] word: &str, #[case] pos: usize) {
assert_eq!(try_at(word, pos), None);
}
#[test]
fn ow_contracts_in_toward() {
assert_eq!(try_at("toward", 1), Some((vec![decode_unicode('⠪')], 2)));
}
#[test]
fn sh_contracts_in_single_morpheme() {
assert_eq!(try_at("bishop", 2), Some((vec![decode_unicode('⠩')], 2)));
}
#[rstest::rstest]
#[case::father("father", 2)]
#[case::panther("panther", 3)]
#[case::heathen("heathen", 3)]
fn digraph_contracts_in_single_morpheme(#[case] word: &str, #[case] pos: usize) {
assert_eq!(try_at(word, pos), Some((vec![decode_unicode('⠹')], 2)));
}
#[test]
fn cluster_groupsign_unaffected() {
assert_eq!(try_at("master", 2), Some((vec![decode_unicode('⠌')], 2)));
}
#[test]
fn non_ascii_word_cannot_split_as_compound_bridge() {
assert!(!splits_between(&['c', 'a', 'f', 'é'], 2));
}
}