///|
/// Unicode Normalization Forms (UAX #15)
///
/// This module implements the four Unicode normalization forms:
/// - NFD: Canonical Decomposition
/// - NFC: Canonical Decomposition followed by Canonical Composition
/// - NFKD: Compatibility Decomposition
/// - NFKC: Compatibility Decomposition followed by Canonical Composition
///|
/// Normalization form enumeration
pub(all) enum NormalizationForm {
NFD // Canonical Decomposition
NFC // Canonical Decomposition + Composition
NFKD // Compatibility Decomposition
NFKC // Compatibility Decomposition + Composition
} derive(Eq)
///|
pub extend NormalizationForm with Eq::{not_equal, equal}
///|
pub extend NormalizationForm with Show::{to_string, output}
///|
pub impl Show for NormalizationForm with fn output(self, logger) {
match self {
NFD => logger.write_string("NFD")
NFC => logger.write_string("NFC")
NFKD => logger.write_string("NFKD")
NFKC => logger.write_string("NFKC")
}
}
///|
/// Normalize a string to NFD (Canonical Decomposition)
///
/// Characters are decomposed to their canonical equivalents and
/// combining marks are reordered by Canonical Combining Class.
pub fn nfd(s : String) -> String {
normalize_spans(s, 12, 1, compatibility=false, compose=false)
}
///|
/// Normalize a string to NFC (Canonical Decomposition + Composition)
///
/// Characters are first decomposed canonically, reordered, then
/// recomposed using canonical composition rules.
/// This is the most commonly used normalization form.
pub fn nfc(s : String) -> String {
normalize_spans(s, 8, 3, compatibility=false, compose=true)
}
///|
/// Normalize a string to NFKD (Compatibility Decomposition)
///
/// Characters are decomposed to their compatibility equivalents
/// (including formatting variants like superscripts, fractions, etc.)
/// and combining marks are reordered.
pub fn nfkd(s : String) -> String {
normalize_spans(s, 13, 1, compatibility=true, compose=false)
}
///|
/// Normalize a string to NFKC (Compatibility Decomposition + Composition)
///
/// Characters are decomposed to compatibility equivalents, reordered,
/// then recomposed. Often used for identifier comparison.
pub fn nfkc(s : String) -> String {
normalize_spans(s, 10, 3, compatibility=true, compose=true)
}
///|
/// Normalize a string to the specified form
pub fn normalize(s : String, form : NormalizationForm) -> String {
match form {
NFD => nfd(s)
NFC => nfc(s)
NFKD => nfkd(s)
NFKC => nfkc(s)
}
}
///|
/// Check if a string is in the specified normalization form
pub fn is_normalized(s : String, form : NormalizationForm) -> Bool {
let result = match form {
NFC =>
process_spans(s, 8, 3, compatibility=false, compose=true, check_only=true)
NFKC =>
process_spans(s, 10, 3, compatibility=true, compose=true, check_only=true)
NFD =>
process_spans(
s,
12,
1,
compatibility=false,
compose=false,
check_only=true,
)
NFKD =>
process_spans(
s,
13,
1,
compatibility=true,
compose=false,
check_only=true,
)
}
result is Some(_)
}