cue_sdk/device/channel/
channels.rs

1use cue_sdk_sys as ffi;
2
3use std::ptr::slice_from_raw_parts;
4
5use super::{Channel, ChannelFromFfiError};
6
7/// The various errors that can happen when reading all of the channels in a given `DeviceInfo` from the
8/// iCUE SDK.
9#[derive(Debug, Clone, Fail, PartialEq)]
10pub enum ChannelsFromFfiError {
11    #[fail(
12        display = "Received invalid (negative) count of channels, count: {}",
13        _0
14    )]
15    InvalidChannelsCount(i32),
16    #[fail(display = "Channels pointer was null.")]
17    NullChannelsPointer,
18    #[fail(display = "Channels from ffi failed: {:?}", _0)]
19    ChannelFromFFIErrors(Vec<ChannelFromFfiError>),
20}
21
22pub(crate) fn channels_from_ffi(
23    info: ffi::CorsairChannelsInfo,
24) -> Result<Vec<Channel>, ChannelsFromFfiError> {
25    if info.channelsCount == 0 {
26        return Ok(vec![]);
27    }
28    if info.channelsCount < 0 {
29        return Err(ChannelsFromFfiError::InvalidChannelsCount(
30            info.channelsCount,
31        ));
32    }
33    if info.channels.is_null() {
34        return Err(ChannelsFromFfiError::NullChannelsPointer);
35    }
36
37    let num_channels = info.channelsCount as usize;
38
39    let channels = unsafe { &(*slice_from_raw_parts(info.channels, num_channels)) };
40
41    let mut errs = Vec::<ChannelFromFfiError>::with_capacity(num_channels);
42    let mut channels_out = Vec::<Channel>::with_capacity(num_channels);
43
44    for c in channels.iter() {
45        match Channel::from_ffi(c) {
46            Ok(channel) => channels_out.push(channel),
47            Err(e) => errs.push(e),
48        }
49    }
50
51    if errs.is_empty() {
52        Ok(channels_out)
53    } else {
54        Err(ChannelsFromFfiError::ChannelFromFFIErrors(errs))
55    }
56}
57
58#[cfg(test)]
59mod tests {
60    use cue_sdk_sys as ffi;
61
62    use std::ptr;
63
64    use super::Channel;
65
66    use super::super::{channels_from_ffi, ChannelDevice, ChannelDeviceType, ChannelsFromFfiError};
67
68    #[test]
69    fn channels_from_ffi_channels_count_zero() {
70        let info = ffi::CorsairChannelsInfo {
71            channelsCount: 0,
72            channels: ptr::null_mut(),
73        };
74
75        let result = channels_from_ffi(info);
76        assert_eq!(result.unwrap(), vec![]);
77    }
78
79    #[test]
80    fn channels_from_ffi_invalid_channels_count() {
81        let info = ffi::CorsairChannelsInfo {
82            channelsCount: -1,
83            channels: ptr::null_mut(),
84        };
85
86        let result = channels_from_ffi(info);
87        assert_eq!(
88            result.unwrap_err(),
89            ChannelsFromFfiError::InvalidChannelsCount(-1)
90        );
91    }
92
93    #[test]
94    fn channels_from_ffi_channels_ptr_null() {
95        let info = ffi::CorsairChannelsInfo {
96            channelsCount: 5,
97            channels: ptr::null_mut(),
98        };
99
100        let result = channels_from_ffi(info);
101        assert_eq!(
102            result.unwrap_err(),
103            ChannelsFromFfiError::NullChannelsPointer
104        );
105    }
106
107    #[test]
108    fn channels_from_ffi_channels_some_channels_invalid() {
109        let mut chans = [
110            ffi::CorsairChannelInfo {
111                totalLedsCount: 23,
112                devicesCount: 0,
113                devices: ptr::null_mut(),
114            },
115            ffi::CorsairChannelInfo {
116                totalLedsCount: -1,
117                devicesCount: 0,
118                devices: ptr::null_mut(),
119            },
120        ];
121
122        let info = ffi::CorsairChannelsInfo {
123            channelsCount: 2,
124            channels: chans.as_mut_ptr(),
125        };
126
127        let result = channels_from_ffi(info);
128        assert!(
129            matches!(result.unwrap_err(), ChannelsFromFfiError::ChannelFromFFIErrors(errs) if errs.len() == 1)
130        );
131    }
132
133    #[test]
134    fn channels_from_ffi_channels_all_channels_invalid() {
135        let mut chans = [
136            ffi::CorsairChannelInfo {
137                totalLedsCount: -234,
138                devicesCount: 0,
139                devices: ptr::null_mut(),
140            },
141            ffi::CorsairChannelInfo {
142                totalLedsCount: -1,
143                devicesCount: 0,
144                devices: ptr::null_mut(),
145            },
146            ffi::CorsairChannelInfo {
147                totalLedsCount: -5,
148                devicesCount: 0,
149                devices: ptr::null_mut(),
150            },
151            ffi::CorsairChannelInfo {
152                totalLedsCount: -25,
153                devicesCount: 0,
154                devices: ptr::null_mut(),
155            },
156        ];
157
158        let info = ffi::CorsairChannelsInfo {
159            channelsCount: 4,
160            channels: chans.as_mut_ptr(),
161        };
162
163        let result = channels_from_ffi(info);
164        assert!(
165            matches!(result.unwrap_err(), ChannelsFromFfiError::ChannelFromFFIErrors(errs) if errs.len() == 4)
166        );
167    }
168
169    #[test]
170    fn channels_from_ffi_channels_all_valid() {
171        let mut devices = [
172            ffi::CorsairChannelDeviceInfo {
173                deviceLedCount: 43,
174                type_: ffi::CorsairChannelDeviceType_CCDT_ML_Fan,
175            },
176            ffi::CorsairChannelDeviceInfo {
177                deviceLedCount: 2,
178                type_: ffi::CorsairChannelDeviceType_CCDT_QL_Fan,
179            },
180        ];
181
182        let mut chans = [
183            ffi::CorsairChannelInfo {
184                totalLedsCount: 23,
185                devicesCount: 0,
186                devices: ptr::null_mut(),
187            },
188            ffi::CorsairChannelInfo {
189                totalLedsCount: 9,
190                devicesCount: 2,
191                devices: devices.as_mut_ptr(),
192            },
193        ];
194
195        let info = ffi::CorsairChannelsInfo {
196            channelsCount: 2,
197            channels: chans.as_mut_ptr(),
198        };
199
200        let result = channels_from_ffi(info);
201        assert_eq!(
202            result.unwrap(),
203            vec![
204                Channel {
205                    total_led_count: 23,
206                    devices: vec![],
207                },
208                Channel {
209                    total_led_count: 9,
210                    devices: vec![
211                        ChannelDevice {
212                            device_type: Some(ChannelDeviceType::MlFan),
213                            device_led_count: 43,
214                        },
215                        ChannelDevice {
216                            device_type: Some(ChannelDeviceType::QlFan),
217                            device_led_count: 2,
218                        }
219                    ],
220                }
221            ]
222        );
223    }
224}