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}