gilrs/mapping/
parser.rs

1// Copyright 2016-2018 Mateusz Sieczko and other GilRs Developers
2//
3// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
4// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
5// http://opensource.org/licenses/MIT>, at your option. This file may not be
6// copied, modified, or distributed except according to those terms.
7use std::error::Error as StdError;
8use std::fmt::{self, Display};
9
10use uuid::Uuid;
11
12use crate::ev::{Axis, AxisOrBtn, Button};
13
14// Must be sorted!
15static AXES_SDL: [&str; 32] = [
16    "a",
17    "b",
18    "back",
19    "c",
20    "dpdown",
21    "dpleft",
22    "dpright",
23    "dpup",
24    "guide",
25    "leftshoulder",
26    "leftstick",
27    "lefttrigger",
28    "leftx",
29    "lefty",
30    "leftz",
31    "misc1",
32    "misc2",
33    "paddle1",
34    "paddle2",
35    "paddle3",
36    "paddle4",
37    "rightshoulder",
38    "rightstick",
39    "righttrigger",
40    "rightx",
41    "righty",
42    "rightz",
43    "start",
44    "touchpad",
45    "x",
46    "y",
47    "z",
48];
49static AXES: [AxisOrBtn; 32] = [
50    AxisOrBtn::Btn(Button::South),
51    AxisOrBtn::Btn(Button::East),
52    AxisOrBtn::Btn(Button::Select),
53    AxisOrBtn::Btn(Button::C),
54    AxisOrBtn::Btn(Button::DPadDown),
55    AxisOrBtn::Btn(Button::DPadLeft),
56    AxisOrBtn::Btn(Button::DPadRight),
57    AxisOrBtn::Btn(Button::DPadUp),
58    AxisOrBtn::Btn(Button::Mode),
59    AxisOrBtn::Btn(Button::LeftTrigger),
60    AxisOrBtn::Btn(Button::LeftThumb),
61    AxisOrBtn::Btn(Button::LeftTrigger2),
62    AxisOrBtn::Axis(Axis::LeftStickX),
63    AxisOrBtn::Axis(Axis::LeftStickY),
64    AxisOrBtn::Axis(Axis::LeftZ),
65    AxisOrBtn::Btn(Button::Unknown),
66    AxisOrBtn::Btn(Button::Unknown),
67    AxisOrBtn::Btn(Button::Unknown),
68    AxisOrBtn::Btn(Button::Unknown),
69    AxisOrBtn::Btn(Button::Unknown),
70    AxisOrBtn::Btn(Button::Unknown),
71    AxisOrBtn::Btn(Button::RightTrigger),
72    AxisOrBtn::Btn(Button::RightThumb),
73    AxisOrBtn::Btn(Button::RightTrigger2),
74    AxisOrBtn::Axis(Axis::RightStickX),
75    AxisOrBtn::Axis(Axis::RightStickY),
76    AxisOrBtn::Axis(Axis::RightZ),
77    AxisOrBtn::Btn(Button::Start),
78    AxisOrBtn::Btn(Button::Unknown),
79    AxisOrBtn::Btn(Button::West),
80    AxisOrBtn::Btn(Button::North),
81    AxisOrBtn::Btn(Button::Z),
82];
83
84pub struct Parser<'a> {
85    data: &'a str,
86    pos: usize,
87    state: State,
88}
89
90impl<'a> Parser<'a> {
91    pub fn new(mapping: &'a str) -> Self {
92        Parser {
93            data: mapping,
94            pos: 0,
95            state: State::Uuid,
96        }
97    }
98
99    pub fn next_token(&mut self) -> Option<Result<Token<'_>, Error>> {
100        if self.pos >= self.data.len() {
101            None
102        } else {
103            Some(match self.state {
104                State::Uuid => self.parse_uuid(),
105                State::Name => self.parse_name(),
106                State::KeyVal => self.parse_key_val(),
107                State::Invalid => Err(Error::new(ErrorKind::InvalidParserState, self.pos)),
108            })
109        }
110    }
111
112    fn parse_uuid(&mut self) -> Result<Token<'_>, Error> {
113        let next_comma = self.next_comma_or_end();
114        let uuid_field = &self.data[self.pos..next_comma];
115        let uuid = if uuid_field == "xinput" {
116            Ok(Token::Uuid(Uuid::nil()))
117        } else {
118            Uuid::parse_str(uuid_field)
119                .map(Token::Uuid)
120                .map_err(|_| Error::new(ErrorKind::InvalidGuid, self.pos))
121        };
122
123        if uuid.is_err() {
124            self.state = State::Invalid;
125        } else if next_comma == self.data.len() {
126            self.state = State::Invalid;
127
128            return Err(Error::new(ErrorKind::UnexpectedEnd, self.pos));
129        } else {
130            self.state = State::Name;
131            self.pos = next_comma + 1;
132        }
133
134        uuid
135    }
136
137    fn parse_name(&mut self) -> Result<Token<'_>, Error> {
138        let next_comma = self.next_comma_or_end();
139        let name = &self.data[self.pos..next_comma];
140
141        self.state = State::KeyVal;
142        self.pos = next_comma + 1;
143
144        Ok(Token::Name(name))
145    }
146
147    fn parse_key_val(&mut self) -> Result<Token<'_>, Error> {
148        let next_comma = self.next_comma_or_end();
149        let pair = &self.data[self.pos..next_comma];
150        let pos = self.pos;
151        self.pos = next_comma + 1;
152
153        let mut split = pair.split(':');
154        let key = split
155            .next()
156            .ok_or_else(|| Error::new(ErrorKind::InvalidKeyValPair, pos))?;
157        let value = split
158            .next()
159            .ok_or_else(|| Error::new(ErrorKind::InvalidKeyValPair, pos))?;
160
161        if split.next().is_some() {
162            return Err(Error::new(ErrorKind::InvalidKeyValPair, pos));
163        }
164
165        if value.is_empty() {
166            return Err(Error::new(ErrorKind::EmptyValue, pos));
167        }
168
169        if key == "platform" {
170            return Ok(Token::Platform(value));
171        }
172
173        let mut input = AxisRange::Full;
174        let mut output = AxisRange::Full;
175        let mut inverted = false;
176        let mut is_axis = false;
177
178        let key = match key.get(0..1) {
179            Some("+") => {
180                output = AxisRange::UpperHalf;
181                &key[1..]
182            }
183            Some("-") => {
184                output = AxisRange::LowerHalf;
185                &key[1..]
186            }
187            _ => key,
188        };
189
190        let from = match value.get(0..1) {
191            Some("+") if value.get(1..2) == Some("a") => {
192                is_axis = true;
193                input = AxisRange::UpperHalf;
194
195                if value.get((value.len() - 1)..) == Some("~") {
196                    inverted = true;
197
198                    &value[2..(value.len() - 1)]
199                } else {
200                    &value[2..]
201                }
202            }
203            Some("-") if value.get(1..2) == Some("a") => {
204                is_axis = true;
205                input = AxisRange::LowerHalf;
206
207                if value.get((value.len() - 1)..) == Some("~") {
208                    inverted = true;
209
210                    &value[2..(value.len() - 1)]
211                } else {
212                    &value[2..]
213                }
214            }
215            Some("a") => {
216                is_axis = true;
217
218                if value.get((value.len() - 1)..) == Some("~") {
219                    inverted = true;
220
221                    &value[1..(value.len() - 1)]
222                } else {
223                    &value[1..]
224                }
225            }
226            Some("b") => &value[1..],
227            Some("h") => {
228                let dot_idx = value
229                    .find('.')
230                    .ok_or_else(|| Error::new(ErrorKind::InvalidValue, pos))?;
231                let hat = value[1..dot_idx]
232                    .parse()
233                    .map_err(|_| Error::new(ErrorKind::InvalidValue, pos + 1))?;
234                let direction = value
235                    .get((dot_idx + 1)..)
236                    .and_then(|s| s.parse().ok())
237                    .ok_or_else(|| Error::new(ErrorKind::InvalidValue, pos + dot_idx + 1))?;
238
239                let idx = AXES_SDL
240                    .binary_search(&key)
241                    .map_err(|_| Error::new(ErrorKind::UnknownButton, pos))?;
242
243                return Ok(Token::HatMapping {
244                    hat,
245                    direction,
246                    to: AXES[idx],
247                    output,
248                });
249            }
250            _ => return Err(Error::new(ErrorKind::InvalidValue, pos)),
251        }
252        .parse::<u16>()
253        .map_err(|_| Error::new(ErrorKind::InvalidValue, pos))?;
254
255        if is_axis {
256            let idx = AXES_SDL
257                .binary_search(&key)
258                .map_err(|_| Error::new(ErrorKind::UnknownAxis, pos))?;
259
260            Ok(Token::AxisMapping {
261                from,
262                to: AXES[idx],
263                input,
264                output,
265                inverted,
266            })
267        } else {
268            let idx = AXES_SDL
269                .binary_search(&key)
270                .map_err(|_| Error::new(ErrorKind::UnknownButton, pos))?;
271
272            Ok(Token::ButtonMapping {
273                from,
274                to: AXES[idx],
275                output,
276            })
277        }
278    }
279
280    fn next_comma_or_end(&self) -> usize {
281        self.data[self.pos..]
282            .find(',')
283            .map(|x| x + self.pos)
284            .unwrap_or_else(|| self.data.len())
285    }
286}
287
288#[derive(Debug)]
289pub enum Token<'a> {
290    Uuid(Uuid),
291    Platform(&'a str),
292    Name(&'a str),
293    #[allow(dead_code)]
294    AxisMapping {
295        from: u16,
296        to: AxisOrBtn,
297        input: AxisRange,
298        output: AxisRange,
299        inverted: bool,
300    },
301    ButtonMapping {
302        from: u16,
303        to: AxisOrBtn,
304        #[allow(dead_code)]
305        output: AxisRange,
306    },
307    // This is just SDL representation, we will convert this to axis mapping later
308    HatMapping {
309        hat: u16,
310        // ?
311        direction: u16,
312        to: AxisOrBtn,
313        #[allow(dead_code)]
314        output: AxisRange,
315    },
316}
317
318#[repr(u8)]
319#[derive(Debug)]
320pub enum AxisRange {
321    LowerHalf,
322    UpperHalf,
323    Full,
324}
325
326#[derive(Copy, Clone, Eq, PartialEq)]
327enum State {
328    Uuid,
329    Name,
330    KeyVal,
331    Invalid,
332}
333
334#[derive(Debug, Clone, PartialEq, Eq)]
335pub struct Error {
336    pub(crate) position: usize,
337    kind: ErrorKind,
338}
339
340impl Error {
341    pub fn new(kind: ErrorKind, position: usize) -> Self {
342        Error { position, kind }
343    }
344
345    pub fn kind(&self) -> &ErrorKind {
346        &self.kind
347    }
348}
349
350#[derive(Debug, Clone, PartialEq, Eq)]
351#[non_exhaustive]
352pub enum ErrorKind {
353    InvalidGuid,
354    InvalidKeyValPair,
355    InvalidValue,
356    EmptyValue,
357    UnknownAxis,
358    UnknownButton,
359    InvalidParserState,
360    UnexpectedEnd,
361}
362
363impl StdError for Error {}
364
365impl Display for Error {
366    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
367        let s = match self.kind {
368            ErrorKind::InvalidGuid => "GUID is invalid",
369            ErrorKind::InvalidKeyValPair => "expected key value pair",
370            ErrorKind::InvalidValue => "value is not valid",
371            ErrorKind::EmptyValue => "value is empty",
372            ErrorKind::UnknownAxis => "invalid axis name",
373            ErrorKind::UnknownButton => "invalid button name",
374            ErrorKind::InvalidParserState => "attempt to parse after unrecoverable error",
375            ErrorKind::UnexpectedEnd => "mapping does not have all required fields",
376        };
377
378        f.write_fmt(format_args!("{} at {}", s, self.position))
379    }
380}
381
382#[cfg(test)]
383mod tests {
384    use crate::mapping::parser::{ErrorKind, Parser};
385    use crate::utils::PATH_SEPARATOR;
386
387    #[test]
388    fn test_all_sdl_mappings_for_parse_errors() {
389        let included_mappings = include_str!(concat!(
390            env!("OUT_DIR"),
391            PATH_SEPARATOR!(),
392            "gamecontrollerdb.txt"
393        ))
394        .lines();
395
396        let mut errors = 0;
397        let mut index = 0;
398        for line in included_mappings {
399            let mut parser = Parser::new(line);
400
401            while let Some(token) = parser.next_token() {
402                if let Err(ref e) = token {
403                    if e.kind() != &ErrorKind::EmptyValue {
404                        errors += 1;
405                        println!("{e:?}");
406                        println!(
407                            "{}: {} (...) {}\n",
408                            index,
409                            line.chars().take(50).collect::<String>(),
410                            line.chars().skip(e.position).take(15).collect::<String>()
411                        );
412
413                        if e.kind() == &ErrorKind::InvalidParserState {
414                            break;
415                        }
416                    }
417                }
418                index += 1;
419            }
420        }
421        assert_eq!(errors, 0);
422    }
423}