Skip to main content

unicode_width_utils/
unicode_width_utils.rs

1use std::sync::LazyLock;
2use unicode_width::{UnicodeWidthChar, UnicodeWidthStr};
3
4static IS_CJK: LazyLock<bool> = LazyLock::new(|| match std::env::var("UNICODE_WIDTH") {
5    Ok(value) => value.eq_ignore_ascii_case("cjk"),
6    _ => false,
7});
8
9#[derive(Clone, Copy, Debug)]
10pub struct UnicodeWidth {
11    is_cjk: bool,
12}
13
14impl Default for UnicodeWidth {
15    fn default() -> Self {
16        Self { is_cjk: *IS_CJK }
17    }
18}
19
20impl UnicodeWidth {
21    pub fn new() -> Self {
22        Self::default()
23    }
24
25    pub fn with_cjk(is_cjk: bool) -> Self {
26        Self { is_cjk }
27    }
28
29    pub fn char(&self, ch: char) -> Option<usize> {
30        match self.is_cjk {
31            false => UnicodeWidthChar::width(ch),
32            true => UnicodeWidthChar::width_cjk(ch),
33        }
34    }
35
36    pub fn str(&self, str: &str) -> usize {
37        match self.is_cjk {
38            false => UnicodeWidthStr::width(str),
39            true => UnicodeWidthStr::width_cjk(str),
40        }
41    }
42}
43
44#[cfg(test)]
45mod tests {
46    use super::*;
47
48    #[test]
49    fn char() {
50        let uw = UnicodeWidth::with_cjk(false);
51        let cjk = UnicodeWidth::with_cjk(true);
52        assert_eq!(uw.char('A'), Some(1));
53        assert_eq!(cjk.char('A'), Some(1));
54        assert_eq!(uw.char('\u{2588}'), Some(1));
55        assert_eq!(cjk.char('\u{2588}'), Some(2));
56        assert_eq!(uw.char('\u{3042}'), Some(2));
57        assert_eq!(cjk.char('\u{3042}'), Some(2));
58    }
59
60    #[test]
61    fn str() {
62        let uw = UnicodeWidth::with_cjk(false);
63        let cjk = UnicodeWidth::with_cjk(true);
64        assert_eq!(uw.str("A"), 1);
65        assert_eq!(cjk.str("A"), 1);
66        assert_eq!(uw.str("\u{2588}"), 1);
67        assert_eq!(cjk.str("\u{2588}"), 2);
68        assert_eq!(uw.str("\u{3042}"), 2);
69        assert_eq!(cjk.str("\u{3042}"), 2);
70    }
71}