Skip to main content

gilrs/mapping/
mod.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.
7#![cfg_attr(target_os = "windows", allow(dead_code))]
8
9mod parser;
10
11use crate::ev::{self, Axis, AxisOrBtn, Button};
12use gilrs_core::native_ev_codes as nec;
13use gilrs_core::EvCode;
14
15use std::collections::HashMap;
16use std::env;
17use std::error::Error;
18use std::fmt::{Display, Formatter, Result as FmtResult, Write as _};
19
20use fnv::FnvHashMap;
21use uuid::Uuid;
22use vec_map::VecMap;
23
24use self::parser::{Error as ParserError, ErrorKind as ParserErrorKind, Parser, Token};
25
26/// Platform name used by SDL mappings
27#[cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd"))]
28const SDL_PLATFORM_NAME: &str = "Linux";
29#[cfg(target_os = "macos")]
30const SDL_PLATFORM_NAME: &str = "Mac OS X";
31#[cfg(target_os = "windows")]
32const SDL_PLATFORM_NAME: &str = "Windows";
33#[cfg(all(
34    not(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd")),
35    not(target_os = "macos"),
36    not(target_os = "windows")
37))]
38const SDL_PLATFORM_NAME: &str = "Unknown";
39
40#[derive(Debug)]
41#[cfg_attr(test, derive(PartialEq))]
42/// Store mappings from one `EvCode` (`u16`) to another.
43///
44/// This struct is internal, `MappingData` is exported in public interface as `Mapping`.
45pub struct Mapping {
46    mappings: FnvHashMap<EvCode, AxisOrBtn>,
47    name: String,
48    default: bool,
49    hats_mapped: u8,
50}
51
52impl Mapping {
53    pub fn new() -> Self {
54        Mapping {
55            mappings: FnvHashMap::default(),
56            name: String::new(),
57            default: false,
58            hats_mapped: 0,
59        }
60    }
61
62    pub fn default(gamepad: &gilrs_core::Gamepad) -> Self {
63        use self::Axis as Ax;
64        use self::AxisOrBtn::*;
65
66        macro_rules! fnv_map {
67            ( $( $key:expr => $elem:expr ),* ) => {
68                {
69                    let mut map = FnvHashMap::default();
70                    $(
71                        map.insert($key, $elem);
72                    )*
73
74                    map
75                }
76            };
77        }
78
79        let mut mappings = fnv_map![
80            nec::BTN_SOUTH => Btn(Button::South),
81            nec::BTN_EAST => Btn(Button::East),
82            nec::BTN_C => Btn(Button::C),
83            nec::BTN_NORTH => Btn(Button::North),
84            nec::BTN_WEST => Btn(Button::West),
85            nec::BTN_Z => Btn(Button::Z),
86            nec::BTN_LT => Btn(Button::LeftTrigger),
87            nec::BTN_RT => Btn(Button::RightTrigger),
88            nec::BTN_LT2 => Btn(Button::LeftTrigger2),
89            nec::BTN_RT2 => Btn(Button::RightTrigger2),
90            nec::BTN_SELECT => Btn(Button::Select),
91            nec::BTN_START => Btn(Button::Start),
92            nec::BTN_MODE => Btn(Button::Mode),
93            nec::BTN_LTHUMB => Btn(Button::LeftThumb),
94            nec::BTN_RTHUMB => Btn(Button::RightThumb),
95            nec::BTN_DPAD_UP => Btn(Button::DPadUp),
96            nec::BTN_DPAD_DOWN => Btn(Button::DPadDown),
97            nec::BTN_DPAD_LEFT => Btn(Button::DPadLeft),
98            nec::BTN_DPAD_RIGHT => Btn(Button::DPadRight),
99
100            nec::AXIS_LT => Btn(Button::LeftTrigger),
101            nec::AXIS_RT => Btn(Button::RightTrigger),
102            nec::AXIS_LT2 => Btn(Button::LeftTrigger2),
103            nec::AXIS_RT2 => Btn(Button::RightTrigger2),
104
105            nec::AXIS_LSTICKX => Axis(Ax::LeftStickX),
106            nec::AXIS_LSTICKY => Axis(Ax::LeftStickY),
107            nec::AXIS_LEFTZ => Axis(Ax::LeftZ),
108            nec::AXIS_RSTICKX => Axis(Ax::RightStickX),
109            nec::AXIS_RSTICKY => Axis(Ax::RightStickY),
110            nec::AXIS_RIGHTZ => Axis(Ax::RightZ),
111            nec::AXIS_DPADX => Axis(Ax::DPadX),
112            nec::AXIS_DPADY => Axis(Ax::DPadY)
113        ];
114
115        // Remove all mappings that don't have corresponding element in gamepad. Partial fix to #83
116        let axes = [
117            nec::AXIS_DPADX,
118            nec::AXIS_DPADY,
119            nec::AXIS_LEFTZ,
120            nec::AXIS_LSTICKX,
121            nec::AXIS_LSTICKY,
122            nec::AXIS_RSTICKX,
123            nec::AXIS_RSTICKY,
124            nec::AXIS_LT,
125            nec::AXIS_LT2,
126            nec::AXIS_RT,
127            nec::AXIS_RT2,
128            nec::AXIS_RIGHTZ,
129        ];
130        let btns = [
131            nec::BTN_SOUTH,
132            nec::BTN_NORTH,
133            nec::BTN_WEST,
134            nec::BTN_WEST,
135            nec::BTN_C,
136            nec::BTN_Z,
137            nec::BTN_LT,
138            nec::BTN_LT2,
139            nec::BTN_RT,
140            nec::BTN_RT2,
141            nec::BTN_SELECT,
142            nec::BTN_START,
143            nec::BTN_MODE,
144            nec::BTN_LTHUMB,
145            nec::BTN_RTHUMB,
146            nec::BTN_DPAD_DOWN,
147            nec::BTN_DPAD_LEFT,
148            nec::BTN_DPAD_RIGHT,
149            nec::BTN_DPAD_UP,
150        ];
151
152        for axis in &axes {
153            if !gamepad.axes().contains(axis) {
154                mappings.remove(axis);
155            }
156        }
157
158        for btn in &btns {
159            if !gamepad.buttons().contains(btn) {
160                mappings.remove(btn);
161            }
162        }
163
164        Mapping {
165            mappings,
166            name: String::new(),
167            default: true,
168            hats_mapped: 0,
169        }
170    }
171
172    pub fn name(&self) -> &str {
173        &self.name
174    }
175
176    pub fn from_data(
177        data: &MappingData,
178        buttons: &[EvCode],
179        axes: &[EvCode],
180        name: &str,
181        uuid: Uuid,
182    ) -> Result<(Self, String), MappingError> {
183        use crate::constants::*;
184
185        if !Self::is_name_valid(name) {
186            return Err(MappingError::InvalidName);
187        }
188
189        let mut mappings = FnvHashMap::default();
190        let mut sdl_mappings = format!("{},{},", uuid.as_simple(), name);
191
192        {
193            let mut add_button = |ident, ev_code, mapped_btn| {
194                Self::add_button(
195                    ident,
196                    ev_code,
197                    mapped_btn,
198                    buttons,
199                    &mut sdl_mappings,
200                    &mut mappings,
201                )
202            };
203
204            for (button, &ev_code) in &data.buttons {
205                match button as u16 {
206                    BTN_SOUTH => add_button("a", ev_code, Button::South)?,
207                    BTN_EAST => add_button("b", ev_code, Button::East)?,
208                    BTN_WEST => add_button("x", ev_code, Button::West)?,
209                    BTN_NORTH => add_button("y", ev_code, Button::North)?,
210                    BTN_LT => add_button("leftshoulder", ev_code, Button::LeftTrigger)?,
211                    BTN_RT => add_button("rightshoulder", ev_code, Button::RightTrigger)?,
212                    BTN_LT2 => add_button("lefttrigger", ev_code, Button::LeftTrigger2)?,
213                    BTN_RT2 => add_button("righttrigger", ev_code, Button::RightTrigger2)?,
214                    BTN_SELECT => add_button("back", ev_code, Button::Select)?,
215                    BTN_START => add_button("start", ev_code, Button::Start)?,
216                    BTN_MODE => add_button("guide", ev_code, Button::Mode)?,
217                    BTN_LTHUMB => add_button("leftstick", ev_code, Button::LeftThumb)?,
218                    BTN_RTHUMB => add_button("rightstick", ev_code, Button::RightThumb)?,
219                    BTN_DPAD_UP => add_button("dpup", ev_code, Button::DPadUp)?,
220                    BTN_DPAD_DOWN => add_button("dpdown", ev_code, Button::DPadDown)?,
221                    BTN_DPAD_LEFT => add_button("dpleft", ev_code, Button::DPadLeft)?,
222                    BTN_DPAD_RIGHT => add_button("dpright", ev_code, Button::DPadRight)?,
223                    BTN_C => add_button("c", ev_code, Button::C)?,
224                    BTN_Z => add_button("z", ev_code, Button::Z)?,
225                    BTN_UNKNOWN => return Err(MappingError::UnknownElement),
226                    _ => unreachable!(),
227                }
228            }
229        }
230
231        {
232            let mut add_axis = |ident, ev_code, mapped_axis| {
233                Self::add_axis(
234                    ident,
235                    ev_code,
236                    mapped_axis,
237                    axes,
238                    &mut sdl_mappings,
239                    &mut mappings,
240                )
241            };
242
243            for (axis, &ev_code) in &data.axes {
244                match axis as u16 {
245                    AXIS_LSTICKX => add_axis("leftx", ev_code, Axis::LeftStickX)?,
246                    AXIS_LSTICKY => add_axis("lefty", ev_code, Axis::LeftStickY)?,
247                    AXIS_RSTICKX => add_axis("rightx", ev_code, Axis::RightStickX)?,
248                    AXIS_RSTICKY => add_axis("righty", ev_code, Axis::RightStickY)?,
249                    AXIS_LEFTZ => add_axis("leftz", ev_code, Axis::LeftZ)?,
250                    AXIS_RIGHTZ => add_axis("rightz", ev_code, Axis::RightZ)?,
251                    AXIS_UNKNOWN => return Err(MappingError::UnknownElement),
252                    _ => unreachable!(),
253                }
254            }
255        }
256
257        let mapping = Mapping {
258            mappings,
259            name: name.to_owned(),
260            default: false,
261            hats_mapped: 0,
262        };
263
264        Ok((mapping, sdl_mappings))
265    }
266
267    pub fn parse_sdl_mapping(
268        line: &str,
269        buttons: &[EvCode],
270        axes: &[EvCode],
271    ) -> Result<Self, ParseSdlMappingError> {
272        let mut mapping = Mapping::new();
273        let mut parser = Parser::new(line);
274
275        let mut uuid: Option<Uuid> = None;
276        while let Some(token) = parser.next_token() {
277            if let Err(ref e) = token {
278                if e.kind() == &ParserErrorKind::EmptyValue {
279                    continue;
280                }
281            }
282
283            let token = token?;
284
285            match token {
286                Token::Platform(platform) => {
287                    if platform != SDL_PLATFORM_NAME {
288                        warn!("Mappings for different platform – {}", platform);
289                    }
290                }
291                Token::Uuid(v) => uuid = Some(v),
292
293                Token::Name(name) => mapping.name = name.to_owned(),
294                Token::AxisMapping { from, to, .. } => {
295                    let axis = axes.get(from as usize).cloned();
296                    if let Some(axis) = axis {
297                        mapping.mappings.insert(axis, to);
298                    } else {
299                        warn!(
300                            "SDL-mapping {} {}: Unknown axis a{}",
301                            uuid.unwrap(),
302                            mapping.name,
303                            from
304                        )
305                    }
306                }
307                Token::ButtonMapping { from, to, .. } => {
308                    let btn = buttons.get(from as usize).cloned();
309
310                    if let Some(btn) = btn {
311                        mapping.mappings.insert(btn, to);
312                    } else {
313                        warn!(
314                            "SDL-mapping {} {}: Unknown button b{}",
315                            uuid.unwrap(),
316                            mapping.name,
317                            from
318                        )
319                    }
320                }
321                Token::HatMapping {
322                    hat, direction, to, ..
323                } => {
324                    if hat != 0 {
325                        warn!(
326                            "Hat mappings are only supported for dpads (requested to map hat \
327                             {}.{} to {:?}",
328                            hat, direction, to
329                        );
330                    } else {
331                        // We  don't have anything like "hat" in gilrs, so let's jus assume that
332                        // user want to map dpad axes.
333                        //
334                        // We have to add mappings for axes AND buttons, because axis_dpad_to_button
335                        // filter may transform event to button event.
336                        let (from_axis, from_btn) = match direction {
337                            1 => (nec::AXIS_DPADY, nec::BTN_DPAD_UP),
338                            4 => (nec::AXIS_DPADY, nec::BTN_DPAD_DOWN),
339                            2 => (nec::AXIS_DPADX, nec::BTN_DPAD_RIGHT),
340                            8 => (nec::AXIS_DPADX, nec::BTN_DPAD_LEFT),
341                            0 => continue, // FIXME: I have no idea what 0 means here
342                            _ => return Err(ParseSdlMappingError::UnknownHatDirection),
343                        };
344
345                        if to.is_button() {
346                            match to {
347                                AxisOrBtn::Btn(Button::DPadLeft | Button::DPadRight) => {
348                                    mapping
349                                        .mappings
350                                        .insert(from_axis, AxisOrBtn::Axis(Axis::DPadX));
351                                }
352                                AxisOrBtn::Btn(Button::DPadUp | Button::DPadDown) => {
353                                    mapping
354                                        .mappings
355                                        .insert(from_axis, AxisOrBtn::Axis(Axis::DPadY));
356                                }
357                                _ => (),
358                            }
359                            mapping.mappings.insert(from_btn, to);
360                        } else {
361                            mapping.mappings.insert(from_axis, to);
362                        }
363
364                        mapping.hats_mapped |= direction as u8;
365                    }
366                }
367            }
368        }
369
370        Ok(mapping)
371    }
372
373    fn add_button(
374        ident: &str,
375        ev_code: EvCode,
376        mapped_btn: Button,
377        buttons: &[EvCode],
378        sdl_mappings: &mut String,
379        mappings: &mut FnvHashMap<EvCode, AxisOrBtn>,
380    ) -> Result<(), MappingError> {
381        let n_btn = buttons
382            .iter()
383            .position(|&x| x == ev_code)
384            .ok_or(MappingError::InvalidCode(ev::Code(ev_code)))?;
385        let _ = write!(sdl_mappings, "{}:b{},", ident, n_btn);
386        mappings.insert(ev_code, AxisOrBtn::Btn(mapped_btn));
387        Ok(())
388    }
389
390    fn add_axis(
391        ident: &str,
392        ev_code: EvCode,
393        mapped_axis: Axis,
394        axes: &[EvCode],
395        sdl_mappings: &mut String,
396        mappings: &mut FnvHashMap<EvCode, AxisOrBtn>,
397    ) -> Result<(), MappingError> {
398        let n_axis = axes
399            .iter()
400            .position(|&x| x == ev_code)
401            .ok_or(MappingError::InvalidCode(ev::Code(ev_code)))?;
402        let _ = write!(sdl_mappings, "{}:a{},", ident, n_axis);
403        mappings.insert(ev_code, AxisOrBtn::Axis(mapped_axis));
404        Ok(())
405    }
406
407    fn is_name_valid(name: &str) -> bool {
408        !name.chars().any(|x| x == ',')
409    }
410
411    pub fn map(&self, code: &EvCode) -> Option<AxisOrBtn> {
412        self.mappings.get(code).cloned()
413    }
414
415    pub fn map_rev(&self, el: &AxisOrBtn) -> Option<EvCode> {
416        self.mappings.iter().find(|x| x.1 == el).map(|x| *x.0)
417    }
418
419    pub fn is_default(&self) -> bool {
420        self.default
421    }
422
423    /// Return bit field with mapped hats. Only for mappings created from SDL format this function
424    /// can return non-zero value.
425    pub fn hats_mapped(&self) -> u8 {
426        self.hats_mapped
427    }
428}
429
430#[derive(Clone, PartialEq, Eq, Debug)]
431pub enum ParseSdlMappingError {
432    UnknownHatDirection,
433    ParseError(ParserError),
434}
435
436impl From<ParserError> for ParseSdlMappingError {
437    fn from(f: ParserError) -> Self {
438        ParseSdlMappingError::ParseError(f)
439    }
440}
441
442impl Error for ParseSdlMappingError {
443    fn source(&self) -> Option<&(dyn Error + 'static)> {
444        if let ParseSdlMappingError::ParseError(ref err) = self {
445            Some(err)
446        } else {
447            None
448        }
449    }
450}
451
452impl Display for ParseSdlMappingError {
453    fn fmt(&self, fmt: &mut Formatter<'_>) -> FmtResult {
454        match self {
455            ParseSdlMappingError::UnknownHatDirection => {
456                fmt.write_str("hat direction wasn't 1, 2, 4 or 8")
457            }
458            ParseSdlMappingError::ParseError(_) => fmt.write_str("parsing error"),
459        }
460    }
461}
462
463#[derive(Debug)]
464pub struct MappingDb {
465    mappings: HashMap<Uuid, String>,
466}
467
468impl MappingDb {
469    pub fn new() -> Self {
470        MappingDb {
471            mappings: HashMap::new(),
472        }
473    }
474
475    pub fn add_included_mappings(&mut self) {
476        self.insert(include_str!(concat!(
477            env!("OUT_DIR"),
478            "/gamecontrollerdb.txt"
479        )));
480    }
481
482    pub fn add_env_mappings(&mut self) {
483        if let Ok(mapping) = env::var("SDL_GAMECONTROLLERCONFIG") {
484            self.insert(&mapping);
485        }
486    }
487
488    pub fn insert(&mut self, s: &str) {
489        for mapping in s.lines() {
490            let pat = "platform:";
491            if let Some(offset) = mapping.find(pat).map(|o| o + pat.len()) {
492                let s = &mapping[offset..];
493                let end = s.find(',').unwrap_or(s.len());
494
495                if &s[..end] != SDL_PLATFORM_NAME {
496                    continue;
497                }
498            }
499
500            mapping
501                .split(',')
502                .next()
503                .and_then(|s| Uuid::parse_str(s).ok())
504                .and_then(|uuid| self.mappings.insert(uuid, mapping.to_owned()));
505        }
506    }
507
508    pub fn get(&self, uuid: Uuid) -> Option<&str> {
509        self.mappings.get(&uuid).map(String::as_ref)
510    }
511
512    pub fn len(&self) -> usize {
513        self.mappings.len()
514    }
515}
516
517/// Stores data used to map gamepad buttons and axes.
518///
519/// After you add all mappings, use
520/// [`Gamepad::set_mapping(…)`](struct.Gamepad.html#method.set_mapping) to change mapping of
521/// existing gamepad.
522///
523/// See `examples/mapping.rs` for more detailed example.
524#[derive(Debug, Clone, Default)]
525// Re-exported as Mapping
526pub struct MappingData {
527    buttons: VecMap<EvCode>,
528    axes: VecMap<EvCode>,
529}
530
531impl MappingData {
532    /// Creates new `Mapping`.
533    pub fn new() -> Self {
534        MappingData {
535            buttons: VecMap::with_capacity(18),
536            axes: VecMap::with_capacity(11),
537        }
538    }
539
540    /// Returns `EvCode` associated with button index.
541    pub fn button(&self, idx: Button) -> Option<ev::Code> {
542        self.buttons.get(idx as usize).cloned().map(ev::Code)
543    }
544
545    /// Returns `EvCode` associated with axis index.
546    pub fn axis(&self, idx: Axis) -> Option<ev::Code> {
547        self.axes.get(idx as usize).cloned().map(ev::Code)
548    }
549
550    /// Inserts new button mapping.
551    pub fn insert_btn(&mut self, from: ev::Code, to: Button) -> Option<ev::Code> {
552        self.buttons.insert(to as usize, from.0).map(ev::Code)
553    }
554
555    /// Inserts new axis mapping.
556    pub fn insert_axis(&mut self, from: ev::Code, to: Axis) -> Option<ev::Code> {
557        self.axes.insert(to as usize, from.0).map(ev::Code)
558    }
559
560    /// Removes button and returns associated `NativEvCode`.
561    pub fn remove_button(&mut self, idx: Button) -> Option<ev::Code> {
562        self.buttons.remove(idx as usize).map(ev::Code)
563    }
564
565    /// Removes axis and returns associated `NativEvCode`.
566    pub fn remove_axis(&mut self, idx: Axis) -> Option<ev::Code> {
567        self.axes.remove(idx as usize).map(ev::Code)
568    }
569}
570
571/// The error type for functions related to gamepad mapping.
572#[derive(Copy, Clone, Debug, PartialEq, Eq)]
573#[non_exhaustive]
574pub enum MappingError {
575    /// Gamepad does not have element referenced by `EvCode`.
576    InvalidCode(ev::Code),
577    /// Name contains comma (',').
578    InvalidName,
579    /// This function is not implemented for current platform.
580    NotImplemented,
581    /// Gamepad is not connected.
582    NotConnected,
583    /// Same gamepad element is referenced by axis and button.
584    DuplicatedEntry,
585    /// `Mapping` with `Button::Unknown` or `Axis::Unknown`.
586    UnknownElement,
587    /// `Mapping` have button or axis that are not present in SDL2.
588    NotSdl2Compatible,
589}
590
591impl Error for MappingError {}
592
593impl Display for MappingError {
594    fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
595        let sbuf;
596        let s = match self {
597            MappingError::InvalidCode(code) => {
598                sbuf = format!("gamepad does not have element with {}", code);
599                sbuf.as_ref()
600            }
601            MappingError::InvalidName => "name can not contain comma",
602            MappingError::NotImplemented => {
603                "current platform does not implement setting custom mappings"
604            }
605            MappingError::NotConnected => "gamepad is not connected",
606            MappingError::DuplicatedEntry => {
607                "same gamepad element is referenced by axis and button"
608            }
609            MappingError::UnknownElement => "Button::Unknown and Axis::Unknown are not allowed",
610            MappingError::NotSdl2Compatible => "one of buttons or axes is not compatible with SDL2",
611        };
612
613        f.write_str(s)
614    }
615}
616
617#[cfg(test)]
618mod tests {
619    use super::*;
620    use crate::ev::{Axis, Button};
621    use gilrs_core::native_ev_codes as nec;
622    use gilrs_core::EvCode;
623    use uuid::Uuid;
624    // Do not include platform, mapping from (with UUID modified)
625    // https://github.com/gabomdq/SDL_GameControllerDB/blob/master/gamecontrollerdb.txt
626    const TEST_STR: &str = "03000000260900008888000000010001,GameCube {WiseGroup USB \
627                            box},a:b0,b:b2,y:b3,x:b1,start:b7,rightshoulder:b6,dpup:h0.1,dpleft:\
628                            h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,\
629                            lefttrigger:a4,righttrigger:a5,";
630
631    const BUTTONS: [EvCode; 15] = [
632        nec::BTN_SOUTH,
633        nec::BTN_EAST,
634        nec::BTN_C,
635        nec::BTN_NORTH,
636        nec::BTN_WEST,
637        nec::BTN_Z,
638        nec::BTN_LT,
639        nec::BTN_RT,
640        nec::BTN_LT2,
641        nec::BTN_RT2,
642        nec::BTN_SELECT,
643        nec::BTN_START,
644        nec::BTN_MODE,
645        nec::BTN_LTHUMB,
646        nec::BTN_RTHUMB,
647    ];
648
649    const AXES: [EvCode; 12] = [
650        nec::AXIS_LSTICKX,
651        nec::AXIS_LSTICKY,
652        nec::AXIS_LEFTZ,
653        nec::AXIS_RSTICKX,
654        nec::AXIS_RSTICKY,
655        nec::AXIS_RIGHTZ,
656        nec::AXIS_DPADX,
657        nec::AXIS_DPADY,
658        nec::AXIS_RT,
659        nec::AXIS_LT,
660        nec::AXIS_RT2,
661        nec::AXIS_LT2,
662    ];
663
664    #[test]
665    fn mapping() {
666        Mapping::parse_sdl_mapping(TEST_STR, &BUTTONS, &AXES).unwrap();
667    }
668
669    #[test]
670    fn from_data() {
671        let uuid = Uuid::nil();
672        let name = "Best Gamepad";
673        let buttons = BUTTONS.iter().cloned().map(ev::Code).collect::<Vec<_>>();
674        let axes = AXES.iter().cloned().map(ev::Code).collect::<Vec<_>>();
675
676        let mut data = MappingData::new();
677        data.insert_axis(axes[0], Axis::LeftStickX);
678        data.insert_axis(axes[1], Axis::LeftStickY);
679        data.insert_axis(axes[2], Axis::LeftZ);
680        data.insert_axis(axes[3], Axis::RightStickX);
681        data.insert_axis(axes[4], Axis::RightStickY);
682        data.insert_axis(axes[5], Axis::RightZ);
683
684        data.insert_btn(buttons[0], Button::South);
685        data.insert_btn(buttons[1], Button::East);
686        data.insert_btn(buttons[3], Button::North);
687        data.insert_btn(buttons[4], Button::West);
688        data.insert_btn(buttons[5], Button::Select);
689        data.insert_btn(buttons[6], Button::Start);
690        data.insert_btn(buttons[7], Button::DPadDown);
691        data.insert_btn(buttons[8], Button::DPadLeft);
692        data.insert_btn(buttons[9], Button::RightThumb);
693
694        let (mappings, sdl_mappings) =
695            Mapping::from_data(&data, &BUTTONS, &AXES, name, uuid).unwrap();
696        let sdl_mappings = Mapping::parse_sdl_mapping(&sdl_mappings, &BUTTONS, &AXES).unwrap();
697        assert_eq!(mappings, sdl_mappings);
698
699        let incorrect_mappings = Mapping::from_data(&data, &BUTTONS, &AXES, "Inval,id name", uuid);
700        assert_eq!(Err(MappingError::InvalidName), incorrect_mappings);
701
702        data.insert_btn(ev::Code(nec::BTN_DPAD_RIGHT), Button::DPadRight);
703        let incorrect_mappings = Mapping::from_data(&data, &BUTTONS, &AXES, name, uuid);
704        assert_eq!(
705            Err(MappingError::InvalidCode(ev::Code(nec::BTN_DPAD_RIGHT))),
706            incorrect_mappings
707        );
708
709        data.insert_btn(ev::Code(BUTTONS[3]), Button::Unknown);
710        let incorrect_mappings = Mapping::from_data(&data, &BUTTONS, &AXES, name, uuid);
711        assert_eq!(Err(MappingError::UnknownElement), incorrect_mappings);
712    }
713
714    #[test]
715    fn with_mappings() {
716        let mappings = format!(
717            "\nShould be ignored\nThis also should,be ignored\n\n{}",
718            TEST_STR
719        );
720        let mut db = MappingDb::new();
721        db.add_included_mappings();
722        db.insert(&mappings);
723
724        assert_eq!(
725            Some(TEST_STR),
726            db.get(Uuid::parse_str("03000000260900008888000000010001").unwrap())
727        );
728    }
729}