Skip to content

Commit 0ec9c72

Browse files
committed
chore: Clean up client library and examples
1 parent 0efeac1 commit 0ec9c72

14 files changed

Lines changed: 169 additions & 241 deletions

File tree

crates/buttplug_client/src/device/command.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,25 +4,25 @@ use crate::ButtplugClientError;
44

55
#[derive(Debug, Clone, Copy)]
66
pub enum ClientDeviceCommandValue {
7-
Int(i32),
8-
Float(f64),
7+
Steps(i32),
8+
Percent(f64),
99
}
1010

1111
impl From<i32> for ClientDeviceCommandValue {
1212
fn from(val: i32) -> Self {
13-
ClientDeviceCommandValue::Int(val)
13+
ClientDeviceCommandValue::Steps(val)
1414
}
1515
}
1616

1717
impl From<u32> for ClientDeviceCommandValue {
1818
fn from(val: u32) -> Self {
19-
ClientDeviceCommandValue::Int(val as i32)
19+
ClientDeviceCommandValue::Steps(val as i32)
2020
}
2121
}
2222

2323
impl From<f64> for ClientDeviceCommandValue {
2424
fn from(val: f64) -> Self {
25-
ClientDeviceCommandValue::Float(val)
25+
ClientDeviceCommandValue::Percent(val)
2626
}
2727
}
2828

crates/buttplug_client/src/device/device.rs

Lines changed: 89 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -7,24 +7,21 @@
77

88
//! Representation and management of devices connected to the server.
99
10-
use crate::device::{ClientDeviceCommandValue, ClientDeviceOutputCommand};
10+
use crate::ButtplugClientError;
11+
use crate::device::ClientDeviceOutputCommand;
1112

1213
use crate::{
1314
ButtplugClientMessageSender,
1415
ButtplugClientResultFuture,
15-
create_boxed_future_client_error,
1616
device::ClientDeviceFeature,
1717
};
18-
use buttplug_core::message::InputType;
18+
use buttplug_core::message::{
19+
InputType,
20+
InputTypeReading,
21+
};
1922
use buttplug_core::{
2023
errors::ButtplugDeviceError,
21-
message::{
22-
ButtplugServerMessageV4,
23-
DeviceFeature,
24-
DeviceMessageInfoV4,
25-
OutputType,
26-
StopCmdV4,
27-
},
24+
message::{ButtplugServerMessageV4, DeviceFeature, DeviceMessageInfoV4, OutputType, StopCmdV4},
2825
util::stream::convert_broadcast_receiver_to_stream,
2926
};
3027
use futures::{FutureExt, Stream, future};
@@ -159,7 +156,7 @@ impl ButtplugClientDevice {
159156
)))
160157
}
161158

