Buttplug sex toy control library
1use std::{collections::HashSet, ops::RangeInclusive}; 2 3use crate::{ 4 ButtplugDeviceConfigError, 5 RangeWithLimit, 6 ServerDeviceFeature, 7 ServerDeviceFeatureInput, 8 ServerDeviceFeatureInputProperties, 9 ServerDeviceFeatureOutput, 10 ServerDeviceFeatureOutputPositionProperties, 11 ServerDeviceFeatureOutputPositionWithDurationProperties, 12 ServerDeviceFeatureOutputValueProperties, 13}; 14use buttplug_core::{ 15 message::InputCommandType, 16 util::range_serialize::{option_range_serialize, range_sequence_serialize, range_serialize}, 17}; 18use getset::{CopyGetters, Getters, MutGetters, Setters}; 19use serde::{Deserialize, Serialize}; 20use uuid::Uuid; 21 22#[derive(Serialize, Deserialize, Debug, Clone, Default, CopyGetters)] 23pub struct BaseFeatureSettings { 24 #[serde(skip_serializing_if = "Option::is_none", default)] 25 #[getset(get_copy = "pub")] 26 alt_protocol_index: Option<u32>, 27} 28 29impl BaseFeatureSettings { 30 pub fn is_none(&self) -> bool { 31 self.alt_protocol_index.is_none() 32 } 33} 34 35#[derive(Serialize, Deserialize, Clone, Debug)] 36struct BaseDeviceFeatureOutputValueProperties { 37 #[serde(serialize_with = "range_serialize")] 38 value: RangeInclusive<i32>, 39} 40 41impl From<BaseDeviceFeatureOutputValueProperties> for ServerDeviceFeatureOutputValueProperties { 42 fn from(val: BaseDeviceFeatureOutputValueProperties) -> Self { 43 ServerDeviceFeatureOutputValueProperties::new(&val.value.into(), false) 44 } 45} 46 47#[derive(Serialize, Deserialize, Clone, Debug)] 48struct BaseDeviceFeatureOutputPositionProperties { 49 #[serde(serialize_with = "range_serialize")] 50 value: RangeInclusive<i32>, 51} 52 53impl From<BaseDeviceFeatureOutputPositionProperties> 54 for ServerDeviceFeatureOutputPositionProperties 55{ 56 fn from(val: BaseDeviceFeatureOutputPositionProperties) -> Self { 57 ServerDeviceFeatureOutputPositionProperties::new(&val.value.into(), false, false) 58 } 59} 60 61#[derive(Serialize, Deserialize, Clone, Debug)] 62struct BaseDeviceFeatureOutputPositionWithDurationProperties { 63 #[serde(serialize_with = "range_serialize")] 64 position: RangeInclusive<i32>, 65 #[serde(serialize_with = "range_serialize")] 66 duration: RangeInclusive<i32>, 67} 68 69impl From<BaseDeviceFeatureOutputPositionWithDurationProperties> 70 for ServerDeviceFeatureOutputPositionWithDurationProperties 71{ 72 fn from(val: BaseDeviceFeatureOutputPositionWithDurationProperties) -> Self { 73 ServerDeviceFeatureOutputPositionWithDurationProperties::new( 74 &val.position.into(), 75 &val.duration.into(), 76 false, 77 false, 78 ) 79 } 80} 81 82#[derive(Serialize, Deserialize, Clone, Debug)] 83struct BaseDeviceFeatureOutput { 84 #[serde(skip_serializing_if = "Option::is_none")] 85 vibrate: Option<BaseDeviceFeatureOutputValueProperties>, 86 #[serde(skip_serializing_if = "Option::is_none")] 87 rotate: Option<BaseDeviceFeatureOutputValueProperties>, 88 #[serde(skip_serializing_if = "Option::is_none")] 89 oscillate: Option<BaseDeviceFeatureOutputValueProperties>, 90 #[serde(skip_serializing_if = "Option::is_none")] 91 constrict: Option<BaseDeviceFeatureOutputValueProperties>, 92 #[serde(skip_serializing_if = "Option::is_none")] 93 temperature: Option<BaseDeviceFeatureOutputValueProperties>, 94 #[serde(skip_serializing_if = "Option::is_none")] 95 led: Option<BaseDeviceFeatureOutputValueProperties>, 96 #[serde(skip_serializing_if = "Option::is_none")] 97 position: Option<BaseDeviceFeatureOutputPositionProperties>, 98 #[serde(skip_serializing_if = "Option::is_none")] 99 position_with_duration: Option<BaseDeviceFeatureOutputPositionWithDurationProperties>, 100 #[serde(skip_serializing_if = "Option::is_none")] 101 spray: Option<BaseDeviceFeatureOutputValueProperties>, 102} 103 104impl From<BaseDeviceFeatureOutput> for ServerDeviceFeatureOutput { 105 fn from(val: BaseDeviceFeatureOutput) -> Self { 106 let mut output = ServerDeviceFeatureOutput::default(); 107 if let Some(vibrate) = val.vibrate { 108 output.set_vibrate(Some(vibrate.into())); 109 } 110 if let Some(rotate) = val.rotate { 111 output.set_rotate(Some(rotate.into())); 112 } 113 if let Some(oscillate) = val.oscillate { 114 output.set_oscillate(Some(oscillate.into())); 115 } 116 if let Some(constrict) = val.constrict { 117 output.set_constrict(Some(constrict.into())); 118 } 119 if let Some(temperature) = val.temperature { 120 output.set_temperature(Some(temperature.into())); 121 } 122 if let Some(led) = val.led { 123 output.set_led(Some(led.into())); 124 } 125 if let Some(position) = val.position { 126 output.set_position(Some(position.into())); 127 } 128 if let Some(position_with_duration) = val.position_with_duration { 129 output.set_position_with_duration(Some(position_with_duration.into())); 130 } 131 if let Some(spray) = val.spray { 132 output.set_spray(Some(spray.into())); 133 } 134 output 135 } 136} 137 138#[derive(Serialize, Deserialize, Clone, Debug)] 139struct UserDeviceFeatureOutputValueProperties { 140 #[serde( 141 skip_serializing_if = "Option::is_none", 142 serialize_with = "option_range_serialize" 143 )] 144 value: Option<RangeInclusive<u32>>, 145 #[serde(default)] 146 disabled: bool, 147} 148 149impl UserDeviceFeatureOutputValueProperties { 150 pub fn with_base_properties( 151 &self, 152 base: &ServerDeviceFeatureOutputValueProperties, 153 ) -> Result<ServerDeviceFeatureOutputValueProperties, ButtplugDeviceConfigError> { 154 let range = RangeWithLimit::try_new(base.value().base(), &self.value)?; 155 Ok(ServerDeviceFeatureOutputValueProperties::new( 156 &range, 157 self.disabled, 158 )) 159 } 160} 161 162impl From<&ServerDeviceFeatureOutputValueProperties> for UserDeviceFeatureOutputValueProperties { 163 fn from(value: &ServerDeviceFeatureOutputValueProperties) -> Self { 164 Self { 165 value: value.value().user().clone(), 166 disabled: value.disabled(), 167 } 168 } 169} 170 171#[derive(Serialize, Deserialize, Clone, Debug)] 172struct UserDeviceFeatureOutputPositionProperties { 173 #[serde( 174 skip_serializing_if = "Option::is_none", 175 serialize_with = "option_range_serialize" 176 )] 177 value: Option<RangeInclusive<u32>>, 178 #[serde(default)] 179 disabled: bool, 180 #[serde(default)] 181 reverse: bool, 182} 183 184impl UserDeviceFeatureOutputPositionProperties { 185 pub fn with_base_properties( 186 &self, 187 base: &ServerDeviceFeatureOutputPositionProperties, 188 ) -> Result<ServerDeviceFeatureOutputPositionProperties, ButtplugDeviceConfigError> { 189 let value = RangeWithLimit::try_new(base.position().base(), &self.value)?; 190 Ok(ServerDeviceFeatureOutputPositionProperties::new( 191 &value, 192 self.disabled, 193 self.reverse, 194 )) 195 } 196} 197 198impl From<&ServerDeviceFeatureOutputPositionProperties> 199 for UserDeviceFeatureOutputPositionProperties 200{ 201 fn from(value: &ServerDeviceFeatureOutputPositionProperties) -> Self { 202 Self { 203 value: value.position().user().clone(), 204 reverse: value.reverse_position(), 205 disabled: value.disabled(), 206 } 207 } 208} 209 210#[derive(Serialize, Deserialize, Clone, Debug)] 211struct UserDeviceFeatureOutputPositionWithDurationProperties { 212 #[serde( 213 skip_serializing_if = "Option::is_none", 214 serialize_with = "option_range_serialize" 215 )] 216 position: Option<RangeInclusive<u32>>, 217 #[serde( 218 skip_serializing_if = "Option::is_none", 219 serialize_with = "option_range_serialize" 220 )] 221 duration: Option<RangeInclusive<u32>>, 222 #[serde(default)] 223 disabled: bool, 224 #[serde(default)] 225 reverse: bool, 226} 227 228impl UserDeviceFeatureOutputPositionWithDurationProperties { 229 pub fn with_base_properties( 230 &self, 231 base: &ServerDeviceFeatureOutputPositionWithDurationProperties, 232 ) -> Result<ServerDeviceFeatureOutputPositionWithDurationProperties, ButtplugDeviceConfigError> 233 { 234 let position = RangeWithLimit::try_new(base.position().base(), &self.position)?; 235 let duration = RangeWithLimit::try_new(base.duration().base(), &self.duration)?; 236 Ok( 237 ServerDeviceFeatureOutputPositionWithDurationProperties::new( 238 &position, 239 &duration, 240 self.disabled, 241 self.reverse, 242 ), 243 ) 244 } 245} 246 247impl From<&ServerDeviceFeatureOutputPositionWithDurationProperties> 248 for UserDeviceFeatureOutputPositionWithDurationProperties 249{ 250 fn from(value: &ServerDeviceFeatureOutputPositionWithDurationProperties) -> Self { 251 Self { 252 position: value.position().user().clone(), 253 duration: value.duration().user().clone(), 254 reverse: value.reverse_position(), 255 disabled: value.disabled(), 256 } 257 } 258} 259 260#[derive(Serialize, Deserialize, Clone, Debug)] 261struct UserDeviceFeatureOutput { 262 #[serde(skip_serializing_if = "Option::is_none")] 263 vibrate: Option<UserDeviceFeatureOutputValueProperties>, 264 #[serde(skip_serializing_if = "Option::is_none")] 265 rotate: Option<UserDeviceFeatureOutputValueProperties>, 266 #[serde(skip_serializing_if = "Option::is_none")] 267 oscillate: Option<UserDeviceFeatureOutputValueProperties>, 268 #[serde(skip_serializing_if = "Option::is_none")] 269 constrict: Option<UserDeviceFeatureOutputValueProperties>, 270 #[serde(skip_serializing_if = "Option::is_none")] 271 temperature: Option<UserDeviceFeatureOutputValueProperties>, 272 #[serde(skip_serializing_if = "Option::is_none")] 273 led: Option<UserDeviceFeatureOutputValueProperties>, 274 #[serde(skip_serializing_if = "Option::is_none")] 275 position: Option<UserDeviceFeatureOutputPositionProperties>, 276 #[serde(skip_serializing_if = "Option::is_none")] 277 position_with_duration: Option<UserDeviceFeatureOutputPositionWithDurationProperties>, 278 #[serde(skip_serializing_if = "Option::is_none")] 279 spray: Option<UserDeviceFeatureOutputValueProperties>, 280} 281 282impl UserDeviceFeatureOutput { 283 pub fn with_base_output( 284 &self, 285 base_output: &ServerDeviceFeatureOutput, 286 ) -> Result<ServerDeviceFeatureOutput, ButtplugDeviceConfigError> { 287 let mut output = ServerDeviceFeatureOutput::default(); 288 // TODO Flip logic and output errors if user has something base doesn't, or vice versa. 289 if let Some(base_vibrate) = base_output.vibrate() { 290 if let Some(user_vibrate) = &self.vibrate { 291 output.set_vibrate(Some(user_vibrate.with_base_properties(base_vibrate)?)); 292 } else { 293 output.set_vibrate(base_output.vibrate().clone()); 294 } 295 } 296 if let Some(user_rotate) = &self.rotate { 297 if let Some(base_rotate) = base_output.rotate() { 298 output.set_rotate(Some(user_rotate.with_base_properties(base_rotate)?)); 299 } else { 300 output.set_rotate(base_output.rotate().clone()); 301 } 302 } 303 if let Some(user_oscillate) = &self.oscillate { 304 if let Some(base_oscillate) = base_output.oscillate() { 305 output.set_oscillate(Some(user_oscillate.with_base_properties(base_oscillate)?)); 306 } else { 307 output.set_oscillate(base_output.oscillate().clone()); 308 } 309 } 310 if let Some(user_constrict) = &self.constrict { 311 if let Some(base_constrict) = base_output.constrict() { 312 output.set_constrict(Some(user_constrict.with_base_properties(base_constrict)?)); 313 } else { 314 output.set_constrict(base_output.constrict().clone()); 315 } 316 } 317 if let Some(user_temperature) = &self.temperature { 318 if let Some(base_temperature) = base_output.temperature() { 319 output.set_temperature(Some(user_temperature.with_base_properties(base_temperature)?)); 320 } else { 321 output.set_temperature(base_output.temperature().clone()); 322 } 323 } 324 if let Some(user_led) = &self.led { 325 if let Some(base_led) = base_output.led() { 326 output.set_led(Some(user_led.with_base_properties(base_led)?)); 327 } else { 328 output.set_led(base_output.led().clone()); 329 } 330 } 331 if let Some(user_spray) = &self.spray { 332 if let Some(base_spray) = base_output.spray() { 333 output.set_spray(Some(user_spray.with_base_properties(base_spray)?)); 334 } else { 335 output.set_spray(base_output.spray().clone()); 336 } 337 } 338 if let Some(user) = &self.position { 339 if let Some(base) = base_output.position() { 340 output.set_position(Some(user.with_base_properties(base)?)); 341 } else { 342 output.set_position(base_output.position().clone()); 343 } 344 } 345 if let Some(user) = &self.position_with_duration { 346 if let Some(base) = base_output.position_with_duration() { 347 output.set_position_with_duration(Some(user.with_base_properties(base)?)); 348 } else { 349 output.set_position_with_duration(base_output.position_with_duration().clone()); 350 } 351 } 352 Ok(output) 353 } 354} 355 356impl From<&ServerDeviceFeatureOutput> for UserDeviceFeatureOutput { 357 fn from(value: &ServerDeviceFeatureOutput) -> Self { 358 Self { 359 vibrate: value.vibrate().as_ref().map(|x| x.into()), 360 rotate: value.rotate().as_ref().map(|x| x.into()), 361 oscillate: value.oscillate().as_ref().map(|x| x.into()), 362 constrict: value.constrict().as_ref().map(|x| x.into()), 363 temperature: value.temperature().as_ref().map(|x| x.into()), 364 led: value.led().as_ref().map(|x| x.into()), 365 position: value.position().as_ref().map(|x| x.into()), 366 position_with_duration: value.position_with_duration().as_ref().map(|x| x.into()), 367 spray: value.spray().as_ref().map(|x| x.into()), 368 } 369 } 370} 371 372#[derive( 373 Clone, Debug, Default, PartialEq, Eq, Getters, MutGetters, Setters, Serialize, Deserialize, 374)] 375pub struct DeviceFeatureInputProperties { 376 #[getset(get = "pub", get_mut = "pub(super)")] 377 #[serde(serialize_with = "range_sequence_serialize")] 378 value_range: Vec<RangeInclusive<i32>>, 379 #[getset(get = "pub")] 380 input_commands: HashSet<InputCommandType>, 381} 382 383impl DeviceFeatureInputProperties { 384 pub fn new( 385 value_range: &Vec<RangeInclusive<i32>>, 386 sensor_commands: &HashSet<InputCommandType>, 387 ) -> Self { 388 Self { 389 value_range: value_range.clone(), 390 input_commands: sensor_commands.clone(), 391 } 392 } 393} 394 395impl From<DeviceFeatureInputProperties> for ServerDeviceFeatureInputProperties { 396 fn from(val: DeviceFeatureInputProperties) -> Self { 397 ServerDeviceFeatureInputProperties::new(&val.value_range, &val.input_commands) 398 } 399} 400 401#[derive(Clone, Debug, Default, Getters, Serialize, Deserialize)] 402#[getset(get = "pub")] 403pub struct DeviceFeatureInput { 404 battery: Option<DeviceFeatureInputProperties>, 405 rssi: Option<DeviceFeatureInputProperties>, 406 pressure: Option<DeviceFeatureInputProperties>, 407 button: Option<DeviceFeatureInputProperties>, 408} 409 410impl From<DeviceFeatureInput> for ServerDeviceFeatureInput { 411 fn from(val: DeviceFeatureInput) -> Self { 412 let mut input = ServerDeviceFeatureInput::default(); 413 if let Some(battery) = val.battery { 414 input.set_battery(Some(battery.into())); 415 } 416 if let Some(rssi) = val.rssi { 417 input.set_rssi(Some(rssi.into())); 418 } 419 if let Some(pressure) = val.pressure { 420 input.set_pressure(Some(pressure.into())); 421 } 422 if let Some(button) = val.button { 423 input.set_button(Some(button.into())); 424 } 425 input 426 } 427} 428 429#[derive(Clone, Debug, Default, Getters, Serialize, Deserialize, CopyGetters)] 430pub struct ConfigBaseDeviceFeature { 431 #[getset(get = "pub")] 432 #[serde(default)] 433 description: String, 434 #[getset(get = "pub")] 435 #[serde(skip_serializing_if = "Option::is_none")] 436 output: Option<BaseDeviceFeatureOutput>, 437 #[getset(get = "pub")] 438 #[serde(skip_serializing_if = "Option::is_none")] 439 input: Option<DeviceFeatureInput>, 440 #[getset(get_copy = "pub")] 441 id: Uuid, 442 #[getset(get = "pub")] 443 #[serde(skip_serializing_if = "BaseFeatureSettings::is_none", default)] 444 feature_settings: BaseFeatureSettings, 445} 446 447impl From<ConfigBaseDeviceFeature> for ServerDeviceFeature { 448 fn from(val: ConfigBaseDeviceFeature) -> Self { 449 // This isn't resolving correctly using .and_then, so having to do it the long way? 450 let output: Option<ServerDeviceFeatureOutput> = val.output.map(|o| o.into()); 451 let input: Option<ServerDeviceFeatureInput> = val.input.map(|i| i.into()); 452 ServerDeviceFeature::new( 453 &val.description, 454 val.id, 455 None, 456 val.feature_settings.alt_protocol_index, 457 &output, 458 &input, 459 ) 460 } 461} 462 463#[derive(Clone, Debug, Default, Getters, Serialize, Deserialize, CopyGetters)] 464pub struct ConfigUserDeviceFeature { 465 #[getset(get_copy = "pub")] 466 id: Uuid, 467 #[getset(get_copy = "pub")] 468 base_id: Uuid, 469 #[getset(get = "pub")] 470 #[serde(rename = "output", skip_serializing_if = "Option::is_none")] 471 output: Option<UserDeviceFeatureOutput>, 472} 473 474impl ConfigUserDeviceFeature { 475 pub fn with_base_feature( 476 &self, 477 base_feature: &ServerDeviceFeature, 478 ) -> Result<ServerDeviceFeature, ButtplugDeviceConfigError> { 479 let output = if let Some(o) = &self.output { 480 if let Some(base) = base_feature.output() { 481 Some(o.with_base_output(base)?) 482 } else { 483 None 484 } 485 } else { 486 None 487 }; 488 Ok(ServerDeviceFeature::new( 489 base_feature.description(), 490 self.id, 491 Some(self.base_id), 492 base_feature.alt_protocol_index(), 493 &output, 494 base_feature.input(), 495 )) 496 } 497} 498 499impl From<&ServerDeviceFeature> for ConfigUserDeviceFeature { 500 fn from(value: &ServerDeviceFeature) -> Self { 501 Self { 502 id: value.id(), 503 base_id: value 504 .base_id() 505 .unwrap_or_else(|| panic!("Should have base id: {:?}", value)), 506 output: value.output().as_ref().map(|x| x.into()), 507 } 508 } 509}