1use std::fmt::{self, Display, Formatter};
25
26use errors::{ParseError, Result};
27use errors::ErrorType::Parse;
28use position::Pos;
29
30use self::Key::*;
31
32struct ConstructorKeys {
34 alt: bool,
35 control: bool,
36 shift: bool,
37}
38
39impl ConstructorKeys {
40 fn new() -> Self {
41 ConstructorKeys {
42 alt: false,
43 control: false,
44 shift: false,
45 }
46 }
47}
48
49#[derive(Clone, Debug, Eq, Hash, PartialEq)]
51pub enum Key {
52 Alt(Box<Key>),
53 Backspace,
54 Char(char),
55 Control(Box<Key>),
56 Delete,
57 Down,
58 End,
59 Enter,
60 Escape,
61 F1,
62 F2,
63 F3,
64 F4,
65 F5,
66 F6,
67 F7,
68 F8,
69 F9,
70 F10,
71 F11,
72 F12,
73 Home,
74 Insert,
75 Left,
76 PageDown,
77 PageUp,
78 Right,
79 Shift(Box<Key>),
80 Space,
81 Tab,
82 Up,
83}
84
85fn key_to_string(key: &Key) -> String {
88 let string =
89 match *key {
90 Alt(ref key) => return format!("A-{}", key_to_string(&*key)),
91 Backspace => "Backspace",
92 Char(character) => return character.to_string(),
93 Control(ref key) => return format!("C-{}", key_to_string(&*key)),
94 Delete => "Delete",
95 Down => "Down",
96 End => "End",
97 Enter => "Enter",
98 Escape => "Esc",
99 F1 => "F1",
100 F2 => "F2",
101 F3 => "F3",
102 F4 => "F4",
103 F5 => "F5",
104 F6 => "F6",
105 F7 => "F7",
106 F8 => "F8",
107 F9 => "F9",
108 F10 => "F10",
109 F11 => "F11",
110 F12 => "F12",
111 Home => "Home",
112 Insert => "Insert",
113 Left => "Left",
114 PageDown => "PageDown",
115 PageUp => "PageUp",
116 Right => "Right",
117 Shift(ref key) => return format!("S-{}", key_to_string(&*key)),
118 Space => "Space",
119 Tab => "Tab",
120 Up => "Up",
121 };
122 string.to_string()
123}
124
125impl Display for Key {
126 fn fmt(&self, formatter: &mut Formatter) -> fmt::Result {
127 let string = key_to_string(self);
128 let string =
129 if let Char(_) = *self {
130 string
131 }
132 else {
133 format!("<{}>", string)
134 };
135 write!(formatter, "{}", string)
136 }
137}
138
139fn key_constructor(key: Key, constructor_keys: &ConstructorKeys) -> Key {
140 let mut ctrl_constructor: fn(Key) -> Key = |key| key;
141 if constructor_keys.control {
142 ctrl_constructor = |key| Control(Box::new(key));
143 }
144
145 let mut shift_constructor: fn(Key) -> Key = |key| key;
146 if constructor_keys.shift {
147 shift_constructor = |key| Shift(Box::new(key));
148 }
149
150 let mut alt_constructor: fn(Key) -> Key = |key| key;
151 if constructor_keys.alt {
152 alt_constructor = |key| Alt(Box::new(key));
153 }
154
155 ctrl_constructor(alt_constructor(shift_constructor(key)))
156}
157
158fn parse_key(input: &str, line_num: usize, column_num: usize) -> Result<(Key, usize)> {
159 let mut chars = input.chars();
160 let result =
161 match chars.next() {
162 Some('<') => {
163 let key: String = chars.take_while(|&character| character != '>').collect();
164 if !input.contains('>') {
165 return Err(ParseError::new(
166 Parse,
167 "(none)".to_string(),
168 ">".to_string(),
169 Pos::new(line_num, column_num + input.len())
170 ));
171
172 }
173 let mut end = key.clone();
174 if end.len() >= 2 && (&end[..2] == "A-" || &end[..2] == "C-" || &end[..2] == "S-") {
175 let mut delta = 0;
176 let mut constructor_keys = ConstructorKeys::new();
177 while end.len() >= 2 && (&end[..2] == "A-" || &end[..2] == "C-" || &end[..2] == "S-") {
178 let new_end = {
179 let (start, new_end) = end.split_at(2);
180 match start {
181 "A-" => constructor_keys.alt = true,
182 "C-" => constructor_keys.control = true,
183 "S-" => constructor_keys.shift = true,
184 _ => unreachable!(),
185 }
186 delta += 2;
187 new_end.to_string()
188 };
189 end = new_end;
190 }
191
192 let character = end.chars().next().unwrap(); let result_special_key = special_key(&end, line_num, column_num + delta, true)
194 .map(|(key, size)| (key_constructor(key, &constructor_keys), size + delta));
195 match result_special_key {
196 Ok(result) => result,
197 Err(error) => {
198 match character {
199 'A' ... 'Z' | 'a' ... 'z' => {
200 if end.len() == 1 {
201 (key_constructor(Char(character), &constructor_keys), 5)
202 }
203 else {
204 return Err(ParseError::new(
205 Parse,
206 end.to_string(),
207 "one character".to_string(),
208 Pos::new(line_num, column_num + 3)
209 ));
210 }
211 },
212 _ => return Err(error),
213 }
214 },
215 }
216 }
217 else {
218 return special_key(&key, line_num, column_num, false);
219 }
220 },
221 Some(character) => {
222 let characters = "=+-;!\"'#%&()*,./<>?@[\\]^_{|}~çÇéÉàÀèÈ$";
223 match character {
224 'A' ... 'Z' | 'a' ... 'z' => (Char(character), 1),
225 _ if characters.contains(character) => (Char(character), 1),
226 _ =>
227 return Err(ParseError::new(
228 Parse,
229 character.to_string(),
230 "key".to_string(),
231 Pos::new(line_num, column_num)
232 ))
233 }
234 },
235 None => unreachable!() ,
236 };
237 Ok(result)
238}
239
240pub fn parse_keys(mut input: &str, line_num: usize, column_num: usize) -> Result<Vec<Key>> {
242 let mut keys = vec![];
243 let mut index = 0;
244 while !input.is_empty() {
245 let (key, size) = parse_key(input, line_num, column_num + index)?;
246 keys.push(key);
247 input = &input[size..];
248 index += size;
249 }
250 Ok(keys)
251}
252
253fn special_key(key: &str, line_num: usize, column_num: usize, in_special_key: bool) -> Result<(Key, usize)> {
255 let expected =
256 if in_special_key {
257 "A-Z or special key"
258 }
259 else {
260 "special key"
261 };
262 let result =
263 match key {
264 "Backspace" => (Backspace, 11),
265 "Delete" => (Delete, 8),
266 "Down" => (Down, 6),
267 "End" => (End, 5),
268 "Enter" => (Enter, 7),
269 "Esc" => (Escape, 5),
270 "F1" => (F1, 4),
271 "F2" => (F2, 4),
272 "F3" => (F3, 4),
273 "F4" => (F4, 4),
274 "F5" => (F5, 4),
275 "F6" => (F6, 4),
276 "F7" => (F7, 4),
277 "F8" => (F8, 4),
278 "F9" => (F9, 4),
279 "F10" => (F10, 5),
280 "F11" => (F11, 5),
281 "F12" => (F12, 5),
282 "Home" => (Home, 6),
283 "Insert" => (Insert, 8),
284 "Left" => (Left, 6),
285 "PageDown" => (PageDown, 10),
286 "PageUp" => (PageUp, 8),
287 "Right" => (Right, 7),
288 "Space" => (Space, 7),
289 "Tab" => (Tab, 5),
290 "Up" => (Up, 4),
291 _ => return Err(ParseError::new(
292 Parse,
293 key.to_string(),
294 expected.to_string(),
295 Pos::new(line_num, column_num + 1)
296 )),
297 };
298 Ok(result)
299}