@@ -32,6 +32,24 @@ use getset::{CopyGetters, Getters};
3232
3333use super :: checked_output_cmd:: CheckedOutputCmdV4 ;
3434
35+ fn feature_index_for_id (
36+ attrs : & ServerDeviceAttributes ,
37+ feature_id : uuid:: Uuid ,
38+ command_name : & str ,
39+ ) -> Result < u32 , ButtplugError > {
40+ attrs
41+ . features ( )
42+ . iter ( )
43+ . find ( |( _, f) | f. id ( ) == feature_id)
44+ . map ( |( idx, _) | * idx)
45+ . ok_or_else ( || {
46+ ButtplugDeviceError :: DeviceConfigurationError ( format ! (
47+ "Feature {feature_id} referenced by {command_name} was not found in device attributes."
48+ ) )
49+ . into ( )
50+ } )
51+ }
52+
3553#[ derive( Debug , Default , PartialEq , Clone , Getters , CopyGetters ) ]
3654pub struct CheckedOutputVecCmdV4 {
3755 #[ getset( get_copy = "pub" ) ]
@@ -155,21 +173,16 @@ impl TryFromDeviceAttributes<VibrateCmdV1> for CheckedOutputVecCmdV4 {
155173
156174 let mut cmds: Vec < CheckedOutputCmdV4 > = vec ! [ ] ;
157175 for vibrate_cmd in msg. speeds ( ) {
158- if vibrate_cmd. index ( ) > vibrate_attributes. features ( ) . len ( ) as u32 {
159- return Err ( ButtplugError :: from (
160- ButtplugDeviceError :: DeviceFeatureCountMismatch (
176+ let feature = vibrate_attributes
177+ . features ( )
178+ . get ( vibrate_cmd. index ( ) as usize )
179+ . ok_or ( ButtplugError :: from (
180+ ButtplugDeviceError :: DeviceFeatureIndexError (
181+ vibrate_attributes. features ( ) . len ( ) as u32 ,
161182 vibrate_cmd. index ( ) ,
162- msg. speeds ( ) . len ( ) as u32 ,
163183 ) ,
164- ) ) ;
165- }
166- let feature = & vibrate_attributes. features ( ) [ vibrate_cmd. index ( ) as usize ] ;
167- let idx = features
168- . features ( )
169- . iter ( )
170- . find ( |( _, f) | f. id ( ) == feature. id ( ) )
171- . expect ( "Already checked existence" )
172- . 0 ;
184+ ) ) ?;
185+ let idx = feature_index_for_id ( features, feature. id ( ) , "VibrateCmdV1" ) ?;
173186 let actuator = feature. get_output ( OutputType :: Vibrate ) . ok_or (
174187 ButtplugDeviceError :: DeviceConfigurationError (
175188 "Device configuration does not have Vibrate actuator available." . to_owned ( ) ,
@@ -178,7 +191,7 @@ impl TryFromDeviceAttributes<VibrateCmdV1> for CheckedOutputVecCmdV4 {
178191 cmds. push ( CheckedOutputCmdV4 :: new (
179192 msg. id ( ) ,
180193 msg. device_index ( ) ,
181- * idx,
194+ idx,
182195 feature. id ( ) ,
183196 OutputCommand :: Vibrate ( OutputValue :: new (
184197 actuator
@@ -220,12 +233,7 @@ impl TryFromDeviceAttributes<ScalarCmdV3> for CheckedOutputVecCmdV4 {
220233 . ok_or ( ButtplugError :: from (
221234 ButtplugDeviceError :: DeviceFeatureIndexError ( scalar_attrs. len ( ) as u32 , cmd. index ( ) ) ,
222235 ) ) ?;
223- let idx = attrs
224- . features ( )
225- . iter ( )
226- . find ( |( _, f) | f. id ( ) == feature. feature ( ) . id ( ) )
227- . expect ( "Already proved existence" )
228- . 0 ;
236+ let idx = feature_index_for_id ( attrs, feature. feature ( ) . id ( ) , "ScalarCmdV3" ) ?;
229237 let output = feature
230238 . feature ( )
231239 . get_output ( cmd. actuator_type ( ) )
@@ -241,7 +249,7 @@ impl TryFromDeviceAttributes<ScalarCmdV3> for CheckedOutputVecCmdV4 {
241249 cmds. push ( CheckedOutputCmdV4 :: new (
242250 msg. id ( ) ,
243251 msg. device_index ( ) ,
244- * idx,
252+ idx,
245253 feature. feature . id ( ) ,
246254 OutputCommand :: from_output_type ( cmd. actuator_type ( ) , output_value) . unwrap ( ) ,
247255 ) ) ;
@@ -338,12 +346,7 @@ impl TryFromDeviceAttributes<RotateCmdV1> for CheckedOutputVecCmdV4 {
338346 . ok_or ( ButtplugError :: from (
339347 ButtplugDeviceError :: DeviceFeatureIndexError ( rotate_attrs. len ( ) as u32 , cmd. index ( ) ) ,
340348 ) ) ?;
341- let idx = attrs
342- . features ( )
343- . iter ( )
344- . find ( |( _, f) | f. id ( ) == feature. feature ( ) . id ( ) )
345- . expect ( "Already proved existence" )
346- . 0 ;
349+ let idx = feature_index_for_id ( attrs, feature. feature ( ) . id ( ) , "RotateCmdV1" ) ?;
347350 let actuator =
348351 feature
349352 . feature ( )
@@ -354,7 +357,7 @@ impl TryFromDeviceAttributes<RotateCmdV1> for CheckedOutputVecCmdV4 {
354357 cmds. push ( CheckedOutputCmdV4 :: new (
355358 msg. id ( ) ,
356359 msg. device_index ( ) ,
357- * idx,
360+ idx,
358361 feature. feature . id ( ) ,
359362 OutputCommand :: Rotate ( OutputValue :: new (
360363 actuator. calculate_from_float ( cmd. speed ( ) ) . map_err ( |_| {
@@ -373,3 +376,56 @@ impl TryFromDeviceAttributes<RotateCmdV1> for CheckedOutputVecCmdV4 {
373376 ) )
374377 }
375378}
379+
380+ #[ cfg( test) ]
381+ mod tests {
382+ use super :: * ;
383+ use crate :: message:: v1:: VibrateSubcommandV1 ;
384+ use buttplug_core:: util:: {
385+ range:: RangeInclusive ,
386+ small_vec_enum_map:: SmallVecEnumMap ,
387+ } ;
388+ use buttplug_server_device_config:: {
389+ RangeWithLimit ,
390+ ServerDeviceFeature ,
391+ ServerDeviceFeatureOutputValueProperties ,
392+ } ;
393+ use std:: collections:: BTreeMap ;
394+ use uuid:: Uuid ;
395+
396+ fn attrs_with_one_vibrate_feature ( ) -> ServerDeviceAttributes {
397+ let output = vec ! [ ServerDeviceFeatureOutput :: Vibrate (
398+ ServerDeviceFeatureOutputValueProperties :: new(
399+ RangeWithLimit :: new( RangeInclusive :: new( 0 , 100 ) ) ,
400+ false ,
401+ ) ,
402+ ) ]
403+ . into ( ) ;
404+ let input = SmallVecEnumMap :: default ( ) ;
405+ let feature = ServerDeviceFeature :: new (
406+ 0 ,
407+ "Vibrate" . to_owned ( ) ,
408+ Uuid :: new_v4 ( ) ,
409+ None ,
410+ None ,
411+ output,
412+ input,
413+ ) ;
414+ let mut features = BTreeMap :: new ( ) ;
415+ features. insert ( 0 , feature) ;
416+ ServerDeviceAttributes :: new ( & features)
417+ }
418+
419+ #[ test]
420+ fn legacy_vibrate_index_equal_to_feature_count_returns_error ( ) {
421+ let attrs = attrs_with_one_vibrate_feature ( ) ;
422+ let msg = VibrateCmdV1 :: new ( 0 , vec ! [ VibrateSubcommandV1 :: new( 1 , 0.5 ) ] ) ;
423+
424+ let result = CheckedOutputVecCmdV4 :: try_from_device_attributes ( msg, & attrs) ;
425+
426+ assert_eq ! (
427+ result. unwrap_err( ) ,
428+ ButtplugError :: from( ButtplugDeviceError :: DeviceFeatureIndexError ( 1 , 1 ) )
429+ ) ;
430+ }
431+ }
0 commit comments