Buttplug sex toy control library
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::v1::NullDeviceMessageAttributesV1;
9use buttplug_core::message::{InputType, OutputType};
10use buttplug_server_device_config::ServerDeviceFeature;
11
12use getset::{Getters, MutGetters, Setters};
13use std::ops::RangeInclusive;
14
15#[derive(Clone, Debug, Default, PartialEq, Eq, Getters, MutGetters, Setters)]
16#[getset(get = "pub")]
17pub struct ServerDeviceMessageAttributesV3 {
18 // Generic commands
19 pub(in crate::message) scalar_cmd: Option<Vec<ServerGenericDeviceMessageAttributesV3>>,
20 pub(in crate::message) rotate_cmd: Option<Vec<ServerGenericDeviceMessageAttributesV3>>,
21 pub(in crate::message) linear_cmd: Option<Vec<ServerGenericDeviceMessageAttributesV3>>,
22
23 // Sensor Messages
24 pub(in crate::message) sensor_read_cmd: Option<Vec<ServerSensorDeviceMessageAttributesV3>>,
25 pub(in crate::message) sensor_subscribe_cmd: Option<Vec<ServerSensorDeviceMessageAttributesV3>>,
26
27 // StopDeviceCmd always exists
28 pub(in crate::message) stop_device_cmd: NullDeviceMessageAttributesV1,
29
30 // Needed to load from config for fallback, but unused here.
31 pub(in crate::message) fleshlight_launch_fw12_cmd: Option<NullDeviceMessageAttributesV1>,
32 pub(in crate::message) vorze_a10_cyclone_cmd: Option<NullDeviceMessageAttributesV1>,
33}
34
35#[derive(Clone, Debug, PartialEq, Eq, Getters, Setters)]
36#[getset(get = "pub")]
37pub struct ServerGenericDeviceMessageAttributesV3 {
38 pub(in crate::message) feature_descriptor: String,
39 pub(in crate::message) actuator_type: OutputType,
40 pub(in crate::message) step_count: u32,
41 pub(in crate::message) index: u32,
42 pub(in crate::message) feature: ServerDeviceFeature,
43}
44
45#[derive(Clone, Debug, PartialEq, Eq, Getters, Setters)]
46#[getset(get = "pub")]
47pub struct ServerSensorDeviceMessageAttributesV3 {
48 pub(in crate::message) feature_descriptor: String,
49 pub(in crate::message) sensor_type: InputType,
50 pub(in crate::message) sensor_range: Vec<RangeInclusive<i32>>,
51 pub(in crate::message) index: u32,
52 pub(in crate::message) feature: ServerDeviceFeature,
53}
54
55impl From<Vec<ServerDeviceFeature>> for ServerDeviceMessageAttributesV3 {
56 fn from(features: Vec<ServerDeviceFeature>) -> Self {
57 let scalar_attrs: Vec<ServerGenericDeviceMessageAttributesV3> = features
58 .iter()
59 .flat_map(|feature| {
60 let mut actuator_vec = vec![];
61 if let Some(output_map) = feature.output() {
62 let mut create_attribute = |actuator_type, step_count| {
63 let actuator_type = actuator_type;
64 let attrs = ServerGenericDeviceMessageAttributesV3 {
65 feature_descriptor: feature.description().to_owned(),
66 actuator_type,
67 step_count,
68 feature: feature.clone(),
69 index: 0,
70 };
71 actuator_vec.push(attrs)
72 };
73 // TODO oh come on just make a fucking iterator here. At least, once we figure out the
74 // unifying trait we can use to make an iterator on this.
75 if let Some(attr) = output_map.constrict().as_ref() {
76 create_attribute(OutputType::Constrict, attr.value().step_count())
77 }
78 if let Some(attr) = output_map.oscillate().as_ref() {
79 create_attribute(OutputType::Oscillate, attr.value().step_count())
80 }
81 if let Some(attr) = output_map.position().as_ref() {
82 create_attribute(OutputType::Position, attr.position().step_count())
83 }
84 if let Some(attr) = output_map.rotate().as_ref() {
85 create_attribute(OutputType::Rotate, attr.value().step_count())
86 }
87 if let Some(attr) = output_map.temperature().as_ref() {
88 create_attribute(OutputType::Temperature, attr.value().step_count())
89 }
90 if let Some(attr) = output_map.led().as_ref() {
91 create_attribute(OutputType::Led, attr.value().step_count())
92 }
93 if let Some(attr) = output_map.vibrate().as_ref() {
94 create_attribute(OutputType::Vibrate, attr.value().step_count())
95 }
96 if let Some(attr) = output_map.spray().as_ref() {
97 create_attribute(OutputType::Spray, attr.value().step_count())
98 }
99 }
100 actuator_vec
101 })
102 .collect();
103
104 // We have to calculate rotation attributes seperately, since they're a combination of
105 // feature type and message in >= v4.
106 let rotate_attrs: Vec<ServerGenericDeviceMessageAttributesV3> = features
107 .iter()
108 .flat_map(|feature| {
109 let mut actuator_vec = vec![];
110 if let Some(output_map) = feature.output()
111 && let Some(actuator) = output_map.rotate()
112 && *actuator.value().base().start() < 0
113 {
114 let actuator_type = OutputType::Rotate;
115 let step_count = actuator.value().step_count();
116 let attrs = ServerGenericDeviceMessageAttributesV3 {
117 feature_descriptor: feature.description().to_owned(),
118 actuator_type,
119 step_count,
120 feature: feature.clone(),
121 index: 0,
122 };
123 actuator_vec.push(attrs)
124 }
125 actuator_vec
126 })
127 .collect();
128
129 let linear_attrs: Vec<ServerGenericDeviceMessageAttributesV3> = features
130 .iter()
131 .flat_map(|feature| {
132 let mut actuator_vec = vec![];
133 if let Some(output_map) = feature.output()
134 && let Some(actuator) = output_map.position_with_duration()
135 {
136 let actuator_type = OutputType::Position;
137 let step_count = actuator.position().step_count();
138 let attrs = ServerGenericDeviceMessageAttributesV3 {
139 feature_descriptor: feature.description().to_owned(),
140 actuator_type,
141 step_count,
142 feature: feature.clone(),
143 index: 0,
144 };
145 actuator_vec.push(attrs)
146 }
147 actuator_vec
148 })
149 .collect();
150
151 let sensor_filter = {
152 let attrs: Vec<ServerSensorDeviceMessageAttributesV3> = features
153 .iter()
154 .map(|feature| {
155 let mut sensor_vec = vec![];
156 if let Some(sensor_map) = feature.input()
157 && let Some(battery) = sensor_map.battery()
158 {
159 // Only convert Battery backwards. Other sensors weren't really built for v3 and we
160 // never recommended using them or implemented much for them.
161 sensor_vec.push(ServerSensorDeviceMessageAttributesV3 {
162 feature_descriptor: feature.description().to_owned(),
163 sensor_type: InputType::Battery,
164 sensor_range: battery.value_range().clone(),
165 feature: feature.clone(),
166 index: 0,
167 });
168 }
169 sensor_vec
170 })
171 .flatten()
172 .collect();
173 if !attrs.is_empty() { Some(attrs) } else { None }
174 };
175
176 Self {
177 scalar_cmd: if scalar_attrs.is_empty() {
178 None
179 } else {
180 Some(scalar_attrs)
181 },
182 rotate_cmd: if rotate_attrs.is_empty() {
183 None
184 } else {
185 Some(rotate_attrs)
186 },
187 linear_cmd: if linear_attrs.is_empty() {
188 None
189 } else {
190 Some(linear_attrs)
191 },
192 sensor_read_cmd: sensor_filter,
193 sensor_subscribe_cmd: None,
194 ..Default::default()
195 }
196 }
197}