Buttplug sex toy control library
at master 136 lines 3.9 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::{ServerDeviceAttributes, TryFromDeviceAttributes}; 9use buttplug_core::{ 10 errors::{ButtplugDeviceError, ButtplugError, ButtplugMessageError}, 11 message::{ 12 ButtplugDeviceMessage, 13 ButtplugMessage, 14 ButtplugMessageFinalizer, 15 ButtplugMessageValidator, 16 OutputCmdV4, 17 OutputCommand, 18 }, 19}; 20 21use getset::{CopyGetters, Getters}; 22use uuid::Uuid; 23 24use super::spec_enums::ButtplugDeviceMessageNameV4; 25 26#[derive( 27 Debug, ButtplugDeviceMessage, ButtplugMessageFinalizer, Clone, Getters, CopyGetters, Eq, 28)] 29#[getset(get_copy = "pub")] 30pub struct CheckedOutputCmdV4 { 31 id: u32, 32 device_index: u32, 33 feature_index: u32, 34 feature_id: Uuid, 35 output_command: OutputCommand, 36} 37 38impl PartialEq for CheckedOutputCmdV4 { 39 fn eq(&self, other: &Self) -> bool { 40 // Compare everything but the message id 41 self.device_index() == other.device_index() 42 && self.feature_index() == other.feature_index() 43 && self.feature_id() == other.feature_id() 44 && self.output_command() == other.output_command() 45 } 46} 47 48/* 49 50impl From<CheckedActuatorCmdV4> for ActuatorCmdV4 { 51 fn from(value: CheckedActuatorCmdV4) -> Self { 52 ActuatorCmdV4::new( 53 value.device_index(), 54 value.feature_index(), 55 value.actuator_type(), 56 value.output_command() 57 ) 58 } 59} 60 */ 61 62impl CheckedOutputCmdV4 { 63 pub fn new( 64 id: u32, 65 device_index: u32, 66 feature_index: u32, 67 feature_id: Uuid, 68 output_command: OutputCommand, 69 ) -> Self { 70 Self { 71 id, 72 device_index, 73 feature_index, 74 feature_id, 75 output_command, 76 } 77 } 78} 79 80impl ButtplugMessageValidator for CheckedOutputCmdV4 { 81 fn is_valid(&self) -> Result<(), ButtplugMessageError> { 82 self.is_not_system_id(self.id)?; 83 Ok(()) 84 } 85} 86 87impl TryFromDeviceAttributes<OutputCmdV4> for CheckedOutputCmdV4 { 88 fn try_from_device_attributes( 89 cmd: OutputCmdV4, 90 attrs: &ServerDeviceAttributes, 91 ) -> Result<Self, ButtplugError> { 92 let features = attrs.features(); 93 94 // Since we have the feature info already, check limit and unpack into step range when creating. 95 // 96 // If this message isn't the result of an upgrade from another older message, we won't have set 97 // our feature id yet. 98 let (feature, _) = if let Some(feature) = features.get(cmd.feature_index() as usize) { 99 (feature, feature.id()) 100 } else { 101 return Err(ButtplugError::from( 102 ButtplugDeviceError::DeviceFeatureIndexError(features.len() as u32, cmd.feature_index()), 103 )); 104 }; 105 106 // Check to make sure the feature has an actuator that handles the data we've been passed 107 if let Some(output_map) = feature.output() { 108 let output_type = cmd.command().as_output_type(); 109 let value = cmd.command().value(); 110 let new_value = output_map 111 .calculate_from_value(output_type, value) 112 .map_err(|e| { 113 error!("{:?}", e); 114 ButtplugDeviceError::DeviceStepRangeError(0, value) 115 })?; 116 let mut new_command = cmd.command(); 117 new_command.set_value(new_value); 118 // We can't make a private trait impl to turn a ValueCmd into a CheckedValueCmd, and this 119 // is all about security, so we just copy. Silly, but it works for our needs in terms of 120 // making this a barrier. 121 Ok(Self { 122 id: cmd.id(), 123 feature_id: feature.id(), 124 device_index: cmd.device_index(), 125 feature_index: cmd.feature_index(), 126 output_command: new_command, 127 }) 128 } else { 129 Err(ButtplugError::from( 130 ButtplugDeviceError::MessageNotSupported( 131 ButtplugDeviceMessageNameV4::OutputCmd.to_string(), 132 ), 133 )) 134 } 135 } 136}