mg_settings/
key.rs

1/*
2 * Copyright (c) 2016-2017 Boucher, Antoni <bouanto@zoho.com>
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a copy of
5 * this software and associated documentation files (the "Software"), to deal in
6 * the Software without restriction, including without limitation the rights to
7 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
8 * the Software, and to permit persons to whom the Software is furnished to do so,
9 * subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in all
12 * copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
16 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
17 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
18 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
19 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20 */
21
22//! Type for representing keys and functions for parsing strings into `Key`s.
23
24use std::fmt::{self, Display, Formatter};
25
26use errors::{ParseError, Result};
27use errors::ErrorType::Parse;
28use position::Pos;
29
30use self::Key::*;
31
32/// Structure to represent which keys were pressed.
33struct 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/// Enum representing the keys that can be used in a mapping.
50#[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
85/// Convert a `Key` a to `String`.
86/// Note that the result does not contain < and >.
87fn 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(); // NOTE: There is at least one character, hence unwrap.
193                    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
240/// Parse a string into a vector of `Key`s.
241pub 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
253/// Parse a special key.
254fn 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}