use std::{fmt::Write as _, string::String, vec::Vec};
use unicode_width::UnicodeWidthChar;
use crate::props::*;
fn table_width(cp: u32) -> usize {
let w = (props(cp) >> WIDTH_SHIFT) & 3;
if w == WIDTH_EMOJI_TEXT { 1 } else { w as usize }
}
fn class(cp: u32) -> u8 {
props(cp) & CB_MASK
}
#[test]
fn char_width_matches_unicode_width() {
let mut diffs: Vec<(u32, usize, usize)> = Vec::new();
for cp in 0..0x11_0000u32 {
let Some(c) = char::from_u32(cp) else {
continue; };
let theirs = UnicodeWidthChar::width(c).unwrap_or(0);
let ours = table_width(cp);
if ours != theirs && cp != 0x17d8 {
diffs.push((cp, ours, theirs));
}
}
let mut msg = String::new();
let mut groups = 0usize;
let mut i = 0usize;
while i < diffs.len() {
let (start, ours, theirs) = diffs[i];
let mut end = start;
while i + 1 < diffs.len() {
let (cp, o, t) = diffs[i + 1];
if cp != end + 1 && !(end == 0xd7ff && cp == 0xe000) || (o, t) != (ours, theirs) {
break;
}
end = cp;
i += 1;
}
end = end.max(diffs[i].0);
writeln!(msg, "U+{start:04X}..U+{end:04X}: ours {ours} theirs {theirs}")
.expect("writing to a String cannot fail");
groups += 1;
i += 1;
}
assert!(diffs.is_empty(), "{} diffs in {groups} ranges:\n{msg}", diffs.len());
}
#[test]
fn break_classes_and_flags() {
assert_eq!(class(0x0d), CB_CR);
assert_eq!(class(0x0a), CB_LF);
assert_eq!(class(0x09), CB_CONTROL);
assert_eq!(class(0x7f), CB_CONTROL);
assert_eq!(class(0x200d), CB_ZWJ);
assert_eq!(class(0x0300), CB_EXTEND); assert_eq!(class(0xfe0f), CB_EXTEND); assert_eq!(class(0x20e3), CB_EXTEND); assert_eq!(class(0x1f3fb), CB_EXTEND); assert_eq!(class(0xe0067), CB_EXTEND); assert_eq!(class(0x1f1e6), CB_RI); assert_eq!(class(0x0600), CB_PREPEND); assert_eq!(class(0x0903), CB_SPACING_MARK); assert_eq!(class(0x1100), CB_L);
assert_eq!(class(0x1160), CB_V);
assert_eq!(class(0x11a8), CB_T);
assert_eq!(class(0xac00), CB_LV); assert_eq!(class(0xac01), CB_LVT); assert_eq!(class(0x094d), CB_EXTEND_INCB_LINKER); assert_eq!(class(0x0915), CB_OTHER_INCB_CONSONANT); assert_eq!(class(b'a' as u32), CB_OTHER);
assert_ne!(props(0x26a0) & EPIC_BIT, 0); assert_ne!(props(0x1f600) & EPIC_BIT, 0); assert_eq!(props(b'0' as u32) & EPIC_BIT, 0);
assert_eq!(props(0x1f1e6) & EPIC_BIT, 0);
assert_ne!(props(0x0300) & INCB_EXTEND_BIT, 0);
assert_ne!(props(0x200d) & INCB_EXTEND_BIT, 0);
let wclass = |cp: u32| (props(cp) >> WIDTH_SHIFT) & 3;
assert_eq!(wclass(b'#' as u32), WIDTH_EMOJI_TEXT);
assert_eq!(wclass(b'0' as u32), WIDTH_EMOJI_TEXT);
assert_eq!(wclass(0x26a0), WIDTH_EMOJI_TEXT); assert_eq!(wclass(0x2705), 2); assert_eq!(wclass(0x1f1e6), 1); assert_eq!(wclass(0x3164), 0); assert_eq!(wclass(0x3131), 2); assert_eq!(wclass(0x00ad), 0); assert_eq!(wclass(0xfffd), 1); assert_eq!(table_width(0xd800), 1); assert_eq!(table_width(0x11_0000), 1); }