Buttplug sex toy control library
at dev 11 kB view raw
1// Buttplug Rust Source Code File - See https://buttplug.io for more info. 2// 3// Copyright 2016-2024 Nonpolynomial Labs LLC. All rights reserved. 4// 5// Licensed under the BSD 3-Clause license. See LICENSE file in the project root 6// for full license information. 7 8use crate::{message::InputCommandType, util::range_serialize::*}; 9use derive_builder::Builder; 10use getset::{CopyGetters, Getters, MutGetters, Setters}; 11use serde::{Deserialize, Serialize, Serializer, ser::SerializeSeq}; 12use std::{collections::HashSet, hash::Hash, ops::RangeInclusive}; 13 14#[derive( 15 Debug, Display, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Hash, EnumIter, EnumString, 16)] 17pub enum OutputType { 18 Unknown, 19 #[serde(alias = "vibrate")] 20 Vibrate, 21 #[serde(alias = "rotate")] 22 Rotate, 23 #[serde(alias = "oscillate")] 24 Oscillate, 25 #[serde(alias = "constrict")] 26 Constrict, 27 #[serde(alias = "temperature")] 28 Temperature, 29 #[serde(alias = "led")] 30 Led, 31 // For instances where we specify a position to move to ASAP. Usually servos, probably for the 32 // OSR-2/SR-6. 33 #[serde(alias = "position")] 34 Position, 35 #[serde(alias = "position_with_duration")] 36 PositionWithDuration, 37 // Lube shooters 38 #[serde(alias = "spray")] 39 Spray, 40 // Things we might add in the future 41 // Inflate, 42} 43 44#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Display, Hash, EnumIter)] 45pub enum InputType { 46 Unknown, 47 #[serde(alias = "battery")] 48 Battery, 49 #[serde(alias = "rssi")] 50 Rssi, 51 #[serde(alias = "button")] 52 Button, 53 #[serde(alias = "pressure")] 54 Pressure, 55 // Temperature, 56 // Accelerometer, 57 // Gyro, 58} 59 60// This will look almost exactly like ServerDeviceFeature. However, it will only contain 61// information we want the client to know, i.e. step counts versus specific step ranges. This is 62// what will be sent to the client as part of DeviceAdded/DeviceList messages. It should not be used 63// for outside configuration/serialization, rather it should be a subset of that information. 64// 65// For many messages, client and server configurations may be exactly the same. If they are not, 66// then we denote this by prefixing the type with Client/Server. Server attributes will usually be 67// hosted in the server/device/configuration module. 68#[derive( 69 Clone, Debug, Default, Getters, MutGetters, CopyGetters, Setters, Serialize, Deserialize, 70)] 71#[serde(rename_all = "PascalCase")] 72pub struct DeviceFeature { 73 // Index of the feature on the device. This was originally implicit as the position in the feature 74 // array. We now make it explicit even though it's still just array position, because implicit 75 // array positions have made life hell in so many different ways. 76 #[getset(get_copy = "pub")] 77 feature_index: u32, 78 #[getset(get = "pub", get_mut = "pub(super)")] 79 #[serde(default, rename = "FeatureDescription")] 80 description: String, 81 // TODO Maybe make this its own object instead of a HashMap? 82 #[getset(get = "pub")] 83 #[serde(skip_serializing_if = "Option::is_none")] 84 output: Option<DeviceFeatureOutput>, 85 #[getset(get = "pub")] 86 #[serde(skip_serializing_if = "Option::is_none")] 87 input: Option<DeviceFeatureInput>, 88} 89 90impl DeviceFeature { 91 pub fn new( 92 index: u32, 93 description: &str, 94 output: &Option<DeviceFeatureOutput>, 95 input: &Option<DeviceFeatureInput>, 96 ) -> Self { 97 Self { 98 feature_index: index, 99 description: description.to_owned(), 100 output: output.clone(), 101 input: input.clone(), 102 } 103 } 104} 105 106fn range_sequence_serialize<S>( 107 range_vec: &Vec<RangeInclusive<i32>>, 108 serializer: S, 109) -> Result<S::Ok, S::Error> 110where 111 S: Serializer, 112{ 113 let mut seq = serializer.serialize_seq(Some(range_vec.len()))?; 114 for range in range_vec { 115 seq.serialize_element(&vec![*range.start(), *range.end()])?; 116 } 117 seq.end() 118} 119 120pub trait DeviceFeatureOutputLimits { 121 fn step_count(&self) -> u32; 122 fn step_limit(&self) -> RangeInclusive<i32>; 123} 124 125#[derive(Serialize, Deserialize, Clone, Debug, Getters)] 126#[serde(rename_all = "PascalCase")] 127pub struct DeviceFeatureOutputValueProperties { 128 #[getset(get = "pub")] 129 #[serde(serialize_with = "range_serialize")] 130 value: RangeInclusive<i32>, 131} 132 133impl DeviceFeatureOutputValueProperties { 134 pub fn new(value: &RangeInclusive<i32>) -> Self { 135 DeviceFeatureOutputValueProperties { 136 value: value.clone(), 137 } 138 } 139 140 pub fn step_count(&self) -> u32 { 141 *self.value.end() as u32 142 } 143} 144 145impl DeviceFeatureOutputLimits for DeviceFeatureOutputValueProperties { 146 fn step_count(&self) -> u32 { 147 self.step_count() 148 } 149 fn step_limit(&self) -> RangeInclusive<i32> { 150 self.value.clone() 151 } 152} 153 154#[derive(Serialize, Deserialize, Clone, Debug, Getters)] 155#[serde(rename_all = "PascalCase")] 156pub struct DeviceFeatureOutputPositionWithDurationProperties { 157 #[getset(get = "pub")] 158 #[serde(serialize_with = "range_serialize")] 159 position: RangeInclusive<i32>, 160 #[getset(get = "pub")] 161 #[serde(serialize_with = "range_serialize")] 162 duration: RangeInclusive<i32>, 163} 164 165impl DeviceFeatureOutputPositionWithDurationProperties { 166 pub fn new(position: &RangeInclusive<i32>, duration: &RangeInclusive<i32>) -> Self { 167 DeviceFeatureOutputPositionWithDurationProperties { 168 position: position.clone(), 169 duration: duration.clone(), 170 } 171 } 172 173 pub fn step_count(&self) -> u32 { 174 *self.position.end() as u32 175 } 176} 177 178impl DeviceFeatureOutputLimits for DeviceFeatureOutputPositionWithDurationProperties { 179 fn step_count(&self) -> u32 { 180 self.step_count() 181 } 182 fn step_limit(&self) -> RangeInclusive<i32> { 183 self.position.clone() 184 } 185} 186 187#[derive(Clone, Debug, Getters, Setters, Default, Serialize, Deserialize, Builder)] 188#[builder(setter(strip_option), default)] 189#[getset(get = "pub")] 190#[serde(rename_all = "PascalCase")] 191pub struct DeviceFeatureOutput { 192 #[serde(skip_serializing_if = "Option::is_none")] 193 vibrate: Option<DeviceFeatureOutputValueProperties>, 194 #[serde(skip_serializing_if = "Option::is_none")] 195 rotate: Option<DeviceFeatureOutputValueProperties>, 196 #[serde(skip_serializing_if = "Option::is_none")] 197 oscillate: Option<DeviceFeatureOutputValueProperties>, 198 #[serde(skip_serializing_if = "Option::is_none")] 199 constrict: Option<DeviceFeatureOutputValueProperties>, 200 #[serde(skip_serializing_if = "Option::is_none")] 201 temperature: Option<DeviceFeatureOutputValueProperties>, 202 #[serde(skip_serializing_if = "Option::is_none")] 203 led: Option<DeviceFeatureOutputValueProperties>, 204 #[serde(skip_serializing_if = "Option::is_none")] 205 position: Option<DeviceFeatureOutputValueProperties>, 206 #[serde(skip_serializing_if = "Option::is_none")] 207 position_with_duration: Option<DeviceFeatureOutputPositionWithDurationProperties>, 208 #[serde(skip_serializing_if = "Option::is_none")] 209 spray: Option<DeviceFeatureOutputValueProperties>, 210} 211 212impl DeviceFeatureOutput { 213 pub fn contains(&self, output_type: OutputType) -> bool { 214 match output_type { 215 OutputType::Constrict => self.constrict.is_some(), 216 OutputType::Temperature => self.temperature.is_some(), 217 OutputType::Led => self.led.is_some(), 218 OutputType::Oscillate => self.oscillate.is_some(), 219 OutputType::Position => self.position.is_some(), 220 OutputType::PositionWithDuration => self.position_with_duration.is_some(), 221 OutputType::Rotate => self.rotate.is_some(), 222 OutputType::Spray => self.spray.is_some(), 223 OutputType::Unknown => false, 224 OutputType::Vibrate => self.vibrate.is_some(), 225 } 226 } 227 228 pub fn get(&self, output_type: OutputType) -> Option<&dyn DeviceFeatureOutputLimits> { 229 match output_type { 230 OutputType::Constrict => self 231 .constrict() 232 .as_ref() 233 .map(|x| x as &dyn DeviceFeatureOutputLimits), 234 OutputType::Temperature => self 235 .temperature() 236 .as_ref() 237 .map(|x| x as &dyn DeviceFeatureOutputLimits), 238 OutputType::Led => self 239 .led() 240 .as_ref() 241 .map(|x| x as &dyn DeviceFeatureOutputLimits), 242 OutputType::Oscillate => self 243 .oscillate() 244 .as_ref() 245 .map(|x| x as &dyn DeviceFeatureOutputLimits), 246 OutputType::Position => self 247 .position() 248 .as_ref() 249 .map(|x| x as &dyn DeviceFeatureOutputLimits), 250 OutputType::PositionWithDuration => self 251 .position_with_duration() 252 .as_ref() 253 .map(|x| x as &dyn DeviceFeatureOutputLimits), 254 OutputType::Rotate => self 255 .rotate() 256 .as_ref() 257 .map(|x| x as &dyn DeviceFeatureOutputLimits), 258 OutputType::Spray => self 259 .spray() 260 .as_ref() 261 .map(|x| x as &dyn DeviceFeatureOutputLimits), 262 OutputType::Unknown => None, 263 OutputType::Vibrate => self 264 .vibrate() 265 .as_ref() 266 .map(|x| x as &dyn DeviceFeatureOutputLimits), 267 } 268 } 269} 270 271#[derive( 272 Clone, Debug, Default, PartialEq, Eq, Getters, MutGetters, Setters, Serialize, Deserialize, 273)] 274#[serde(rename_all = "PascalCase")] 275pub struct DeviceFeatureInputProperties { 276 #[getset(get = "pub", get_mut = "pub(super)")] 277 #[serde(serialize_with = "range_sequence_serialize")] 278 value_range: Vec<RangeInclusive<i32>>, 279 #[getset(get = "pub")] 280 input_commands: HashSet<InputCommandType>, 281} 282 283impl DeviceFeatureInputProperties { 284 pub fn new( 285 value_range: &Vec<RangeInclusive<i32>>, 286 sensor_commands: &HashSet<InputCommandType>, 287 ) -> Self { 288 Self { 289 value_range: value_range.clone(), 290 input_commands: sensor_commands.clone(), 291 } 292 } 293} 294 295#[derive(Clone, Debug, Getters, Setters, Default, Serialize, Deserialize, Builder)] 296#[builder(setter(strip_option), default)] 297#[getset(get = "pub")] 298#[serde(rename_all = "PascalCase")] 299pub struct DeviceFeatureInput { 300 #[serde(skip_serializing_if = "Option::is_none")] 301 battery: Option<DeviceFeatureInputProperties>, 302 #[serde(skip_serializing_if = "Option::is_none")] 303 rssi: Option<DeviceFeatureInputProperties>, 304 #[serde(skip_serializing_if = "Option::is_none")] 305 pressure: Option<DeviceFeatureInputProperties>, 306 #[serde(skip_serializing_if = "Option::is_none")] 307 button: Option<DeviceFeatureInputProperties>, 308} 309 310impl DeviceFeatureInput { 311 pub fn contains(&self, input_type: InputType) -> bool { 312 match input_type { 313 InputType::Battery => self.battery.is_some(), 314 InputType::Rssi => self.rssi.is_some(), 315 InputType::Pressure => self.pressure.is_some(), 316 InputType::Button => self.button.is_some(), 317 InputType::Unknown => false, 318 } 319 } 320 321 pub fn get(&self, input_type: InputType) -> &Option<DeviceFeatureInputProperties> { 322 match input_type { 323 InputType::Battery => self.battery(), 324 InputType::Rssi => self.rssi(), 325 InputType::Pressure => self.pressure(), 326 InputType::Button => self.button(), 327 InputType::Unknown => &None, 328 } 329 } 330}