use crate::blob;
use crate::ids::{Cid, CidSet};
pub(crate) fn unicode_from_cid(set: CidSet, cid: Cid) -> Option<char> {
let raw = match set {
CidSet::Unicode => u32::from(cid.0),
CidSet::Unknown => return None,
_ => u32::from(blob::cid2unicode(set.registry_index()?, cid.0)?),
};
char::from_u32(raw)
}
pub(crate) fn has_table(set: CidSet) -> bool {
set.registry_index()
.is_some_and(|reg| blob::cid2unicode_len(reg) > 0)
}
#[cfg(test)]
mod tests {
use super::{has_table, unicode_from_cid};
use crate::blob;
use crate::ids::{Cid, CidSet};
#[test]
fn cid_zero_is_the_replacement_character_everywhere() {
for set in [CidSet::Gb1, CidSet::Cns1, CidSet::Japan1, CidSet::Korea1] {
assert_eq!(unicode_from_cid(set, Cid(0)), Some('\u{FFFD}'), "{set:?}");
}
}
#[test]
fn gb1_low_cids_are_ascii() {
assert_eq!(unicode_from_cid(CidSet::Gb1, Cid(1)), Some(' '));
assert_eq!(unicode_from_cid(CidSet::Gb1, Cid(2)), Some('!'));
assert_eq!(unicode_from_cid(CidSet::Gb1, Cid(34)), Some('A'));
}
#[test]
fn unicode_collection_is_the_identity() {
assert_eq!(unicode_from_cid(CidSet::Unicode, Cid(0x41)), Some('A'));
assert_eq!(unicode_from_cid(CidSet::Unicode, Cid(0)), Some('\0'));
assert_eq!(unicode_from_cid(CidSet::Unicode, Cid(0xD800)), None);
}
#[test]
fn unknown_collection_has_nothing() {
assert_eq!(unicode_from_cid(CidSet::Unknown, Cid(1)), None);
assert!(!has_table(CidSet::Unknown));
assert!(!has_table(CidSet::Unicode));
for set in [CidSet::Gb1, CidSet::Cns1, CidSet::Japan1, CidSet::Korea1] {
assert!(has_table(set), "{set:?}");
}
}
#[test]
fn a_cid_past_the_table_has_no_mapping() {
let len = blob::cid2unicode_len(0);
let last = u16::try_from(len - 1).unwrap();
assert!(unicode_from_cid(CidSet::Gb1, Cid(last)).is_some());
assert_eq!(
unicode_from_cid(CidSet::Gb1, Cid(u16::try_from(len).unwrap())),
None
);
assert_eq!(unicode_from_cid(CidSet::Gb1, Cid(u16::MAX)), None);
}
}