Buttplug sex toy control library
at master 390 lines 14 kB view raw
1use std::{collections::HashMap, fmt::Debug}; 2 3use crate::message::{ 4 ButtplugClientMessageVariant, 5 RequestServerInfoV1, 6 ServerDeviceAttributes, 7 TryFromDeviceAttributes, 8 server_device_attributes::TryFromClientMessage, 9 v0::ButtplugClientMessageV0, 10 v1::ButtplugClientMessageV1, 11 v2::ButtplugClientMessageV2, 12 v3::ButtplugClientMessageV3, 13}; 14use buttplug_core::{ 15 errors::{ButtplugDeviceError, ButtplugError, ButtplugMessageError}, 16 message::{ 17 ButtplugClientMessageV4, 18 ButtplugDeviceMessage, 19 ButtplugMessage, 20 ButtplugMessageFinalizer, 21 ButtplugMessageValidator, 22 PingV0, 23 RequestDeviceListV0, 24 RequestServerInfoV4, 25 StartScanningV0, 26 StopAllDevicesV0, 27 StopDeviceCmdV0, 28 StopScanningV0, 29 }, 30}; 31 32use super::{ 33 checked_input_cmd::CheckedInputCmdV4, 34 checked_output_cmd::CheckedOutputCmdV4, 35 checked_output_vec_cmd::CheckedOutputVecCmdV4, 36}; 37 38/// An CheckedClientMessage has had its contents verified and should need no further error/validity 39/// checking. Processing may still return errors, but should be due to system state, not message 40/// contents. 41/// 42/// There should only be one version of CheckedClientMessage in the library, matching the latest 43/// version of the message spec. For any messages that don't require error checking, their regular 44/// struct can be used as an enum parameter. Any messages requiring error checking or validation 45/// will have an alternate Checked[x] form that they will need to be cast as. 46#[derive( 47 Debug, 48 Clone, 49 PartialEq, 50 ButtplugMessage, 51 ButtplugMessageValidator, 52 ButtplugMessageFinalizer, 53 FromSpecificButtplugMessage, 54)] 55pub enum ButtplugCheckedClientMessageV4 { 56 // Handshake messages 57 RequestServerInfo(RequestServerInfoV4), 58 Ping(PingV0), 59 // Device enumeration messages 60 StartScanning(StartScanningV0), 61 StopScanning(StopScanningV0), 62 RequestDeviceList(RequestDeviceListV0), 63 // Generic commands 64 StopDeviceCmd(StopDeviceCmdV0), 65 StopAllDevices(StopAllDevicesV0), 66 OutputCmd(CheckedOutputCmdV4), 67 // Sensor commands 68 InputCmd(CheckedInputCmdV4), 69 // Internal conversions for v1-v3 messages with subcommands 70 OutputVecCmd(CheckedOutputVecCmdV4), 71} 72 73impl TryFromClientMessage<ButtplugClientMessageV4> for ButtplugCheckedClientMessageV4 { 74 fn try_from_client_message( 75 value: ButtplugClientMessageV4, 76 feature_map: &HashMap<u32, ServerDeviceAttributes>, 77 ) -> Result<Self, ButtplugError> { 78 match value { 79 // Messages that don't need checking 80 ButtplugClientMessageV4::RequestServerInfo(m) => { 81 Ok(ButtplugCheckedClientMessageV4::RequestServerInfo(m)) 82 } 83 ButtplugClientMessageV4::Ping(m) => Ok(ButtplugCheckedClientMessageV4::Ping(m)), 84 ButtplugClientMessageV4::StartScanning(m) => { 85 Ok(ButtplugCheckedClientMessageV4::StartScanning(m)) 86 } 87 ButtplugClientMessageV4::StopScanning(m) => { 88 Ok(ButtplugCheckedClientMessageV4::StopScanning(m)) 89 } 90 ButtplugClientMessageV4::RequestDeviceList(m) => { 91 Ok(ButtplugCheckedClientMessageV4::RequestDeviceList(m)) 92 } 93 ButtplugClientMessageV4::StopAllDevices(m) => { 94 Ok(ButtplugCheckedClientMessageV4::StopAllDevices(m)) 95 } 96 97 // Messages that need device index checking 98 ButtplugClientMessageV4::StopDeviceCmd(m) => { 99 if feature_map.get(&m.device_index()).is_some() { 100 Ok(ButtplugCheckedClientMessageV4::StopDeviceCmd(m)) 101 } else { 102 Err(ButtplugError::from( 103 ButtplugDeviceError::DeviceNotAvailable(m.device_index()), 104 )) 105 } 106 } 107 108 // Message that need device index and feature checking 109 ButtplugClientMessageV4::OutputCmd(m) => { 110 if let Some(features) = feature_map.get(&m.device_index()) { 111 Ok(ButtplugCheckedClientMessageV4::OutputCmd( 112 CheckedOutputCmdV4::try_from_device_attributes(m, features)?, 113 )) 114 } else { 115 Err(ButtplugError::from( 116 ButtplugDeviceError::DeviceNotAvailable(m.device_index()), 117 )) 118 } 119 } 120 ButtplugClientMessageV4::InputCmd(m) => { 121 if let Some(features) = feature_map.get(&m.device_index()) { 122 Ok(ButtplugCheckedClientMessageV4::InputCmd( 123 CheckedInputCmdV4::try_from_device_attributes(m, features)?, 124 )) 125 } else { 126 Err(ButtplugError::from( 127 ButtplugDeviceError::DeviceNotAvailable(m.device_index()), 128 )) 129 } 130 } 131 } 132 } 133} 134 135impl From<RequestServerInfoV1> for RequestServerInfoV4 { 136 fn from(value: RequestServerInfoV1) -> Self { 137 let mut msg = RequestServerInfoV4::new(value.client_name(), value.message_version(), 0); 138 msg.set_id(value.id()); 139 msg 140 } 141} 142 143// For v3 to v4, all deprecations should be treated as conversions, but will require current 144// connected device state, meaning they'll need to be implemented where they can also access the 145// device manager. 146impl TryFrom<ButtplugClientMessageV3> for ButtplugCheckedClientMessageV4 { 147 type Error = ButtplugMessageError; 148 149 fn try_from(value: ButtplugClientMessageV3) -> Result<Self, Self::Error> { 150 match value { 151 ButtplugClientMessageV3::Ping(m) => Ok(ButtplugCheckedClientMessageV4::Ping(m.clone())), 152 ButtplugClientMessageV3::RequestServerInfo(m) => Ok( 153 ButtplugCheckedClientMessageV4::RequestServerInfo(RequestServerInfoV4::from(m)), 154 ), 155 ButtplugClientMessageV3::StartScanning(m) => { 156 Ok(ButtplugCheckedClientMessageV4::StartScanning(m.clone())) 157 } 158 ButtplugClientMessageV3::StopScanning(m) => { 159 Ok(ButtplugCheckedClientMessageV4::StopScanning(m.clone())) 160 } 161 ButtplugClientMessageV3::RequestDeviceList(m) => { 162 Ok(ButtplugCheckedClientMessageV4::RequestDeviceList(m.clone())) 163 } 164 ButtplugClientMessageV3::StopAllDevices(m) => { 165 Ok(ButtplugCheckedClientMessageV4::StopAllDevices(m.clone())) 166 } 167 ButtplugClientMessageV3::StopDeviceCmd(m) => { 168 Ok(ButtplugCheckedClientMessageV4::StopDeviceCmd(m.clone())) 169 } 170 _ => Err(ButtplugMessageError::MessageConversionError(format!( 171 "Cannot convert message {value:?} to V4 message spec while lacking state." 172 ))), 173 } 174 } 175} 176 177impl TryFromClientMessage<ButtplugClientMessageVariant> for ButtplugCheckedClientMessageV4 { 178 fn try_from_client_message( 179 msg: ButtplugClientMessageVariant, 180 features: &HashMap<u32, ServerDeviceAttributes>, 181 ) -> Result<Self, buttplug_core::errors::ButtplugError> { 182 let id = msg.id(); 183 let mut converted_msg = match msg { 184 ButtplugClientMessageVariant::V0(m) => Self::try_from_client_message(m, features), 185 ButtplugClientMessageVariant::V1(m) => Self::try_from_client_message(m, features), 186 ButtplugClientMessageVariant::V2(m) => Self::try_from_client_message(m, features), 187 ButtplugClientMessageVariant::V3(m) => Self::try_from_client_message(m, features), 188 ButtplugClientMessageVariant::V4(m) => Self::try_from_client_message(m, features), 189 }?; 190 // Always make sure the ID is set after conversion 191 converted_msg.set_id(id); 192 Ok(converted_msg) 193 } 194} 195 196impl TryFromClientMessage<ButtplugClientMessageV0> for ButtplugCheckedClientMessageV4 { 197 fn try_from_client_message( 198 msg: ButtplugClientMessageV0, 199 features: &HashMap<u32, ServerDeviceAttributes>, 200 ) -> Result<Self, ButtplugError> { 201 // All v0 messages can be converted to v1 messages. 202 Self::try_from_client_message(ButtplugClientMessageV1::from(msg), features) 203 } 204} 205 206fn check_device_index_and_convert<T, U>( 207 msg: T, 208 features: &HashMap<u32, ServerDeviceAttributes>, 209) -> Result<U, ButtplugError> 210where 211 T: ButtplugDeviceMessage + Debug, 212 U: TryFromDeviceAttributes<T> + Debug, 213{ 214 // Vorze and RotateCmd are equivalent, so this is an ok conversion. 215 if let Some(attrs) = features.get(&msg.device_index()) { 216 Ok(U::try_from_device_attributes(msg.clone(), attrs)?) 217 } else { 218 Err(ButtplugError::from( 219 ButtplugDeviceError::DeviceNotAvailable(msg.device_index()), 220 )) 221 } 222} 223 224impl TryFromClientMessage<ButtplugClientMessageV1> for ButtplugCheckedClientMessageV4 { 225 fn try_from_client_message( 226 msg: ButtplugClientMessageV1, 227 features: &HashMap<u32, ServerDeviceAttributes>, 228 ) -> Result<Self, ButtplugError> { 229 // Instead of converting to v2 message attributes then to v4 device features, we move directly 230 // from v0 command messages to v4 device features here. There's no reason to do the middle step. 231 match msg { 232 ButtplugClientMessageV1::VorzeA10CycloneCmd(_) => { 233 // Vorze and RotateCmd are equivalent, so this is an ok conversion. 234 Err(ButtplugError::ButtplugMessageError(ButtplugMessageError::MessageConversionError("VorzeA10CycloneCmd is considered unused, and no longer supported. If you are seeing this message and need VorzeA10CycloneCmd, file an issue in the Buttplug repo.".to_owned()))) 235 } 236 ButtplugClientMessageV1::SingleMotorVibrateCmd(m) => { 237 Ok(check_device_index_and_convert::<_, CheckedOutputVecCmdV4>(m, features)?.into()) 238 } 239 _ => Self::try_from_client_message(ButtplugClientMessageV2::try_from(msg)?, features), 240 } 241 } 242} 243 244impl TryFromClientMessage<ButtplugClientMessageV2> for ButtplugCheckedClientMessageV4 { 245 fn try_from_client_message( 246 msg: ButtplugClientMessageV2, 247 features: &HashMap<u32, ServerDeviceAttributes>, 248 ) -> Result<Self, ButtplugError> { 249 match msg { 250 // Convert v2 specific queries to v3 generic sensor queries 251 ButtplugClientMessageV2::BatteryLevelCmd(m) => { 252 Ok(check_device_index_and_convert::<_, CheckedInputCmdV4>(m, features)?.into()) 253 } 254 // Convert VibrateCmd to a ScalarCmd command 255 ButtplugClientMessageV2::VibrateCmd(m) => { 256 Ok(check_device_index_and_convert::<_, CheckedOutputVecCmdV4>(m, features)?.into()) 257 } 258 _ => Self::try_from_client_message(ButtplugClientMessageV3::try_from(msg)?, features), 259 } 260 } 261} 262 263impl TryFromClientMessage<ButtplugClientMessageV3> for ButtplugCheckedClientMessageV4 { 264 fn try_from_client_message( 265 msg: ButtplugClientMessageV3, 266 features: &HashMap<u32, ServerDeviceAttributes>, 267 ) -> Result<Self, ButtplugError> { 268 match msg { 269 // Convert v1/v2 message attribute commands into device feature commands 270 ButtplugClientMessageV3::VibrateCmd(m) => { 271 Ok(check_device_index_and_convert::<_, CheckedOutputVecCmdV4>(m, features)?.into()) 272 } 273 ButtplugClientMessageV3::ScalarCmd(m) => { 274 Ok(check_device_index_and_convert::<_, CheckedOutputVecCmdV4>(m, features)?.into()) 275 } 276 ButtplugClientMessageV3::RotateCmd(m) => { 277 Ok(check_device_index_and_convert::<_, CheckedOutputVecCmdV4>(m, features)?.into()) 278 } 279 ButtplugClientMessageV3::LinearCmd(m) => { 280 Ok(check_device_index_and_convert::<_, CheckedOutputVecCmdV4>(m, features)?.into()) 281 } 282 ButtplugClientMessageV3::SensorReadCmd(m) => { 283 Ok(check_device_index_and_convert::<_, CheckedInputCmdV4>(m, features)?.into()) 284 } 285 ButtplugClientMessageV3::SensorSubscribeCmd(_) => { 286 // Always reject v3 sub/unsub. It was never implemented or indexed correctly. 287 Err(ButtplugError::from( 288 ButtplugDeviceError::MessageNotSupported("SensorSubscribeCmdV3".to_owned()), 289 )) 290 } 291 ButtplugClientMessageV3::SensorUnsubscribeCmd(_) => { 292 // Always reject v3 sub/unsub. It was never implemented or indexed correctly. 293 Err(ButtplugError::from( 294 ButtplugDeviceError::MessageNotSupported("SensorUnsubscribeCmdV3".to_owned()), 295 )) 296 } 297 _ => { 298 ButtplugCheckedClientMessageV4::try_from(msg).map_err(|e: ButtplugMessageError| e.into()) 299 } 300 } 301 } 302} 303 304/// Represents messages that should go to the 305/// [DeviceManager][crate::server::device_manager::DeviceManager] of a 306/// [ButtplugServer](crate::server::ButtplugServer) 307#[derive( 308 Debug, 309 Clone, 310 PartialEq, 311 Eq, 312 ButtplugMessage, 313 ButtplugMessageValidator, 314 ButtplugMessageFinalizer, 315 FromSpecificButtplugMessage, 316)] 317pub(crate) enum ButtplugDeviceManagerMessageUnion { 318 RequestDeviceList(RequestDeviceListV0), 319 StopAllDevices(StopAllDevicesV0), 320 StartScanning(StartScanningV0), 321 StopScanning(StopScanningV0), 322} 323 324impl TryFrom<ButtplugCheckedClientMessageV4> for ButtplugDeviceManagerMessageUnion { 325 type Error = (); 326 327 fn try_from(value: ButtplugCheckedClientMessageV4) -> Result<Self, Self::Error> { 328 match value { 329 ButtplugCheckedClientMessageV4::RequestDeviceList(m) => { 330 Ok(ButtplugDeviceManagerMessageUnion::RequestDeviceList(m)) 331 } 332 ButtplugCheckedClientMessageV4::StopAllDevices(m) => { 333 Ok(ButtplugDeviceManagerMessageUnion::StopAllDevices(m)) 334 } 335 ButtplugCheckedClientMessageV4::StartScanning(m) => { 336 Ok(ButtplugDeviceManagerMessageUnion::StartScanning(m)) 337 } 338 ButtplugCheckedClientMessageV4::StopScanning(m) => { 339 Ok(ButtplugDeviceManagerMessageUnion::StopScanning(m)) 340 } 341 _ => Err(()), 342 } 343 } 344} 345 346/// Represents all possible device command message types. 347#[derive( 348 Debug, 349 Clone, 350 PartialEq, 351 ButtplugDeviceMessage, 352 ButtplugMessageValidator, 353 ButtplugMessageFinalizer, 354 FromSpecificButtplugMessage, 355)] 356pub enum ButtplugDeviceCommandMessageUnionV4 { 357 StopDeviceCmd(StopDeviceCmdV0), 358 OutputCmd(CheckedOutputCmdV4), 359 OutputVecCmd(CheckedOutputVecCmdV4), 360 InputCmd(CheckedInputCmdV4), 361} 362 363impl TryFrom<ButtplugCheckedClientMessageV4> for ButtplugDeviceCommandMessageUnionV4 { 364 type Error = (); 365 366 fn try_from(value: ButtplugCheckedClientMessageV4) -> Result<Self, Self::Error> { 367 match value { 368 ButtplugCheckedClientMessageV4::StopDeviceCmd(m) => { 369 Ok(ButtplugDeviceCommandMessageUnionV4::StopDeviceCmd(m)) 370 } 371 ButtplugCheckedClientMessageV4::OutputCmd(m) => { 372 Ok(ButtplugDeviceCommandMessageUnionV4::OutputCmd(m)) 373 } 374 ButtplugCheckedClientMessageV4::OutputVecCmd(m) => { 375 Ok(ButtplugDeviceCommandMessageUnionV4::OutputVecCmd(m)) 376 } 377 ButtplugCheckedClientMessageV4::InputCmd(m) => { 378 Ok(ButtplugDeviceCommandMessageUnionV4::InputCmd(m)) 379 } 380 _ => Err(()), 381 } 382 } 383} 384 385#[derive(Copy, Debug, Clone, PartialEq, Eq, Hash, Display)] 386pub enum ButtplugDeviceMessageNameV4 { 387 StopDeviceCmd, 388 InputCmd, 389 OutputCmd, 390}