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