1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
//! The GUID of joystick devices.

use std::{
    ffi::{CStr, CString},
    os::raw::c_int,
};

use crate::bind;

/// A GUID associated with joystick devices.
#[derive(Debug, Clone)]
pub struct Guid([u8; 16]);

impl Guid {
    /// Returns mapping string for the game controller having the GUID.
    #[must_use]
    pub fn mapping(&self) -> String {
        let ptr = unsafe {
            bind::SDL_GameControllerMappingForGUID(bind::SDL_JoystickGUID { data: self.0 })
        };
        let cstr = unsafe { CStr::from_ptr(ptr) };
        let ret = cstr.to_string_lossy().to_string();
        unsafe { bind::SDL_free(ptr.cast()) };
        ret
    }
}

impl std::fmt::Display for Guid {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        let mut buf = [0u8; 33];
        unsafe {
            bind::SDL_JoystickGetGUIDString(
                bind::SDL_JoystickGUID { data: self.0 },
                buf.as_mut_ptr().cast(),
                buf.len() as c_int,
            );
        }
        write!(f, "{}", String::from_utf8_lossy(&buf))
    }
}

/// An error to tell the failure on parsing `Guid` from string, showing that it is invalid length to convert into.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct InvalidLengthError {
    actual_length: usize,
}

impl std::fmt::Display for InvalidLengthError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(
            f,
            "length must equals to 16 but actual length was {}",
            self.actual_length
        )
    }
}

impl std::error::Error for InvalidLengthError {}

impl std::str::FromStr for Guid {
    type Err = InvalidLengthError;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        if s.len() != 16 {
            return Err(InvalidLengthError {
                actual_length: s.len(),
            });
        }
        let c_str = CString::new(s).expect("invalid string");
        let raw_guid = unsafe { bind::SDL_JoystickGetGUIDFromString(c_str.as_ptr()) };
        Ok(raw_guid.into())
    }
}

impl From<bind::SDL_JoystickGUID> for Guid {
    fn from(raw: bind::SDL_JoystickGUID) -> Self {
        Self(raw.data)
    }
}