verba 0.5.1

A library for working with Latin words.
Documentation
use std::borrow::Cow;

use unicode_normalization::{UnicodeNormalization, IsNormalized, is_nfc, is_nfc_quick};

/// Takes a String and returns a String in NFC form. If the passed in String
/// was already in NFC form, it is returned unchanged. If it wasn't in NFC 
/// form, a newly created String in NFC form is returned. 
pub(crate) fn normalize(word: String) -> String {
    if let Cow::Owned(normalized) = normalize_if_needed(&word) {
        normalized
    } else {
        word
    }
}

/// Takes a string reference and depending on the string's Unicode normal form
/// returns either a borrowed or an owned Cow.
/// 
/// If the string is in Unicode Normalization Form Cononical Composition (NFC), 
/// then a borrowed Cow containing the reference is returned. If the string 
/// isn't in NFC, then an owned Cow containing a NFC equivielent form String is
/// returned. 
pub(crate) fn normalize_if_needed(word: &str) -> Cow<str> {
    if is_normalized(word) {
        Cow::Borrowed(word)
    } else {
        Cow::Owned(word.nfc().collect::<String>())
    }
}

/// Returns true if the string referenced is in Unicode Normalization Form 
/// Cononical Composition (NFC), otherwise false is returned. 
pub(crate) fn is_normalized(word: &str) -> bool {
    match is_nfc_quick(word.chars()) {
        IsNormalized::Yes => true,
        IsNormalized::No => false,
        IsNormalized::Maybe => is_nfc(&word),
    }
}

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn test_is_normalized() {
        let nfc = "cornū".nfc().collect::<String>();
        let nfd = "cornū".nfd().collect::<String>();

        assert!(is_normalized(&nfc), "is_normalized returned false for a string that is in NFC form.");
        assert_eq!(is_normalized(&nfd), false, "is_normalized returned true for a string that is not in NFC form.");
    }

    #[test]
    fn test_normalize_if_needed() {
        let nfc = "cornū".nfc().collect::<String>();
        let nfd = "cornū".nfd().collect::<String>();

        // Ensure normalize returns a Cow::Borrowed when passed an &str that is 
        // already in NFC form. Moreover, ensure the contents are in NFC form.
        match normalize_if_needed(&nfc) {
            Cow::Borrowed(normalized) => {
                if is_nfc(normalized) == false {
                    panic!("normalized(&nfc) returned a string that wasn't in NFC form.")
                }
            },
            Cow::Owned(_) => panic!("normalize returned a Cow::Owned for a string that is in NFC form."),
        }

        // Ensure normalize returns a Cow::Owned when passed an &str that is in
        // NFD form. Moreover, ensure the contents are in NFC form. 
        match normalize_if_needed(&nfd) {
            Cow::Owned(normalized) => {
                if is_nfc(&normalized) == false {
                    panic!("normalized(&nfd) returned a string that wasn't in NFC form.")
                }
            },
            Cow::Borrowed(_) => panic!("normalize returned a Cow::Borrowed for a string that is in NFD form."),
        }
    }

    #[test]
    fn test_normalize() {
        let nfd = "cornū".nfd().collect::<String>();
        let nfc = normalize(nfd);
        
        if is_nfc(&nfc) == false {
            panic!("String returned by normalize is not in NFC form.")
        }
    }
}