cue_sdk/device/channel/
channels.rs1use cue_sdk_sys as ffi;
2
3use std::ptr::slice_from_raw_parts;
4
5use super::{Channel, ChannelFromFfiError};
6
7#[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}