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 let Some(c) = chars.next() {
154 if chars.next() == None {
155 return Some(c);
156 }
157 }
158
159 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 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}