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; 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 HatMapping {
309 hat: u16,
310 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}