Skip to main content

sofie_values/
slug_character.rs

1use std::ops::RangeInclusive;
2
3use unicode_normalization::char::is_combining_mark;
4
5/// Decomposed accents, which a slug drops as it spells Latin letters in ASCII.
6pub const LATIN_COMBINING_ACCENTS: RangeInclusive<char> = '\u{300}'..='\u{36f}';
7
8/// A letter, digit or nonspacing or spacing mark (L*, N*, Mn, Mc) that lowercasing leaves as it
9/// is; anything else parts a slug's words, unassigned, private-use and format characters too.
10#[must_use]
11pub fn is_slug_character(character: char) -> bool {
12    character.to_lowercase().eq([character])
13        && (character.is_alphanumeric() || is_word_mark(character))
14        && !LATIN_COMBINING_ACCENTS.contains(&character)
15        && !sofie_core::misleads_display(character)
16}
17
18/// The enclosing marks (Me) draw a circle or a sign around what comes before them.
19pub(crate) fn is_word_mark(character: char) -> bool {
20    is_combining_mark(character)
21        && !draws_nothing(character)
22        && !matches!(
23            character,
24            '\u{488}'..='\u{489}'
25                | '\u{1abe}'
26                | '\u{20dd}'..='\u{20e0}'
27                | '\u{20e2}'..='\u{20e4}'
28                | '\u{a670}'..='\u{a672}'
29        )
30}
31
32/// The default ignorable marks (variation selectors, the grapheme joiner): text holding one looks
33/// the same as text without it.
34pub(crate) fn draws_nothing(character: char) -> bool {
35    matches!(
36        character,
37        '\u{34f}'
38            | '\u{17b4}'..='\u{17b5}'
39            | '\u{180b}'..='\u{180d}'
40            | '\u{180f}'
41            | '\u{fe00}'..='\u{fe0f}'
42            | '\u{e0100}'..='\u{e01ef}'
43    )
44}
45
46/// The text with its marks that draw nothing removed, which input refused only after they were
47/// stored.
48pub(crate) fn without_marks_that_draw_nothing(text: &str) -> String {
49    text.chars()
50        .filter(|&character| !draws_nothing(character))
51        .collect()
52}