use icu::properties::maps;
use icu::properties::maps::CodePointMapDataBorrowed;
use icu::properties::EastAsianWidth;
use icu::properties::GeneralCategory;
const GENERAL_CATEGORY: CodePointMapDataBorrowed<'static, GeneralCategory> =
maps::general_category();
pub const EAST_ASIAN_WIDTH: CodePointMapDataBorrowed<'static, EastAsianWidth> =
maps::east_asian_width();
pub fn is_print(ch: char) -> bool {
if ch == ' ' {
return true;
}
match GENERAL_CATEGORY.get(ch) {
GeneralCategory::LowercaseLetter => true, GeneralCategory::ModifierLetter => true, GeneralCategory::OtherLetter => true, GeneralCategory::TitlecaseLetter => true, GeneralCategory::UppercaseLetter => true, GeneralCategory::SpacingMark => true, GeneralCategory::EnclosingMark => true, GeneralCategory::NonspacingMark => true, GeneralCategory::DecimalNumber => true, GeneralCategory::LetterNumber => true, GeneralCategory::OtherNumber => true, GeneralCategory::ConnectorPunctuation => true, GeneralCategory::DashPunctuation => true, GeneralCategory::ClosePunctuation => true, GeneralCategory::FinalPunctuation => true, GeneralCategory::InitialPunctuation => true, GeneralCategory::OtherPunctuation => true, GeneralCategory::OpenPunctuation => true, GeneralCategory::CurrencySymbol => true, GeneralCategory::ModifierSymbol => true, GeneralCategory::MathSymbol => true, GeneralCategory::OtherSymbol => true, _ => false,
}
}
pub fn char_width(ch: char) -> usize {
if !is_print(ch) {
return 0;
}
match EAST_ASIAN_WIDTH.get(ch) {
EastAsianWidth::Halfwidth => 1,
EastAsianWidth::Narrow => 1,
EastAsianWidth::Neutral => 1,
EastAsianWidth::Fullwidth => 2,
EastAsianWidth::Wide => 2,
_ => 2, }
}
pub fn text_width(text: &str) -> usize {
let mut w: usize = 0;
for ch in text.chars() {
w += char_width(ch);
}
return w;
}
#[cfg(test)]
mod test_of_unicode {
use super::*;
#[test]
fn test_is_print() {
for ch in '\0'..char::MAX {
check_is_print(ch);
}
}
fn check_is_print(ch: char) {
let b = is_print(ch);
match GENERAL_CATEGORY.get(ch) {
GeneralCategory::Control => assert_eq!(b, false), GeneralCategory::Format => assert_eq!(b, false), GeneralCategory::PrivateUse => assert_eq!(b, false), GeneralCategory::Unassigned => assert_eq!(b, false), GeneralCategory::LineSeparator => assert_eq!(b, false), GeneralCategory::ParagraphSeparator => assert_eq!(b, false), GeneralCategory::SpaceSeparator => {
if ch == ' ' {
assert_eq!(b, true);
} else {
assert_eq!(b, false);
}
}
_ => assert_eq!(b, true),
}
}
#[test]
fn test_char_width() {
let ch = 'क';
assert_eq!(EAST_ASIAN_WIDTH.get(ch), EastAsianWidth::Neutral);
assert_eq!(char_width(ch), 1);
let ch = 'α';
assert_eq!(EAST_ASIAN_WIDTH.get(ch), EastAsianWidth::Ambiguous);
assert_eq!(char_width(ch), 2);
let ch = 'ア';
assert_eq!(EAST_ASIAN_WIDTH.get(ch), EastAsianWidth::Halfwidth);
assert_eq!(char_width(ch), 1);
let ch = 'A';
assert_eq!(EAST_ASIAN_WIDTH.get(ch), EastAsianWidth::Fullwidth);
assert_eq!(char_width(ch), 2);
let ch = 'A';
assert_eq!(EAST_ASIAN_WIDTH.get(ch), EastAsianWidth::Narrow);
assert_eq!(char_width(ch), 1);
let ch = 'ア';
assert_eq!(EAST_ASIAN_WIDTH.get(ch), EastAsianWidth::Wide);
assert_eq!(char_width(ch), 2);
let ch = '\t';
assert_eq!(EAST_ASIAN_WIDTH.get(ch), EastAsianWidth::Neutral);
assert_eq!(char_width(ch), 0);
}
#[test]
fn test_text_width() {
assert_eq!(text_width("abc"), 3);
assert_eq!(text_width("あいう"), 6);
assert_eq!(text_width(""), 0);
}
}