77
88//! Representation and management of devices connected to the server.
99
10- use crate :: device:: { ClientDeviceCommandValue , ClientDeviceOutputCommand } ;
10+ use crate :: ButtplugClientError ;
11+ use crate :: device:: ClientDeviceOutputCommand ;
1112
1213use crate :: {
1314 ButtplugClientMessageSender ,
1415 ButtplugClientResultFuture ,
15- create_boxed_future_client_error,
1616 device:: ClientDeviceFeature ,
1717} ;
18- use buttplug_core:: message:: InputType ;
18+ use buttplug_core:: message:: {
19+ InputType ,
20+ InputTypeReading ,
21+ } ;
1922use buttplug_core:: {
2023 errors:: ButtplugDeviceError ,
21- message:: {
22- ButtplugServerMessageV4 ,
23- DeviceFeature ,
24- DeviceMessageInfoV4 ,
25- OutputType ,
26- StopCmdV4 ,
27- } ,
24+ message:: { ButtplugServerMessageV4 , DeviceFeature , DeviceMessageInfoV4 , OutputType , StopCmdV4 } ,
2825 util:: stream:: convert_broadcast_receiver_to_stream,
2926} ;
3027use futures:: { FutureExt , Stream , future} ;
@@ -159,7 +156,7 @@ impl ButtplugClientDevice {
159156 ) ) )
160157 }
161158
162- fn filter_device_actuators ( & self , actuator_type : OutputType ) -> Vec < ClientDeviceFeature > {
159+ fn filter_device_outputs ( & self , actuator_type : OutputType ) -> Vec < ClientDeviceFeature > {
163160 self
164161 . device_features
165162 . iter ( )
@@ -179,7 +176,7 @@ impl ButtplugClientDevice {
179176 & self ,
180177 client_device_command : & ClientDeviceOutputCommand ,
181178 ) -> ButtplugClientResultFuture {
182- let features = self . filter_device_actuators ( client_device_command. into ( ) ) ;
179+ let features = self . filter_device_outputs ( client_device_command. into ( ) ) ;
183180 if features. is_empty ( ) {
184181 // TODO err
185182 }
@@ -198,117 +195,111 @@ impl ButtplugClientDevice {
198195 . boxed ( )
199196 }
200197
201- pub fn send_command (
202- & self ,
203- client_device_command : & ClientDeviceOutputCommand ,
204- ) -> ButtplugClientResultFuture {
205- self . set_client_value ( client_device_command)
206- }
207-
208- pub fn vibrate_features ( & self ) -> Vec < ClientDeviceFeature > {
209- self . filter_device_actuators ( OutputType :: Vibrate )
210- }
211-
212- /// Commands device to vibrate, assuming it has the features to do so.
213- pub fn vibrate ( & self , level : impl Into < ClientDeviceCommandValue > ) -> ButtplugClientResultFuture {
214- self . set_client_value ( & ClientDeviceOutputCommand :: Vibrate ( level. into ( ) ) )
215- }
216-
217- pub fn oscillate (
218- & self ,
219- level : impl Into < ClientDeviceCommandValue > ,
220- ) -> ButtplugClientResultFuture {
221- self . set_client_value ( & ClientDeviceOutputCommand :: Oscillate ( level. into ( ) ) )
222- }
223-
224- pub fn rotate ( & self , level : impl Into < ClientDeviceCommandValue > ) -> ButtplugClientResultFuture {
225- self . set_client_value ( & ClientDeviceOutputCommand :: Rotate ( level. into ( ) ) )
226- }
227-
228- pub fn spray ( & self , level : impl Into < ClientDeviceCommandValue > ) -> ButtplugClientResultFuture {
229- self . set_client_value ( & ClientDeviceOutputCommand :: Spray ( level. into ( ) ) )
230- }
231-
232- pub fn constrict (
233- & self ,
234- level : impl Into < ClientDeviceCommandValue > ,
235- ) -> ButtplugClientResultFuture {
236- self . set_client_value ( & ClientDeviceOutputCommand :: Constrict ( level. into ( ) ) )
198+ pub fn outputs ( & self , output_type : OutputType ) -> Vec < ClientDeviceFeature > {
199+ self . filter_device_outputs ( output_type)
237200 }
238201
239- pub fn position ( & self , level : impl Into < ClientDeviceCommandValue > ) -> ButtplugClientResultFuture {
240- self . set_client_value ( & ClientDeviceOutputCommand :: Position ( level . into ( ) ) )
202+ pub fn output_available ( & self , output_type : OutputType ) -> bool {
203+ ! self . filter_device_outputs ( output_type ) . is_empty ( )
241204 }
242205
243- pub fn hw_position_with_duration (
206+ pub fn run_output (
244207 & self ,
245- position : impl Into < ClientDeviceCommandValue > ,
246- duration_in_ms : u32 ,
208+ client_device_command : & ClientDeviceOutputCommand ,
247209 ) -> ButtplugClientResultFuture {
248- self . set_client_value ( & ClientDeviceOutputCommand :: HwPositionWithDuration (
249- position. into ( ) ,
250- duration_in_ms,
251- ) )
210+ self . set_client_value ( client_device_command)
252211 }
253212
254- pub fn has_battery_level ( & self ) -> bool {
213+ pub fn input_available ( & self , input_type : InputType ) -> bool {
255214 self . device_features . iter ( ) . any ( |x| {
256215 x. 1
257216 . feature ( )
258217 . input ( )
259218 . as_ref ( )
260- . is_some_and ( |x| x. contains ( InputType :: Battery ) )
219+ . is_some_and ( |x| x. contains ( input_type ) )
261220 } )
262221 }
263222
264- pub fn battery_level ( & self ) -> ButtplugClientResultFuture < u32 > {
265- if let Some ( battery) = self . device_features . iter ( ) . find ( |x| {
266- x. 1
267- . feature ( )
268- . input ( )
269- . as_ref ( )
270- . is_some_and ( |x| x. contains ( InputType :: Battery ) )
271- } ) {
272- battery. 1 . battery_level ( )
223+ fn input_feature (
224+ & self ,
225+ input_type : InputType ,
226+ ) -> Result < & ClientDeviceFeature , ButtplugClientError > {
227+ let inputs: Vec < _ > = self
228+ . device_features
229+ . iter ( )
230+ . filter ( |x| {
231+ x. 1
232+ . feature ( )
233+ . input ( )
234+ . as_ref ( )
235+ . is_some_and ( |x| x. contains ( input_type) )
236+ } )
237+ . collect ( ) ;
238+ let input_count = inputs. len ( ) ;
239+ if input_count > 1 {
240+ Err ( ButtplugClientError :: ButtplugMultipleInputAvailableError (
241+ input_type,
242+ ) )
243+ } else if input_count == 0 {
244+ Err ( ButtplugClientError :: ButtplugError (
245+ ButtplugDeviceError :: DeviceNoInputError ( input_type) . into ( ) ,
246+ ) )
273247 } else {
274- create_boxed_future_client_error (
275- ButtplugDeviceError :: DeviceFeatureMismatch (
276- "Device does not have battery feature available" . to_owned ( ) ,
277- )
278- . into ( ) ,
279- )
248+ Ok ( inputs[ 0 ] . 1 )
280249 }
281250 }
282251
283- pub fn has_rssi_level ( & self ) -> bool {
284- self . device_features . iter ( ) . any ( |x| {
285- x. 1
286- . feature ( )
287- . input ( )
288- . as_ref ( )
289- . is_some_and ( |x| x. contains ( InputType :: Rssi ) )
290- } )
252+ pub fn run_input_read ( & self , input_type : InputType ) -> ButtplugClientResultFuture < InputTypeReading > {
253+ match self . input_feature ( input_type) {
254+ Ok ( dev) => dev. run_input_read ( input_type) . boxed ( ) ,
255+ Err ( e) => future:: ready ( Err ( e) ) . boxed ( ) ,
256+ }
291257 }
292258
293- pub fn rssi_level ( & self ) -> ButtplugClientResultFuture < i8 > {
294- if let Some ( rssi) = self . device_features . iter ( ) . find ( |x| {
295- x. 1
296- . feature ( )
297- . input ( )
298- . as_ref ( )
299- . is_some_and ( |x| x. contains ( InputType :: Rssi ) )
300- } ) {
301- rssi. 1 . rssi_level ( )
302- } else {
303- create_boxed_future_client_error (
304- ButtplugDeviceError :: DeviceFeatureMismatch (
305- "Device does not have RSSI feature available" . to_owned ( ) ,
306- )
307- . into ( ) ,
308- )
259+ pub fn run_input_subscribe ( & self , input_type : InputType ) -> ButtplugClientResultFuture {
260+ match self . input_feature ( input_type) {
261+ Ok ( dev) => dev. run_input_subscribe ( input_type) . boxed ( ) ,
262+ Err ( e) => future:: ready ( Err ( e) ) . boxed ( ) ,
309263 }
310264 }
311265
266+ pub fn run_input_unsubscribe ( & self , input_type : InputType ) -> ButtplugClientResultFuture {
267+ match self . input_feature ( input_type) {
268+ Ok ( dev) => dev. run_input_unsubscribe ( input_type) . boxed ( ) ,
269+ Err ( e) => future:: ready ( Err ( e) ) . boxed ( ) ,
270+ }
271+ }
272+
273+ pub fn battery ( & self ) -> ButtplugClientResultFuture < u32 > {
274+ let fut = self . run_input_read ( InputType :: Battery ) ;
275+ async move {
276+ let val = fut. await ?;
277+ if let InputTypeReading :: Battery ( x) = val {
278+ Ok ( x. data ( ) . into ( ) )
279+ } else {
280+ Err ( ButtplugClientError :: ButtplugError (
281+ ButtplugDeviceError :: DeviceNoInputError ( val. into ( ) ) . into ( ) ,
282+ ) )
283+ }
284+ }
285+ . boxed ( )
286+ }
287+
288+ pub fn rssi ( & self ) -> ButtplugClientResultFuture < i8 > {
289+ let fut = self . run_input_read ( InputType :: Rssi ) ;
290+ async move {
291+ let val = fut. await ?;
292+ if let InputTypeReading :: Rssi ( x) = val {
293+ Ok ( x. data ( ) . into ( ) )
294+ } else {
295+ Err ( ButtplugClientError :: ButtplugError (
296+ ButtplugDeviceError :: DeviceNoInputError ( val. into ( ) ) . into ( ) ,
297+ ) )
298+ }
299+ }
300+ . boxed ( )
301+ }
302+
312303 /// Commands device to stop all movement.
313304 pub fn stop ( & self ) -> ButtplugClientResultFuture {
314305 // All devices accept StopDeviceCmd
0 commit comments