unicode_cli/
lib.rs

1use std::fmt;
2use unic_ucd::*;
3use regex::Regex;
4use log::*;
5
6pub struct Info {
7    name: Name,
8    category: GeneralCategory,
9    block: &'static str,
10    alphabetic: bool,
11    bidi_mirrored: bool,
12    case_ignorable: bool,
13    cased: bool,
14    lowercase: bool,
15    uppercase: bool,
16    whitespace: bool,
17    age: Age,
18}
19
20impl Info {
21    pub fn of(c: char) -> Option<Info> {
22        Some(Info {
23            name: Name::of(c)?,
24            category: GeneralCategory::of(c),
25            block: Block::of(c)?.name,
26            alphabetic: is_alphabetic(c),
27            bidi_mirrored: is_bidi_mirrored(c),
28            case_ignorable: is_case_ignorable(c),
29            cased: is_cased(c),
30            lowercase: is_lowercase(c),
31            uppercase: is_uppercase(c),
32            whitespace: is_white_space(c),
33            age: Age::of(c)?,
34        })
35    }
36}
37
38impl fmt::Display for Info {
39    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
40        write!(f, "name:           {}\n", self.name)?;
41        write!(f, "category:       {}\n", self.category)?;
42        write!(f, "block:          {}\n", self.block)?;
43        write!(f, "alphabetic:     {}\n", self.alphabetic)?;
44        write!(f, "bidi_mirrored:  {}\n", self.bidi_mirrored)?;
45        write!(f, "case_ignorable: {}\n", self.case_ignorable)?;
46        write!(f, "cased:          {}\n", self.cased)?;
47        write!(f, "lowercase:      {}\n", self.lowercase)?;
48        write!(f, "uppercase:      {}\n", self.uppercase)?;
49        write!(f, "whitespace:     {}\n", self.whitespace)?;
50        write!(f, "alphabetic:     {}\n", self.alphabetic)?;
51        write!(f, "since version:  {}\n", self.age.actual())
52    }
53}
54
55pub struct CharInfo {
56    long: usize,
57}
58
59impl CharInfo {
60    pub fn new(long: usize) -> Self {
61        CharInfo {
62            long
63        }
64    }
65
66    pub fn display(&self, c: char) {
67        match self.long {
68            9 => self.display_block(c),
69            0 => self.display_print(c),
70            _ => self.display_line(c)
71        }
72    }
73
74    fn display_block(&self, c: char) {
75        match (Name::of(c), Block::of(c)) {
76            (Some(name), Some(block)) => {
77                println!("type: unicode");
78                println!("name: {}", name);
79                println!("block: {}", block.name);
80            }
81            (None, Some(block)) => {
82                if c.is_ascii() {
83                    println!("type: ASCII");
84                    println!("name: unknown");
85                    println!("block: {}", block.name);
86                }
87            }
88            _ => {}
89        }
90    }
91
92    fn display_line(&self, c: char) {
93        let flags: [(char, fn(char) -> bool); 6] = [
94            ('a', is_alphabetic),
95            ('b', is_bidi_mirrored),
96            ('c', is_cased),
97            ('i', is_case_ignorable),
98            ('u', is_uppercase),
99            ('l', is_lowercase),
100        ];
101
102        if self.long >= 2 {
103            print!("{}", chartype(c));
104            for flag in &flags {
105                if flag.1(c) {
106                    print!("{}", flag.0);
107                } else {
108                    print!("-");
109                }
110            }
111
112            print!(" {:6X} ", c as u32);
113        }
114
115        if self.long >= 1 {
116            if let Some(block) = Block::of(c) {
117                print!("{} ", block.name);
118            } else {
119                print!("Unknown Block ");
120            }
121
122            if let Some(name) = Name::of(c) {
123                print!("{} ", name);
124            } else {
125                print!("None ");
126            }
127
128            println!("");
129        }
130    }
131
132    fn display_print(&self, c: char) {
133        print!("{}", c);
134    }
135}
136
137fn is_unicode(c: char) -> bool {
138    Block::of(c).is_some()
139}
140
141fn chartype(c: char) -> char {
142    if c.is_ascii() {
143        'a'
144    } else {
145        'u'
146    }
147}
148
149pub fn parse_scalar_value(s: &str) -> Option<char> {
150    let mut chars = s.chars();
151
152    // if it's a single char, just return that.
153    if let Some(c) = chars.next() {
154        if chars.next() == None {
155            return Some(c);
156        }
157    }
158
159    // maybe it's a hex-encoded unicode char.
160    if let Ok(raw) = u32::from_str_radix(s, 16) {
161        if let Some(c) = std::char::from_u32(raw) {
162            return Some(c);
163        }
164    }
165
166    // maybe it's a character name
167    if let Some(c) = unicode_names2::character(s) {
168        return Some(c);
169    }
170
171    None
172}
173
174pub fn search(regex: &Regex) -> Vec<char> {
175    let mut results = Vec::new();
176
177    for block in BlockIter::new() {
178        for candidate in block.range {
179            if let Some(name) = Name::of(candidate) {
180                let name = name.to_string();
181
182                if regex.is_match(&name) {
183                    results.push(candidate);
184                }
185            } else {
186                debug!("no name for {} in {:?}", candidate as u32, block);
187            }
188        }
189    }
190
191    results
192}