162-
fn filter_device_actuators(&self, actuator_type: OutputType) -> Vec<ClientDeviceFeature> {
159+
fn filter_device_outputs(&self, actuator_type: OutputType) -> Vec<ClientDeviceFeature> {
163160
self
164161
.device_features
165162
.iter()
@@ -179,7 +176,7 @@ impl ButtplugClientDevice {
179176
&self,
180177
client_device_command: &ClientDeviceOutputCommand,
181178
) -> ButtplugClientResultFuture {
182-
let features = self.filter_device_actuators(client_device_command.into());
179+
let features = self.filter_device_outputs(client_device_command.into());
183180
if features.is_empty() {
184181
// TODO err
185182
}
@@ -198,117 +195,111 @@ impl ButtplugClientDevice {
198195
.boxed()
199196
}
200197

201-
pub fn send_command(
202-
&self,
203-
client_device_command: &ClientDeviceOutputCommand,
204-
) -> ButtplugClientResultFuture {
205-
self.set_client_value(client_device_command)
206-
}
207-
208-
pub fn vibrate_features(&self) -> Vec<ClientDeviceFeature> {
209-
self.filter_device_actuators(OutputType::Vibrate)
210-
}
211-
212-
/// Commands device to vibrate, assuming it has the features to do so.
213-
pub fn vibrate(&self, level: impl Into<ClientDeviceCommandValue>) -> ButtplugClientResultFuture {
214-
self.set_client_value(&ClientDeviceOutputCommand::Vibrate(level.into()))
215-
}
216-
217-
pub fn oscillate(
218-
&self,
219-
level: impl Into<ClientDeviceCommandValue>,
220-
) -> ButtplugClientResultFuture {
221-
self.set_client_value(&ClientDeviceOutputCommand::Oscillate(level.into()))
222-
}
223-
224-
pub fn rotate(&self, level: impl Into<ClientDeviceCommandValue>) -> ButtplugClientResultFuture {
225-
self.set_client_value(&ClientDeviceOutputCommand::Rotate(level.into()))
226-
}
227-
228-
pub fn spray(&self, level: impl Into<ClientDeviceCommandValue>) -> ButtplugClientResultFuture {
229-
self.set_client_value(&ClientDeviceOutputCommand::Spray(level.into()))
230-
}
231-
232-
pub fn constrict(
233-
&self,
234-
level: impl Into<ClientDeviceCommandValue>,
235-
) -> ButtplugClientResultFuture {
236-
self.set_client_value(&ClientDeviceOutputCommand::Constrict(level.into()))
198+
pub fn outputs(&self, output_type: OutputType) -> Vec<ClientDeviceFeature> {
199+
self.filter_device_outputs(output_type)
237200
}
238201

239-
pub fn position(&self, level: impl Into<ClientDeviceCommandValue>) -> ButtplugClientResultFuture {
240-
self.set_client_value(&ClientDeviceOutputCommand::Position(level.into()))
202+
pub fn output_available(&self, output_type: OutputType) -> bool {
203+
!self.filter_device_outputs(output_type).is_empty()
241204
}
242205

243-
pub fn hw_position_with_duration(
206+
pub fn run_output(
244207
&self,
245-
position: impl Into<ClientDeviceCommandValue>,
246-
duration_in_ms: u32,
208+
client_device_command: &ClientDeviceOutputCommand,
247209
) -> ButtplugClientResultFuture {
248-
self.set_client_value(&ClientDeviceOutputCommand::HwPositionWithDuration(
249-
position.into(),
250-
duration_in_ms,
251-
))
210+
self.set_client_value(client_device_command)
252211
}
253212

254-
pub fn has_battery_level(&self) -> bool {
213+
pub fn input_available(&self, input_type: InputType) -> bool {
255214
self.device_features.iter().any(|x| {
256215
x.1
257216
.feature()
258217
.input()
259218
.as_ref()
260-
.is_some_and(|x| x.contains(InputType::Battery))
219+
.is_some_and(|x| x.contains(input_type))
261220
})
262221
}
263222

264-
pub fn battery_level(&self) -> ButtplugClientResultFuture<u32> {
265-
if let Some(battery) = self.device_features.iter().find(|x| {
266-
x.1
267-
.feature()
268-
.input()
269-
.as_ref()
270-
.is_some_and(|x| x.contains(InputType::Battery))
271-
}) {
272-
battery.1.battery_level()
223+
fn input_feature(
224+
&self,
225+
input_type: InputType,
226+
) -> Result<&ClientDeviceFeature, ButtplugClientError> {
227+
let inputs: Vec<_> = self
228+
.device_features
229+
.iter()
230+
.filter(|x| {
231+
x.1
232+
.feature()
233+
.input()
234+
.as_ref()
235+
.is_some_and(|x| x.contains(input_type))
236+
})
237+
.collect();
238+
let input_count = inputs.len();
239+
if input_count > 1 {
240+
Err(ButtplugClientError::ButtplugMultipleInputAvailableError(
241+
input_type,
242+
))
243+
} else if input_count == 0 {
244+
Err(ButtplugClientError::ButtplugError(
245+
ButtplugDeviceError::DeviceNoInputError(input_type).into(),
246+
))
273247
} else {
274-
create_boxed_future_client_error(
275-
ButtplugDeviceError::DeviceFeatureMismatch(
276-
"Device does not have battery feature available".to_owned(),
277-
)
278-
.into(),
279-
)
248+
Ok(inputs[0].1)
280249
}
281250
}
282251

283-
pub fn has_rssi_level(&self) -> bool {
284-
self.device_features.iter().any(|x| {
285-
x.1
286-
.feature()
287-
.input()
288-
.as_ref()
289-
.is_some_and(|x| x.contains(InputType::Rssi))
290-
})
252+
pub fn run_input_read(&self, input_type: InputType) -> ButtplugClientResultFuture<InputTypeReading> {
253+
match self.input_feature(input_type) {
254+
Ok(dev) => dev.run_input_read(input_type).boxed(),
255+
Err(e) => future::ready(Err(e)).boxed(),
256+
}
291257
}
292258

293-
pub fn rssi_level(&self) -> ButtplugClientResultFuture<i8> {
294-
if let Some(rssi) = self.device_features.iter().find(|x| {
295-
x.1
296-
.feature()
297-
.input()
298-
.as_ref()
299-
.is_some_and(|x| x.contains(InputType::Rssi))
300-
}) {
301-
rssi.1.rssi_level()
302-
} else {
303-
create_boxed_future_client_error(
304-
ButtplugDeviceError::DeviceFeatureMismatch(
305-
"Device does not have RSSI feature available".to_owned(),
306-
)
307-
.into(),
308-
)
259+
pub fn run_input_subscribe(&self, input_type: InputType) -> ButtplugClientResultFuture {
260+
match self.input_feature(input_type) {
261+
Ok(dev) => dev.run_input_subscribe(input_type).boxed(),
262+
Err(e) => future::ready(Err(e)).boxed(),
309263
}
310264
}
311265

266+
pub fn run_input_unsubscribe(&self, input_type: InputType) -> ButtplugClientResultFuture {
267+
match self.input_feature(input_type) {
268+
Ok(dev) => dev.run_input_unsubscribe(input_type).boxed(),
269+
Err(e) => future::ready(Err(e)).boxed(),
270+
}
271+
}
272+
273+
pub fn battery(&self) -> ButtplugClientResultFuture<u32> {
274+
let fut = self.run_input_read(InputType::Battery);
275+
async move {
276+
let val = fut.await?;
277+
if let InputTypeReading::Battery(x) = val {
278+
Ok(x.data().into())
279+
} else {
280+
Err(ButtplugClientError::ButtplugError(
281+
ButtplugDeviceError::DeviceNoInputError(val.into()).into(),
282+
))
283+
}
284+
}
285+
.boxed()
286+
}
287+
288+
pub fn rssi(&self) -> ButtplugClientResultFuture<i8> {
289+
let fut = self.run_input_read(InputType::Rssi);
290+
async move {
291+
let val = fut.await?;
292+
if let InputTypeReading::Rssi(x) = val {
293+
Ok(x.data().into())
294+
} else {
295+
Err(ButtplugClientError::ButtplugError(
296+
ButtplugDeviceError::DeviceNoInputError(val.into()).into(),
297+
))
298+
}
299+
}
300+
.boxed()
301+
}
302+
312303
/// Commands device to stop all movement.
313304
pub fn stop(&self) -> ButtplugClientResultFuture {
314305
// All devices accept StopDeviceCmd

0 commit comments

Comments
 (0)