Buttplug sex toy control library

feat: Add per-output-type-property configurations

Instead of just having a single configuration that's settable in any output,
allow all fields to be limitable.

+1443 -1352
+21
buttplug-user-device-config-v4.json
··· 1 + { 2 + "version": { 3 + "major": 4, 4 + "minor": 0 5 + }, 6 + "user-configs": { 7 + "protocols": { 8 + "lovense": { 9 + "communication": [ 10 + { 11 + "websocket": { 12 + "name": "LVS-Test" 13 + } 14 + } 15 + ], 16 + "configurations": [] 17 + } 18 + }, 19 + "devices": [] 20 + } 21 + }
+32 -12
crates/buttplug_core/schema/buttplug-schema.json
··· 306 306 "type": "string" 307 307 }, 308 308 "Output": { 309 - "type": "object", 310 - "patternProperties": { 311 - "^.*$": { 312 - "type": "object", 313 - "properties": { 314 - "StepCount": { 315 - "type": "integer" 316 - } 309 + "type": "array", 310 + "items": { 311 + "type": "object", 312 + "patternProperties": { 313 + "^(Vibrate|Rotate|Oscillate|Constrict|Spray|Position|Heater|Led)$": { 314 + "type": "object", 315 + "properties": { 316 + "Value": { 317 + "$ref": "#/components/RangeInclusive" 318 + } 319 + }, 320 + "required": [ 321 + "Value" 322 + ] 317 323 }, 318 - "required": [ 319 - "StepCount" 320 - ] 321 - } 324 + "PositionWithDuration": { 325 + "type": "object", 326 + "properties": { 327 + "Position": { 328 + "$ref": "#/components/RangeInclusive" 329 + }, 330 + "Duration": { 331 + "$ref": "#/components/RangeInclusive" 332 + } 333 + }, 334 + "required": [ 335 + "Position", 336 + "Duration" 337 + ] 338 + } 339 + }, 340 + "minProperties": 1, 341 + "maxProperties": 1 322 342 } 323 343 }, 324 344 "Input": {
+78 -13
crates/buttplug_core/src/message/device_feature.rs
··· 9 9 use getset::{CopyGetters, Getters, MutGetters, Setters}; 10 10 use serde::{ser::SerializeSeq, Deserialize, Serialize, Serializer}; 11 11 use std::{ 12 - collections::{HashMap, HashSet}, 13 - ops::RangeInclusive, 12 + collections::{HashMap, HashSet}, hash::Hash, ops::RangeInclusive 14 13 }; 15 14 16 15 #[derive(Debug, Display, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Hash, EnumIter)] ··· 70 69 // then we denote this by prefixing the type with Client/Server. Server attributes will usually be 71 70 // hosted in the server/device/configuration module. 72 71 #[derive( 73 - Clone, Debug, Default, PartialEq, Getters, MutGetters, CopyGetters, Setters, Serialize, Deserialize, 72 + Clone, Debug, Default, Getters, MutGetters, CopyGetters, Setters, Serialize, Deserialize, 74 73 )] 75 74 pub struct DeviceFeature { 76 75 // Index of the feature on the device. This was originally implicit as the position in the feature ··· 83 82 #[serde(default)] 84 83 #[serde(rename = "FeatureDescription")] 85 84 description: String, 85 + // TODO Maybe make this its own object instead of a HashMap? 86 86 #[getset(get = "pub")] 87 87 #[serde(skip_serializing_if = "Option::is_none")] 88 88 #[serde(rename = "Output")] 89 - output: Option<HashMap<OutputType, DeviceFeatureOutput>>, 89 + output: Option<HashSet<DeviceFeatureOutput>>, 90 90 #[getset(get = "pub")] 91 91 #[serde(skip_serializing_if = "Option::is_none")] 92 92 #[serde(rename = "Input")] ··· 97 97 pub fn new( 98 98 index: u32, 99 99 description: &str, 100 - output: &Option<HashMap<OutputType, DeviceFeatureOutput>>, 100 + output: &Option<HashSet<DeviceFeatureOutput>>, 101 101 input: &Option<HashMap<InputType, DeviceFeatureInput>>, 102 102 ) -> Self { 103 103 Self { ··· 123 123 seq.end() 124 124 } 125 125 126 - #[derive(Clone, Debug, PartialEq, Eq, CopyGetters, Serialize, Deserialize)] 127 - pub struct DeviceFeatureOutput { 128 - #[getset(get_copy = "pub")] 129 - #[serde(rename = "StepCount")] 130 - step_count: u32, 126 + #[derive(Serialize, Deserialize, Clone, Debug, Getters)] 127 + pub struct DeviceFeatureOutputValueProperties { 128 + #[getset(get = "pub")] 129 + #[serde(rename = "Value")] 130 + value: RangeInclusive<i32>, 131 + } 132 + 133 + impl DeviceFeatureOutputValueProperties { 134 + pub fn new(value: &RangeInclusive<i32>) -> Self { 135 + DeviceFeatureOutputValueProperties { value: value.clone() } 136 + } 137 + } 138 + 139 + #[derive(Serialize, Deserialize, Clone, Debug, Getters)] 140 + pub struct DeviceFeatureOutputPositionWithDurationProperties { 141 + #[getset(get = "pub")] 142 + #[serde(rename = "Position")] 143 + position: RangeInclusive<u32>, 144 + #[getset(get = "pub")] 145 + #[serde(rename = "Duration")] 146 + duration: RangeInclusive<u32>, 147 + } 148 + 149 + impl DeviceFeatureOutputPositionWithDurationProperties { 150 + pub fn new(position: &RangeInclusive<u32>, duration: &RangeInclusive<u32>) -> Self { 151 + DeviceFeatureOutputPositionWithDurationProperties { position: position.clone(), duration: duration.clone() } 152 + } 153 + } 154 + 155 + #[derive(Serialize, Deserialize, Clone, Debug)] 156 + pub enum DeviceFeatureOutput { 157 + Unknown, 158 + Vibrate(DeviceFeatureOutputValueProperties), 159 + Rotate(DeviceFeatureOutputValueProperties), 160 + RotateWithDirection(DeviceFeatureOutputValueProperties), 161 + Oscillate(DeviceFeatureOutputValueProperties), 162 + Constrict(DeviceFeatureOutputValueProperties), 163 + Heater(DeviceFeatureOutputValueProperties), 164 + Led(DeviceFeatureOutputValueProperties), 165 + Position(DeviceFeatureOutputValueProperties), 166 + PositionWithDuration(DeviceFeatureOutputPositionWithDurationProperties), 167 + Spray(DeviceFeatureOutputValueProperties), 168 + } 169 + 170 + impl From<&DeviceFeatureOutput> for OutputType { 171 + fn from(value: &DeviceFeatureOutput) -> Self { 172 + match value { 173 + DeviceFeatureOutput::Constrict(_) => OutputType::Constrict, 174 + DeviceFeatureOutput::Heater(_) => OutputType::Heater, 175 + DeviceFeatureOutput::Led(_) => OutputType::Led, 176 + DeviceFeatureOutput::Oscillate(_) => OutputType::Oscillate, 177 + DeviceFeatureOutput::Position(_) => OutputType::Position, 178 + DeviceFeatureOutput::PositionWithDuration(_) => OutputType::PositionWithDuration, 179 + DeviceFeatureOutput::Rotate(_) => OutputType::Rotate, 180 + DeviceFeatureOutput::RotateWithDirection(_) => OutputType::RotateWithDirection, 181 + DeviceFeatureOutput::Spray(_) => OutputType::Spray, 182 + DeviceFeatureOutput::Unknown => OutputType::Unknown, 183 + DeviceFeatureOutput::Vibrate(_) => OutputType::Vibrate 184 + } 185 + } 131 186 } 132 187 133 - impl DeviceFeatureOutput { 134 - pub fn new(step_count: u32) -> Self { 135 - Self { step_count } 188 + impl PartialEq for DeviceFeatureOutput { 189 + fn eq(&self, other: &Self) -> bool { 190 + // Just make sure our two DeviceFeatureOutput's are the same variant, their values may not match 191 + // but we should never store two of the same variant in the same structure. 192 + std::mem::discriminant(self) == std::mem::discriminant(other) 193 + } 194 + } 195 + 196 + impl Eq for DeviceFeatureOutput {} 197 + 198 + impl Hash for DeviceFeatureOutput { 199 + fn hash<H: std::hash::Hasher>(&self, state: &mut H) { 200 + OutputType::from(self).hash(state) 136 201 } 137 202 } 138 203
+1 -1
crates/buttplug_server/src/message/mod.rs
··· 123 123 } 124 124 125 125 #[derive( 126 - Debug, Clone, PartialEq, ButtplugMessage, ButtplugMessageFinalizer, ButtplugMessageValidator, 126 + Debug, Clone, ButtplugMessage, ButtplugMessageFinalizer, ButtplugMessageValidator, 127 127 )] 128 128 pub enum ButtplugServerMessageVariant { 129 129 V0(ButtplugServerMessageV0),
+14 -14
crates/buttplug_server_device_config/build-config/buttplug-device-config-v4.json
··· 1 1 { 2 2 "version": { 3 3 "major": 4, 4 - "minor": 62 4 + "minor": 63 5 5 }, 6 6 "protocols": { 7 7 "activejoy": { ··· 10042 10042 "output": { 10043 10043 "rotate_with_direction": { 10044 10044 "value": [ 10045 - 0, 10045 + -20, 10046 10046 20 10047 10047 ] 10048 10048 } ··· 10574 10574 "output": { 10575 10575 "rotate_with_direction": { 10576 10576 "value": [ 10577 - 0, 10577 + -20, 10578 10578 20 10579 10579 ] 10580 10580 } ··· 10920 10920 "output": { 10921 10921 "rotate_with_direction": { 10922 10922 "value": [ 10923 - 0, 10923 + -20, 10924 10924 20 10925 10925 ] 10926 10926 } ··· 11325 11325 "output": { 11326 11326 "rotate_with_direction": { 11327 11327 "value": [ 11328 - 0, 11328 + -20, 11329 11329 20 11330 11330 ] 11331 11331 } ··· 13297 13297 "output": { 13298 13298 "rotate_with_direction": { 13299 13299 "value": [ 13300 - 0, 13300 + -255, 13301 13301 255 13302 13302 ] 13303 13303 } ··· 13916 13916 "output": { 13917 13917 "rotate_with_direction": { 13918 13918 "value": [ 13919 - 0, 13919 + -2, 13920 13920 2 13921 13921 ] 13922 13922 } ··· 18226 18226 "output": { 18227 18227 "rotate_with_direction": { 18228 18228 "value": [ 18229 - 0, 18229 + -6, 18230 18230 6 18231 18231 ] 18232 18232 } ··· 18463 18463 "output": { 18464 18464 "rotate_with_direction": { 18465 18465 "value": [ 18466 - 0, 18466 + -100, 18467 18467 100 18468 18468 ] 18469 18469 } ··· 18778 18778 "output": { 18779 18779 "rotate_with_direction": { 18780 18780 "value": [ 18781 - 0, 18781 + -10, 18782 18782 10 18783 18783 ] 18784 18784 } ··· 18859 18859 "output": { 18860 18860 "rotate_with_direction": { 18861 18861 "value": [ 18862 - 0, 18862 + -99, 18863 18863 99 18864 18864 ] 18865 18865 } ··· 18880 18880 "output": { 18881 18881 "rotate_with_direction": { 18882 18882 "value": [ 18883 - 0, 18883 + -99, 18884 18884 99 18885 18885 ] 18886 18886 } ··· 18901 18901 "output": { 18902 18902 "rotate_with_direction": { 18903 18903 "value": [ 18904 - 0, 18904 + -99, 18905 18905 99 18906 18906 ] 18907 18907 } ··· 18912 18912 "output": { 18913 18913 "rotate_with_direction": { 18914 18914 "value": [ 18915 - 0, 18915 + -99, 18916 18916 99 18917 18917 ] 18918 18918 }
+40 -35
crates/buttplug_server_device_config/device-config-v4/buttplug-device-config-schema-v4.json
··· 241 241 ], 242 242 "additionalProperties": false 243 243 } 244 - } 244 + } 245 245 }, 246 - "feature-settings": { 246 + "feature_settings": { 247 247 "type": "object", 248 248 "properties": { 249 - "alt-protocol-index": { 249 + "alt_protocol_index": { 250 250 "type": "number" 251 251 } 252 252 } ··· 255 255 "required": [ 256 256 "id" 257 257 ], 258 - "additionalProperties": false 258 + "additionalProperties": false 259 259 } 260 260 }, 261 261 "user-config-features": { ··· 295 295 "properties": { 296 296 "value": { 297 297 "$ref": "#/components/value-range" 298 - }, 298 + }, 299 299 "reverse": { 300 300 "type": "boolean" 301 301 }, 302 302 "disabled": { 303 303 "type": "boolean" 304 304 } 305 - } 305 + } 306 306 }, 307 307 "^position_with_duration$": { 308 308 "type": "object", 309 309 "properties": { 310 310 "position": { 311 311 "$ref": "#/components/value-range" 312 - }, 312 + }, 313 + "duration": { 314 + "$ref": "#/components/value-range" 315 + }, 313 316 "reverse": { 314 317 "type": "boolean" 315 318 }, ··· 347 350 ], 348 351 "additionalProperties": false 349 352 } 350 - } 353 + } 351 354 } 352 355 }, 353 356 "required": [ ··· 379 382 "deny", 380 383 "index" 381 384 ] 382 - }, 383 - "user-config-definition": { 384 - "type": "object", 385 - "properties": { 386 - "name": { 387 - "type": "string" 388 - }, 389 - "id": { 390 - "$ref": "#/components/uuid" 391 - }, 392 - "base-id": { 393 - "$ref": "#/components/uuid" 394 - }, 395 - "features": { 396 - "$ref": "#/components/user-config-features" 397 - }, 398 - "user-config": { 399 - "$ref": "#/components/user-config-customization" 400 - } 401 - }, 402 - "required": [ 403 - "id", 404 - "features", 405 - "user-config" 406 - ], 407 - "additionalProperties": false 408 385 }, 409 386 "defaults-definition": { 410 387 "type": "object", ··· 597 574 ] 598 575 }, 599 576 "config": { 600 - "$ref": "#/components/user-config-definition" 577 + "type": "object", 578 + "properties": { 579 + "name": { 580 + "type": "string" 581 + }, 582 + "id": { 583 + "$ref": "#/components/uuid" 584 + }, 585 + "base-id": { 586 + "$ref": "#/components/uuid" 587 + }, 588 + "features": { 589 + "$ref": "#/components/user-config-features" 590 + }, 591 + "user-config": { 592 + "$ref": "#/components/user-config-customization" 593 + }, 594 + "message-gap-ms": { 595 + "type": "integer", 596 + "min": 0 597 + } 598 + }, 599 + "required": [ 600 + "id", 601 + "base-id", 602 + "features", 603 + "user-config" 604 + ], 605 + "additionalProperties": false 601 606 } 602 607 }, 603 608 "additionalProperties": false,
+1 -1
crates/buttplug_server_device_config/device-config-v4/version.yaml
··· 1 1 version: 2 2 major: 4 3 - minor: 62 3 + minor: 63
+44
crates/buttplug_server_device_config/src/device_config_file/base.rs
··· 1 + use std::collections::HashMap; 2 + 3 + use getset::Getters; 4 + use serde::{Deserialize, Serialize}; 5 + 6 + use crate::device_config_file::{get_internal_config_version, protocol::ProtocolDefinition, ConfigVersion, ConfigVersionGetter}; 7 + 8 + 9 + #[derive(Deserialize, Serialize, Debug, Getters)] 10 + #[getset(get_mut = "pub", set = "pub")] 11 + pub struct BaseConfigFile { 12 + #[getset(get_copy = "pub")] 13 + version: ConfigVersion, 14 + #[getset(get = "pub")] 15 + #[serde(default, skip_serializing_if = "Option::is_none")] 16 + protocols: Option<HashMap<String, ProtocolDefinition>>, 17 + } 18 + 19 + impl Default for BaseConfigFile { 20 + fn default() -> Self { 21 + Self { 22 + version: get_internal_config_version(), 23 + protocols: Some(HashMap::new()), 24 + } 25 + } 26 + } 27 + 28 + impl ConfigVersionGetter for BaseConfigFile { 29 + fn version(&self) -> ConfigVersion { 30 + self.version 31 + } 32 + } 33 + 34 + impl BaseConfigFile { 35 + pub fn new(major_version: u32, minor_version: u32) -> Self { 36 + Self { 37 + version: ConfigVersion { 38 + major: major_version, 39 + minor: minor_version, 40 + }, 41 + protocols: None, 42 + } 43 + } 44 + }
+81
crates/buttplug_server_device_config/src/device_config_file/device.rs
··· 1 + use getset::{CopyGetters, Getters, MutGetters}; 2 + use serde::{Deserialize, Serialize}; 3 + use uuid::Uuid; 4 + 5 + use crate::{ServerDeviceDefinition, ServerDeviceDefinitionBuilder}; 6 + 7 + use super::feature::{ConfigBaseDeviceFeature, ConfigUserDeviceFeature}; 8 + 9 + #[derive(Debug, Clone, Getters, CopyGetters, Serialize, Deserialize)] 10 + pub struct ConfigBaseDeviceDefinition { 11 + #[getset(get = "pub")] 12 + /// Given name of the device this instance represents. 13 + name: String, 14 + #[getset(get_copy = "pub")] 15 + id: Uuid, 16 + #[getset(get = "pub")] 17 + protocol_variant: Option<String>, 18 + #[getset(get_copy = "pub")] 19 + message_gap_ms: Option<u32>, 20 + #[getset(get = "pub")] 21 + features: Vec<ConfigBaseDeviceFeature>, 22 + } 23 + 24 + impl Into<ServerDeviceDefinition> for ConfigBaseDeviceDefinition { 25 + fn into(self) -> ServerDeviceDefinition { 26 + let mut builder = ServerDeviceDefinitionBuilder::new(&self.name, &self.id); 27 + if let Some(variant) = self.protocol_variant { 28 + builder.protocol_variant(&variant); 29 + } 30 + if let Some(gap) = self.message_gap_ms { 31 + builder.message_gap_ms(gap); 32 + } 33 + for feature in self.features { 34 + builder.add_feature(feature.into()); 35 + } 36 + builder.finish() 37 + } 38 + } 39 + 40 + #[derive(Serialize, Deserialize, Debug, Getters, CopyGetters, Default, Clone, MutGetters)] 41 + pub struct ConfigUserDeviceCustomization { 42 + #[serde( 43 + rename = "display-name", 44 + default, 45 + skip_serializing_if = "Option::is_none" 46 + )] 47 + #[getset(get = "pub")] 48 + display_name: Option<String>, 49 + #[serde(default)] 50 + #[getset(get_copy = "pub")] 51 + allow: bool, 52 + #[serde(default)] 53 + #[getset(get_copy = "pub")] 54 + deny: bool, 55 + #[getset(get_copy = "pub", get_mut = "pub")] 56 + index: u32, 57 + #[getset(get_copy = "pub")] 58 + #[serde( 59 + rename = "message-gap-ms", 60 + default, 61 + skip_serializing_if = "Option::is_none" 62 + )] 63 + message_gap_ms: Option<u32>, 64 + } 65 + 66 + #[derive(Debug, Clone, Getters, MutGetters, Serialize, Deserialize, CopyGetters)] 67 + pub struct ConfigUserDeviceDefinition { 68 + #[getset(get_copy = "pub")] 69 + id: Uuid, 70 + #[getset(get_copy = "pub")] 71 + #[serde(rename = "base-id")] 72 + base_id: Uuid, 73 + #[getset(get = "pub")] 74 + /// Message attributes for this device instance. 75 + #[getset(get = "pub", get_mut = "pub")] 76 + features: Vec<ConfigUserDeviceFeature>, 77 + #[getset(get = "pub", get_mut = "pub")] 78 + #[serde(rename = "user-config")] 79 + /// Per-user configurations specific to this device instance. 80 + user_config: ConfigUserDeviceCustomization, 81 + }
+221
crates/buttplug_server_device_config/src/device_config_file/feature.rs
··· 1 + use std::{collections::HashSet, ops::RangeInclusive}; 2 + 3 + use buttplug_core::message::InputCommandType; 4 + use getset::{CopyGetters, Getters, MutGetters, Setters}; 5 + use serde::{Deserialize, Serialize}; 6 + use uuid::Uuid; 7 + use crate::{ServerDeviceFeature, ServerDeviceFeatureOutput, ServerDeviceFeatureOutputPositionWithDurationProperties, ServerDeviceFeatureOutputValueProperties}; 8 + 9 + use super::range_sequence_serialize; 10 + 11 + #[derive(Serialize, Deserialize, Clone, Debug)] 12 + struct BaseDeviceFeatureOutputValueProperties { 13 + value: RangeInclusive<i32> 14 + } 15 + 16 + impl Into<ServerDeviceFeatureOutputValueProperties> for BaseDeviceFeatureOutputValueProperties { 17 + fn into(self) -> ServerDeviceFeatureOutputValueProperties { 18 + ServerDeviceFeatureOutputValueProperties::new(&self.value.into(), false) 19 + } 20 + } 21 + 22 + #[derive(Serialize, Deserialize, Clone, Debug)] 23 + struct BaseDeviceFeatureOutputPositionWithDurationProperties { 24 + position: RangeInclusive<u32>, 25 + duration: RangeInclusive<u32>, 26 + } 27 + 28 + impl Into<ServerDeviceFeatureOutputPositionWithDurationProperties> for BaseDeviceFeatureOutputPositionWithDurationProperties { 29 + fn into(self) -> ServerDeviceFeatureOutputPositionWithDurationProperties { 30 + ServerDeviceFeatureOutputPositionWithDurationProperties::new(&self.position.into(), &self.duration.into(), false, false) 31 + } 32 + } 33 + 34 + #[derive(Serialize, Deserialize, Clone, Debug)] 35 + struct BaseDeviceFeatureOutput { 36 + #[serde(skip_serializing_if="Option::is_none")] 37 + vibrate: Option<BaseDeviceFeatureOutputValueProperties>, 38 + #[serde(skip_serializing_if="Option::is_none")] 39 + rotate: Option<BaseDeviceFeatureOutputValueProperties>, 40 + #[serde(skip_serializing_if="Option::is_none")] 41 + rotate_with_direction: Option<BaseDeviceFeatureOutputValueProperties>, 42 + #[serde(skip_serializing_if="Option::is_none")] 43 + oscillate: Option<BaseDeviceFeatureOutputValueProperties>, 44 + #[serde(skip_serializing_if="Option::is_none")] 45 + constrict: Option<BaseDeviceFeatureOutputValueProperties>, 46 + #[serde(skip_serializing_if="Option::is_none")] 47 + heater: Option<BaseDeviceFeatureOutputValueProperties>, 48 + #[serde(skip_serializing_if="Option::is_none")] 49 + led: Option<BaseDeviceFeatureOutputValueProperties>, 50 + #[serde(skip_serializing_if="Option::is_none")] 51 + position: Option<BaseDeviceFeatureOutputValueProperties>, 52 + #[serde(skip_serializing_if="Option::is_none")] 53 + position_with_duration: Option<BaseDeviceFeatureOutputPositionWithDurationProperties>, 54 + #[serde(skip_serializing_if="Option::is_none")] 55 + spray: Option<BaseDeviceFeatureOutputValueProperties>, 56 + } 57 + /* 58 + impl Into<ServerDeviceFeatureOutput> for BaseDeviceFeatureOutput { 59 + fn into(self) -> ServerDeviceFeatureOutput { 60 + if let Some(vibrate) = self.vibrate { 61 + } 62 + } 63 + } 64 + */ 65 + 66 + #[derive(Serialize, Deserialize, Clone, Debug)] 67 + struct UserDeviceFeatureOutputValueProperties { 68 + #[serde(skip_serializing_if="Option::is_none")] 69 + value: Option<RangeInclusive<i32>>, 70 + #[serde(default)] 71 + disabled: bool, 72 + #[serde(default)] 73 + reverse: bool 74 + } 75 + 76 + 77 + #[derive(Serialize, Deserialize, Clone, Debug)] 78 + struct UserDeviceFeatureOutputPositionProperties { 79 + #[serde(skip_serializing_if="Option::is_none")] 80 + value: Option<RangeInclusive<u32>>, 81 + #[serde(default)] 82 + disabled: bool, 83 + #[serde(default)] 84 + reverse: bool 85 + } 86 + 87 + #[derive(Serialize, Deserialize, Clone, Debug)] 88 + struct UserDeviceFeatureOutputPositionWithDurationProperties { 89 + #[serde(skip_serializing_if="Option::is_none")] 90 + position: Option<RangeInclusive<u32>>, 91 + #[serde(skip_serializing_if="Option::is_none")] 92 + duration: Option<RangeInclusive<u32>>, 93 + #[serde(default)] 94 + disabled: bool, 95 + #[serde(default)] 96 + reverse: bool 97 + } 98 + 99 + #[derive(Serialize, Deserialize, Clone, Debug)] 100 + struct UserDeviceFeatureOutput { 101 + #[serde(skip_serializing_if="Option::is_none")] 102 + vibrate: Option<UserDeviceFeatureOutputValueProperties>, 103 + #[serde(skip_serializing_if="Option::is_none")] 104 + rotate: Option<UserDeviceFeatureOutputValueProperties>, 105 + #[serde(skip_serializing_if="Option::is_none")] 106 + rotate_with_direction: Option<UserDeviceFeatureOutputValueProperties>, 107 + #[serde(skip_serializing_if="Option::is_none")] 108 + oscillate: Option<UserDeviceFeatureOutputValueProperties>, 109 + #[serde(skip_serializing_if="Option::is_none")] 110 + constrict: Option<UserDeviceFeatureOutputValueProperties>, 111 + #[serde(skip_serializing_if="Option::is_none")] 112 + heater: Option<UserDeviceFeatureOutputValueProperties>, 113 + #[serde(skip_serializing_if="Option::is_none")] 114 + led: Option<UserDeviceFeatureOutputValueProperties>, 115 + #[serde(skip_serializing_if="Option::is_none")] 116 + position: Option<UserDeviceFeatureOutputPositionProperties>, 117 + #[serde(skip_serializing_if="Option::is_none")] 118 + position_with_duration: Option<UserDeviceFeatureOutputPositionWithDurationProperties>, 119 + #[serde(skip_serializing_if="Option::is_none")] 120 + spray: Option<UserDeviceFeatureOutputValueProperties>, 121 + } 122 + 123 + #[derive( 124 + Clone, Debug, Default, PartialEq, Eq, Getters, MutGetters, Setters, Serialize, Deserialize, 125 + )] 126 + pub struct DeviceFeatureInputProperties { 127 + #[getset(get = "pub", get_mut = "pub(super)")] 128 + #[serde(serialize_with = "range_sequence_serialize")] 129 + value_range: Vec<RangeInclusive<i32>>, 130 + #[getset(get = "pub")] 131 + input_commands: HashSet<InputCommandType> 132 + } 133 + 134 + impl DeviceFeatureInputProperties { 135 + pub fn new( 136 + value_range: &Vec<RangeInclusive<i32>>, 137 + sensor_commands: &HashSet<InputCommandType>, 138 + ) -> Self { 139 + Self { 140 + value_range: value_range.clone(), 141 + input_commands: sensor_commands.clone(), 142 + } 143 + } 144 + } 145 + 146 + #[derive( 147 + Clone, Debug, Default, Getters, Serialize, Deserialize, 148 + )] 149 + #[getset(get = "pub")] 150 + pub struct DeviceFeatureInput { 151 + battery: Option<DeviceFeatureInputProperties>, 152 + rssi: Option<DeviceFeatureInputProperties>, 153 + pressure: Option<DeviceFeatureInputProperties>, 154 + button: Option<DeviceFeatureInputProperties> 155 + } 156 + 157 + #[derive( 158 + Clone, Debug, Default, Getters, Serialize, Deserialize, CopyGetters, 159 + )] 160 + pub struct ConfigBaseDeviceFeature { 161 + #[getset(get = "pub")] 162 + #[serde(default)] 163 + description: String, 164 + #[getset(get = "pub")] 165 + #[serde(skip_serializing_if = "Option::is_none")] 166 + output: Option<BaseDeviceFeatureOutput>, 167 + #[getset(get = "pub")] 168 + #[serde(skip_serializing_if = "Option::is_none")] 169 + input: Option<DeviceFeatureInput>, 170 + #[getset(get_copy = "pub")] 171 + id: Uuid, 172 + #[getset(get = "pub")] 173 + #[serde( 174 + skip_serializing_if = "BaseFeatureSettings::is_none", 175 + default 176 + )] 177 + feature_settings: BaseFeatureSettings, 178 + } 179 + 180 + impl Into<ServerDeviceFeature> for ConfigBaseDeviceFeature { 181 + fn into(self) -> ServerDeviceFeature { 182 + ServerDeviceFeature::new( 183 + &self.description, 184 + self.id, 185 + None, 186 + self.feature_settings.alt_protocol_index, 187 + self.output.into(), 188 + self.input.into() 189 + ) 190 + } 191 + } 192 + 193 + #[derive( 194 + Clone, Debug, Default, Getters, Serialize, Deserialize, CopyGetters, 195 + )] 196 + pub struct ConfigUserDeviceFeature { 197 + #[getset(get_copy = "pub")] 198 + id: Uuid, 199 + #[getset(get_copy = "pub")] 200 + base_id: Uuid, 201 + #[getset(get = "pub")] 202 + #[serde(rename = "output", skip_serializing_if = "Option::is_none")] 203 + output: Option<UserDeviceFeatureOutput> 204 + } 205 + 206 + #[derive(Serialize, Deserialize, Debug, Clone, Default, CopyGetters)] 207 + pub struct BaseFeatureSettings { 208 + #[serde( 209 + rename = "alt-protocol-index", 210 + skip_serializing_if = "Option::is_none", 211 + default 212 + )] 213 + #[getset(get_copy = "pub")] 214 + alt_protocol_index: Option<u32>, 215 + } 216 + 217 + impl BaseFeatureSettings { 218 + pub fn is_none(&self) -> bool { 219 + self.alt_protocol_index.is_none() 220 + } 221 + }
+270
crates/buttplug_server_device_config/src/device_config_file/mod.rs
··· 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 + 8 + mod base; 9 + mod device; 10 + mod feature; 11 + mod protocol; 12 + mod user; 13 + 14 + use base::BaseConfigFile; 15 + 16 + use crate::device_config_file::{protocol::ProtocolDefinition, user::{UserConfigDefinition, UserConfigFile, UserDeviceConfigPair}}; 17 + 18 + use super::{ 19 + BaseDeviceIdentifier, 20 + DeviceConfigurationManager, 21 + DeviceConfigurationManagerBuilder, 22 + }; 23 + use buttplug_core::{ 24 + errors::{ButtplugDeviceError, ButtplugError}, 25 + util::json::JSONValidator, 26 + }; 27 + use dashmap::DashMap; 28 + use getset::CopyGetters; 29 + use serde::{Deserialize, Serialize, Serializer, ser::{self, SerializeSeq}}; 30 + use std::{collections::HashMap, fmt::Display, ops::RangeInclusive}; 31 + 32 + pub static DEVICE_CONFIGURATION_JSON: &str = 33 + include_str!("../../build-config/buttplug-device-config-v4.json"); 34 + static DEVICE_CONFIGURATION_JSON_SCHEMA: &str = include_str!( 35 + "../../device-config-v4/buttplug-device-config-schema-v4.json" 36 + ); 37 + 38 + fn range_serialize<S>(range: &Option<RangeInclusive<u32>>, serializer: S) -> Result<S::Ok, S::Error> 39 + where 40 + S: Serializer, 41 + { 42 + if let Some(range) = range { 43 + let mut seq = serializer.serialize_seq(Some(2))?; 44 + seq.serialize_element(&range.start())?; 45 + seq.serialize_element(&range.end())?; 46 + seq.end() 47 + } else { 48 + Err(ser::Error::custom( 49 + "shouldn't be serializing if range is None", 50 + )) 51 + } 52 + } 53 + 54 + fn range_sequence_serialize<S>( 55 + range_vec: &Vec<RangeInclusive<i32>>, 56 + serializer: S, 57 + ) -> Result<S::Ok, S::Error> 58 + where 59 + S: Serializer, 60 + { 61 + let mut seq = serializer.serialize_seq(Some(range_vec.len()))?; 62 + for range in range_vec { 63 + seq.serialize_element(&vec![*range.start(), *range.end()])?; 64 + } 65 + seq.end() 66 + } 67 + 68 + #[derive(Deserialize, Serialize, Debug, CopyGetters, Clone, Copy)] 69 + #[getset(get_copy = "pub", get_mut = "pub")] 70 + struct ConfigVersion { 71 + pub major: u32, 72 + pub minor: u32, 73 + } 74 + 75 + impl Display for ConfigVersion { 76 + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 77 + write!(f, "{}.{}", self.major, self.minor) 78 + } 79 + } 80 + 81 + trait ConfigVersionGetter { 82 + fn version(&self) -> ConfigVersion; 83 + } 84 + 85 + 86 + fn get_internal_config_version() -> ConfigVersion { 87 + let config: BaseConfigFile = serde_json::from_str(DEVICE_CONFIGURATION_JSON) 88 + .expect("If this fails, the whole library goes with it."); 89 + config.version() 90 + } 91 + 92 + fn load_protocol_config_from_json<'a, T>( 93 + config_str: &'a str, 94 + skip_version_check: bool, 95 + ) -> Result<T, ButtplugDeviceError> 96 + where 97 + T: ConfigVersionGetter + Deserialize<'a>, 98 + { 99 + let config_validator = JSONValidator::new(DEVICE_CONFIGURATION_JSON_SCHEMA); 100 + match config_validator.validate(config_str) { 101 + Ok(_) => match serde_json::from_str::<T>(config_str) { 102 + Ok(protocol_config) => { 103 + let internal_config_version = get_internal_config_version(); 104 + if !skip_version_check && protocol_config.version().major != internal_config_version.major { 105 + Err(ButtplugDeviceError::DeviceConfigurationError(format!( 106 + "Device configuration file major version {} is different than internal major version {}. Cannot load external files that do not have matching major version numbers.", 107 + protocol_config.version(), 108 + internal_config_version 109 + ))) 110 + } else { 111 + Ok(protocol_config) 112 + } 113 + } 114 + Err(err) => Err(ButtplugDeviceError::DeviceConfigurationError(format!( 115 + "{err}" 116 + ))), 117 + }, 118 + Err(err) => Err(ButtplugDeviceError::DeviceConfigurationError(format!( 119 + "{err}" 120 + ))), 121 + } 122 + } 123 + 124 + fn load_main_config( 125 + main_config_str: &Option<String>, 126 + skip_version_check: bool, 127 + ) -> Result<DeviceConfigurationManagerBuilder, ButtplugDeviceError> { 128 + if main_config_str.is_some() { 129 + info!("Loading from custom base device configuration...") 130 + } else { 131 + info!("Loading from internal base device configuration...") 132 + } 133 + // Start by loading the main config 134 + let main_config = load_protocol_config_from_json::<BaseConfigFile>( 135 + main_config_str 136 + .as_ref() 137 + .unwrap_or(&DEVICE_CONFIGURATION_JSON.to_owned()), 138 + skip_version_check, 139 + )?; 140 + 141 + info!("Loaded config version {:?}", main_config.version()); 142 + 143 + let mut dcm_builder = DeviceConfigurationManagerBuilder::default(); 144 + 145 + for (protocol_name, protocol_def) in main_config.protocols().clone().unwrap_or_default() { 146 + if let Some(specifiers) = protocol_def.communication() { 147 + dcm_builder.communication_specifier(&protocol_name, &specifiers); 148 + } 149 + 150 + if let Some(features) = protocol_def.defaults() { 151 + dcm_builder.protocol_features(&BaseDeviceIdentifier::new_default(&protocol_name), features); 152 + } 153 + 154 + for (config_ident, config) in protocol_def.configurations() { 155 + let ident = BaseDeviceIdentifier::new(&protocol_name, config_ident); 156 + dcm_builder.protocol_features(ident, config.clone()); 157 + } 158 + } 159 + 160 + Ok(dcm_builder) 161 + } 162 + 163 + fn load_user_config( 164 + user_config_str: &str, 165 + skip_version_check: bool, 166 + dcm_builder: &mut DeviceConfigurationManagerBuilder, 167 + ) -> Result<(), ButtplugDeviceError> { 168 + info!("Loading user configuration from string."); 169 + let user_config_file = 170 + load_protocol_config_from_json::<UserConfigFile>(user_config_str, skip_version_check)?; 171 + 172 + if user_config_file.user_configs().is_none() { 173 + info!("No user configurations provided in user config."); 174 + return Ok(()); 175 + } 176 + 177 + let user_config = user_config_file 178 + .user_configs() 179 + .clone() 180 + .expect("Just checked validity"); 181 + 182 + for (protocol_name, protocol_def) in user_config.protocols().clone().unwrap_or_default() { 183 + if let Some(specifiers) = protocol_def.communication() { 184 + dcm_builder.user_communication_specifier(&protocol_name, &specifiers); 185 + } 186 + 187 + if let Some(features) = protocol_def.defaults() { 188 + dcm_builder.protocol_features(&BaseDeviceIdentifier::new_default(&protocol_name), features); 189 + } 190 + 191 + for (config_ident, config) in protocol_def.configurations() { 192 + let ident = BaseDeviceIdentifier::new(&protocol_name, config_ident); 193 + dcm_builder.protocol_features(ident, config.clone()); 194 + } 195 + } 196 + 197 + for user_device_config_pair in user_config.user_device_configs().clone().unwrap_or_default() { 198 + dcm_builder.user_protocol_features( 199 + user_device_config_pair.identifier(), 200 + user_device_config_pair.config(), 201 + ); 202 + } 203 + 204 + Ok(()) 205 + } 206 + 207 + pub fn load_protocol_configs( 208 + main_config_str: &Option<String>, 209 + user_config_str: &Option<String>, 210 + skip_version_check: bool, 211 + ) -> Result<DeviceConfigurationManagerBuilder, ButtplugDeviceError> { 212 + let mut dcm_builder = load_main_config(main_config_str, skip_version_check)?; 213 + 214 + if let Some(config_str) = user_config_str { 215 + load_user_config(config_str, skip_version_check, &mut dcm_builder)?; 216 + } else { 217 + info!("No user configuration provided."); 218 + } 219 + 220 + Ok(dcm_builder) 221 + } 222 + 223 + // TODO Update save_user_config to work with new device structures 224 + /* 225 + pub fn save_user_config(dcm: &DeviceConfigurationManager) -> Result<String, ButtplugError> { 226 + let user_specifiers = dcm.user_communication_specifiers(); 227 + let user_definitions_vec = dcm 228 + .user_device_definitions() 229 + .iter() 230 + .map(|kv| UserDeviceConfigPair { 231 + identifier: kv.key().clone(), 232 + config: kv.value().user_device().clone(), 233 + }) 234 + .collect(); 235 + let user_protos = DashMap::new(); 236 + for spec in user_specifiers { 237 + user_protos.insert( 238 + spec.key().clone(), 239 + ProtocolDefinition { 240 + communication: Some(spec.value().clone()), 241 + ..Default::default() 242 + }, 243 + ); 244 + } 245 + let user_config_definition = UserConfigDefinition { 246 + protocols: Some(user_protos.clone()), 247 + user_device_configs: Some(user_definitions_vec), 248 + }; 249 + let mut user_config_file = UserConfigFile::new(4, 0); 250 + user_config_file.set_user_configs(Some(user_config_definition)); 251 + serde_json::to_string(&user_config_file).map_err(|e| { 252 + ButtplugError::from(ButtplugDeviceError::DeviceConfigurationError(format!( 253 + "Cannot save device configuration file: {e:?}", 254 + ))) 255 + }) 256 + } 257 + */ 258 + 259 + #[cfg(test)] 260 + mod test { 261 + use super::{load_protocol_config_from_json, DEVICE_CONFIGURATION_JSON, base::BaseConfigFile}; 262 + 263 + #[test] 264 + fn test_config_file_parsing() { 265 + load_protocol_config_from_json::<BaseConfigFile>( 266 + &DEVICE_CONFIGURATION_JSON.to_owned(), 267 + true 268 + ).unwrap(); 269 + } 270 + }
+19
crates/buttplug_server_device_config/src/device_config_file/protocol.rs
··· 1 + use std::collections::HashMap; 2 + 3 + use getset::{Getters, MutGetters, Setters}; 4 + use serde::{Deserialize, Serialize}; 5 + 6 + use super::device::ConfigBaseDeviceDefinition; 7 + 8 + use crate::{ProtocolCommunicationSpecifier}; 9 + 10 + #[derive(Deserialize, Serialize, Debug, Clone, Default, Getters, Setters, MutGetters)] 11 + #[getset(get = "pub", set = "pub", get_mut = "pub(crate)")] 12 + pub(super) struct ProtocolDefinition { 13 + #[serde(skip_serializing_if = "Option::is_none")] 14 + pub communication: Option<Vec<ProtocolCommunicationSpecifier>>, 15 + #[serde(skip_serializing_if = "Option::is_none")] 16 + pub defaults: Option<ConfigBaseDeviceDefinition>, 17 + #[serde(default)] 18 + pub configurations: HashMap<String, ConfigBaseDeviceDefinition>, 19 + }
+64
crates/buttplug_server_device_config/src/device_config_file/user.rs
··· 1 + use dashmap::DashMap; 2 + use getset::{Getters, MutGetters, Setters}; 3 + use serde::{Deserialize, Serialize}; 4 + 5 + use crate::UserDeviceIdentifier; 6 + 7 + use super::{get_internal_config_version, device::ConfigUserDeviceDefinition, protocol::ProtocolDefinition, ConfigVersion, ConfigVersionGetter}; 8 + 9 + #[derive(Deserialize, Serialize, Debug, Clone, Getters, Setters, MutGetters)] 10 + #[getset(get = "pub", set = "pub", get_mut = "pub(crate)")] 11 + pub struct UserDeviceConfigPair { 12 + pub identifier: UserDeviceIdentifier, 13 + pub config: ConfigUserDeviceDefinition, 14 + } 15 + 16 + #[derive(Deserialize, Serialize, Debug, Clone, Default, Getters, Setters, MutGetters)] 17 + #[getset(get = "pub", set = "pub", get_mut = "pub")] 18 + pub struct UserConfigDefinition { 19 + #[serde(skip_serializing_if = "Option::is_none")] 20 + pub protocols: Option<DashMap<String, ProtocolDefinition>>, 21 + #[serde(rename = "devices", default, skip_serializing_if = "Option::is_none")] 22 + pub user_device_configs: Option<Vec<UserDeviceConfigPair>>, 23 + } 24 + 25 + #[derive(Deserialize, Serialize, Debug, Getters, Setters)] 26 + #[getset(get = "pub", get_mut = "pub", set = "pub")] 27 + pub struct UserConfigFile { 28 + version: ConfigVersion, 29 + #[serde(rename = "user-configs", default)] 30 + user_configs: Option<UserConfigDefinition>, 31 + } 32 + 33 + impl Default for UserConfigFile { 34 + fn default() -> Self { 35 + Self { 36 + version: get_internal_config_version(), 37 + user_configs: Some(UserConfigDefinition::default()), 38 + } 39 + } 40 + } 41 + 42 + impl ConfigVersionGetter for UserConfigFile { 43 + fn version(&self) -> ConfigVersion { 44 + self.version 45 + } 46 + } 47 + 48 + impl UserConfigFile { 49 + pub fn new(major_version: u32, minor_version: u32) -> Self { 50 + Self { 51 + version: ConfigVersion { 52 + major: major_version, 53 + minor: minor_version, 54 + }, 55 + user_configs: None, 56 + } 57 + } 58 + 59 + #[allow(dead_code)] 60 + pub fn to_json(&self) -> String { 61 + serde_json::to_string(self) 62 + .expect("All types below this are Serialize, so this should be infallible.") 63 + } 64 + }
+328
crates/buttplug_server_device_config/src/device_config_manager.rs
··· 1 + 2 + use buttplug_core::errors::ButtplugDeviceError; 3 + use dashmap::DashMap; 4 + use getset::Getters; 5 + use std::{ 6 + collections::HashMap, 7 + fmt::{self, Debug}, 8 + }; 9 + 10 + use crate::{BaseDeviceIdentifier, ProtocolCommunicationSpecifier, ServerDeviceDefinition, UserDeviceIdentifier}; 11 + 12 + #[derive(Default, Clone)] 13 + pub struct DeviceConfigurationManagerBuilder { 14 + communication_specifiers: HashMap<String, Vec<ProtocolCommunicationSpecifier>>, 15 + user_communication_specifiers: DashMap<String, Vec<ProtocolCommunicationSpecifier>>, 16 + base_device_definitions: HashMap<BaseDeviceIdentifier, ServerDeviceDefinition>, 17 + user_device_definitions: DashMap<UserDeviceIdentifier, ServerDeviceDefinition>, 18 + } 19 + 20 + impl DeviceConfigurationManagerBuilder { 21 + pub fn communication_specifier( 22 + &mut self, 23 + protocol_name: &str, 24 + specifier: &[ProtocolCommunicationSpecifier], 25 + ) -> &mut Self { 26 + self 27 + .communication_specifiers 28 + .entry(protocol_name.to_owned()) 29 + .or_default() 30 + .extend(specifier.iter().cloned()); 31 + self 32 + } 33 + 34 + pub fn protocol_features( 35 + &mut self, 36 + identifier: &BaseDeviceIdentifier, 37 + features: &ServerDeviceDefinition, 38 + ) -> &mut Self { 39 + self 40 + .base_device_definitions 41 + .insert(identifier.clone(), features.clone()); 42 + self 43 + } 44 + 45 + pub fn user_communication_specifier( 46 + &mut self, 47 + protocol_name: &str, 48 + specifier: &[ProtocolCommunicationSpecifier], 49 + ) -> &mut Self { 50 + self 51 + .user_communication_specifiers 52 + .entry(protocol_name.to_owned()) 53 + .or_default() 54 + .extend(specifier.iter().cloned()); 55 + self 56 + } 57 + 58 + pub fn user_protocol_features( 59 + &mut self, 60 + identifier: &UserDeviceIdentifier, 61 + features: &ServerDeviceDefinition, 62 + ) -> &mut Self { 63 + if let Some((_, base_definition)) = self 64 + .base_device_definitions 65 + .iter() 66 + .find(|(_, x)| x.id() == features.base_id()) 67 + { 68 + self.user_device_definitions.insert( 69 + identifier.clone(), 70 + ServerDeviceDefinition::new(base_definition, features), 71 + ); 72 + } else { 73 + error!( 74 + "Cannot find protocol with base id {} for user id {}", 75 + features.base_id(), 76 + features.id() 77 + ) 78 + } 79 + self 80 + } 81 + 82 + pub fn finish(&mut self) -> Result<DeviceConfigurationManager, ButtplugDeviceError> { 83 + // Build and validate the protocol attributes tree. 84 + let mut attribute_tree_map = HashMap::new(); 85 + 86 + // Add all the defaults first, they won't have parent attributes. 87 + for (ident, attr) in &self.base_device_definitions { 88 + /* 89 + for feature in attr.features() { 90 + if let Err(e) = feature.is_valid() { 91 + error!("Feature {attr:?} for ident {ident:?} is not valid, skipping addition: {e:?}"); 92 + continue; 93 + } 94 + } 95 + */ 96 + attribute_tree_map.insert(ident.clone(), attr.clone()); 97 + } 98 + 99 + let user_attribute_tree_map = DashMap::new(); 100 + // Finally, add in user configurations, which will have an address. 101 + for kv in &self.user_device_definitions { 102 + let (ident, attr) = (kv.key(), kv.value()); 103 + for feature in attr.features() { 104 + if let Err(e) = feature.is_valid() { 105 + error!("Feature {attr:?} for ident {ident:?} is not valid, skipping addition: {e:?}"); 106 + continue; 107 + } 108 + } 109 + user_attribute_tree_map.insert(kv.key().clone(), kv.value().clone()); 110 + } 111 + 112 + Ok(DeviceConfigurationManager { 113 + base_communication_specifiers: self.communication_specifiers.clone(), 114 + user_communication_specifiers: self.user_communication_specifiers.clone(), 115 + base_device_definitions: attribute_tree_map, 116 + user_device_definitions: user_attribute_tree_map, 117 + //protocol_map, 118 + }) 119 + } 120 + } 121 + 122 + /// Correlates information about protocols and which devices they support. 123 + /// 124 + /// The [DeviceConfigurationManager] handles stores information about which device protocols the 125 + /// library supports, as well as which devices can use those protocols. When a 126 + /// [DeviceCommunicationManager](crate::server::device::communication_manager) finds a device during scanning, 127 + /// device information is given to the [DeviceConfigurationManager] to decide whether Buttplug 128 + /// should try to connect to and communicate with the device. 129 + /// 130 + /// Assuming the device is supported by the library, the [DeviceConfigurationManager] also stores 131 + /// information about what commands can be sent to the device (Vibrate, Rotate, etc...), and the 132 + /// parameters for those commands (number of power levels, stroke distances, etc...). 133 + #[derive(Getters)] 134 + #[getset(get = "pub")] 135 + pub struct DeviceConfigurationManager { 136 + /// Communication specifiers from the base device config, mapped from protocol name to vector of 137 + /// specifiers. Should not change/update during a session. 138 + base_communication_specifiers: HashMap<String, Vec<ProtocolCommunicationSpecifier>>, 139 + /// Device definitions from the base device config. Should not change/update during a session. 140 + base_device_definitions: HashMap<BaseDeviceIdentifier, ServerDeviceDefinition>, 141 + /// Communication specifiers provided by the user, mapped from protocol name to vector of 142 + /// specifiers. Loaded at session start, may change over life of session. 143 + user_communication_specifiers: DashMap<String, Vec<ProtocolCommunicationSpecifier>>, 144 + /// Device definitions from the user device config. Loaded at session start, may change over life 145 + /// of session. 146 + user_device_definitions: DashMap<UserDeviceIdentifier, ServerDeviceDefinition>, 147 + } 148 + 149 + impl Debug for DeviceConfigurationManager { 150 + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 151 + f.debug_struct("DeviceConfigurationManager").finish() 152 + } 153 + } 154 + 155 + impl Default for DeviceConfigurationManager { 156 + fn default() -> Self { 157 + // Unwrap allowed here because we assume our built in device config will 158 + // always work. System won't pass tests or possibly even build otherwise. 159 + DeviceConfigurationManagerBuilder::default() 160 + .finish() 161 + .expect("Default creation of a DCM should always work.") 162 + } 163 + } 164 + 165 + impl DeviceConfigurationManager { 166 + pub fn add_user_communication_specifier( 167 + &self, 168 + protocol: &str, 169 + specifier: &ProtocolCommunicationSpecifier, 170 + ) -> Result<(), ButtplugDeviceError> { 171 + //self.protocol_map.contains_key(protocol); 172 + self 173 + .user_communication_specifiers 174 + .entry(protocol.to_owned()) 175 + .or_default() 176 + .push(specifier.clone()); 177 + Ok(()) 178 + } 179 + 180 + pub fn remove_user_communication_specifier( 181 + &self, 182 + protocol: &str, 183 + specifier: &ProtocolCommunicationSpecifier, 184 + ) { 185 + if let Some(mut specifiers) = self.user_communication_specifiers.get_mut(protocol) { 186 + let specifier_vec = specifiers.value_mut(); 187 + *specifier_vec = specifier_vec 188 + .iter() 189 + .filter(|s| *specifier != **s) 190 + .cloned() 191 + .collect(); 192 + } 193 + } 194 + 195 + pub fn add_user_device_definition( 196 + &self, 197 + identifier: &UserDeviceIdentifier, 198 + definition: &ServerDeviceDefinition, 199 + ) -> Result<(), ButtplugDeviceError> { 200 + //self.protocol_map.contains_key(identifier.protocol()); 201 + // Check validity of device 202 + let mut index = definition.user_config().index(); 203 + let indexes: Vec<u32> = self.user_device_definitions().iter().map(|x| x.value().user_config().index()).collect(); 204 + // If we just added 1 to the maximum value of the current indexes, someone decides to set an 205 + // index to u32::MAX-1, then we'd have a problem. This is kind of a shit solution but it'll work 206 + // quickly for anyone that's not actively fucking with us by manually playing with user config files. 207 + while indexes.contains(&index) { 208 + index = index.wrapping_add(1); 209 + } 210 + let mut def = definition.clone(); 211 + *def.user_device_mut().user_config_mut().index_mut() = index; 212 + self 213 + .user_device_definitions 214 + .entry(identifier.clone()) 215 + .insert(def); 216 + Ok(()) 217 + } 218 + 219 + pub fn remove_user_device_definition(&self, identifier: &UserDeviceIdentifier) { 220 + self.user_device_definitions.remove(identifier); 221 + } 222 + 223 + pub fn address_allowed(&self, address: &str) -> bool { 224 + // Make sure the device isn't on the deny list 225 + if self 226 + .user_device_definitions 227 + .iter() 228 + .any(|kv| kv.key().address() == address && kv.value().user_config().deny()) 229 + { 230 + // If device is outright denied, deny 231 + info!( 232 + "Device {} denied by configuration, not connecting.", 233 + address 234 + ); 235 + false 236 + } else if self 237 + .user_device_definitions 238 + .iter() 239 + .any(|kv| kv.value().user_config().allow()) 240 + && !self 241 + .user_device_definitions 242 + .iter() 243 + .any(|kv| kv.key().address() == address && kv.value().user_config().allow()) 244 + { 245 + // If device is not on allow list and allow list isn't empty, deny 246 + info!( 247 + "Device {} not on allow list and allow list not empty, not connecting.", 248 + address 249 + ); 250 + false 251 + } else { 252 + true 253 + } 254 + } 255 + 256 + fn device_index(&self, identifier: &UserDeviceIdentifier) -> u32 { 257 + // See if we have a reserved or reusable device index here. 258 + if let Some(config) = self.user_device_definitions.get(identifier) { 259 + let index = config.user_config().index(); 260 + debug!("Found index {index} for device {identifier:?}"); 261 + return index; 262 + } 263 + 264 + let current_indexes: Vec<u32> = self 265 + .user_device_definitions 266 + .iter() 267 + .map(|x| x.user_config().index()) 268 + .collect(); 269 + 270 + // Someone is gonna make a max device index in their config file just to fuck with me, therefore 271 + // we don't do "max + 1", we fill in holes (lol) in sequences. To whomever has 4 billion sex toys: 272 + // sorry your index finding for new devices is slow and takes 16GB of allocation every time we 273 + // want to search the index space. 274 + 275 + let mut index = 0; 276 + while current_indexes.contains(&index) { 277 + index += 1; 278 + } 279 + debug!("Generating and assigning index {index:?} for device {identifier:?}"); 280 + index 281 + } 282 + 283 + /// Provides read-only access to the internal protocol/identifier map. Mainly 284 + /// used for WebBluetooth filter construction, but could also be handy for 285 + /// listing capabilities in UI, etc. 286 + pub fn protocol_device_configurations( 287 + &self, 288 + ) -> HashMap<String, Vec<ProtocolCommunicationSpecifier>> { 289 + self.base_communication_specifiers.clone() 290 + } 291 + 292 + pub fn device_definition(&self, identifier: &UserDeviceIdentifier) -> Option<DeviceDefinition> { 293 + let features = if let Some(attrs) = self.user_device_definitions.get(identifier) { 294 + debug!("User device config found for {:?}", identifier); 295 + attrs.clone() 296 + } else if let Some(attrs) = self.base_device_definitions.get(&BaseDeviceIdentifier::new( 297 + identifier.protocol(), 298 + identifier.identifier(), 299 + )) { 300 + debug!( 301 + "Protocol + Identifier device config found for {:?}", 302 + identifier 303 + ); 304 + DeviceDefinition::new_from_base_definition(attrs, self.device_index(identifier)) 305 + } else if let Some(attrs) = self 306 + .base_device_definitions 307 + .get(&BaseDeviceIdentifier::new(identifier.protocol(), &None)) 308 + { 309 + debug!("Protocol device config found for {:?}", identifier); 310 + DeviceDefinition::new_from_base_definition(attrs, self.device_index(identifier)) 311 + } else { 312 + return None; 313 + }; 314 + 315 + // If this is a new device, it needs to be added to the user device definition map. 316 + // 317 + // Device definitions are looked up before we fully initialize a device, mostly for algorithm 318 + // preparation. There is a very small chance we may save the device config then error out when 319 + // we connect to the device, but we'll assume we may connect successfully later. 320 + if self.user_device_definitions.get(identifier).is_none() { 321 + self 322 + .user_device_definitions 323 + .insert(identifier.clone(), features.clone()); 324 + } 325 + 326 + Some(features) 327 + } 328 + }
-442
crates/buttplug_server_device_config/src/device_configuration.rs
··· 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 - 8 - use super::{ 9 - BaseDeviceDefinition, 10 - BaseDeviceIdentifier, 11 - DeviceConfigurationManager, 12 - DeviceConfigurationManagerBuilder, 13 - DeviceSettings, 14 - ProtocolCommunicationSpecifier, 15 - ServerBaseDeviceFeature, 16 - UserDeviceDefinition, 17 - UserDeviceIdentifier, 18 - }; 19 - use buttplug_core::{ 20 - errors::{ButtplugDeviceError, ButtplugError}, 21 - util::json::JSONValidator, 22 - }; 23 - use dashmap::DashMap; 24 - use getset::{CopyGetters, Getters, MutGetters, Setters}; 25 - use serde::{Deserialize, Serialize}; 26 - use std::{collections::HashMap, fmt::Display}; 27 - use uuid::Uuid; 28 - 29 - pub static DEVICE_CONFIGURATION_JSON: &str = 30 - include_str!("../build-config/buttplug-device-config-v4.json"); 31 - static DEVICE_CONFIGURATION_JSON_SCHEMA: &str = include_str!( 32 - "../device-config-v4/buttplug-device-config-schema-v4.json" 33 - ); 34 - 35 - /// The top level configuration for a protocol. Contains all data about devices that can use the 36 - /// protocol, as well as names, message attributes, etc... for different devices. 37 - /// 38 - /// Example: A Kiiroo ProtocolDeviceConfiguration would contain the Bluetooth LE information for all 39 - /// devices supported under the Kiiroo protocol. It would also contain information about the names 40 - /// and capabilities of different Kiiroo devices (Cliona, Onyx, Keon, etc...). 41 - #[derive(Debug, Clone, Getters, MutGetters, Default)] 42 - struct ProtocolDeviceConfiguration { 43 - /// BLE/USB/etc info for device identification. 44 - #[getset(get = "pub(crate)", get_mut = "pub(crate)")] 45 - specifiers: Vec<ProtocolCommunicationSpecifier>, 46 - /// Names and message attributes for all possible devices that use this protocol 47 - #[getset(get = "pub(crate)", get_mut = "pub(crate)")] 48 - configurations: HashMap<Option<String>, BaseDeviceDefinition>, 49 - } 50 - 51 - impl ProtocolDeviceConfiguration { 52 - /// Create a new instance 53 - pub fn new( 54 - specifiers: Vec<ProtocolCommunicationSpecifier>, 55 - configurations: HashMap<Option<String>, BaseDeviceDefinition>, 56 - ) -> Self { 57 - Self { 58 - specifiers, 59 - configurations, 60 - } 61 - } 62 - } 63 - 64 - #[derive(Serialize, Deserialize, Debug, Clone, Default, Getters, Setters, MutGetters)] 65 - #[getset(get = "pub", set = "pub", get_mut = "pub(crate)")] 66 - struct ProtocolAttributes { 67 - #[serde(skip_serializing_if = "Option::is_none")] 68 - identifier: Option<Vec<String>>, 69 - name: String, 70 - id: Uuid, 71 - #[serde(skip_serializing_if = "Option::is_none", rename = "protocol-variant")] 72 - protocol_variant: Option<String>, 73 - #[serde(skip_serializing_if = "Option::is_none")] 74 - features: Option<Vec<ServerBaseDeviceFeature>>, 75 - #[serde(skip_serializing_if = "Option::is_none")] 76 - device_settings: Option<DeviceSettings>, 77 - } 78 - 79 - #[derive(Deserialize, Serialize, Debug, Clone, Default, Getters, Setters, MutGetters)] 80 - #[getset(get = "pub", set = "pub", get_mut = "pub(crate)")] 81 - struct ProtocolDefinition { 82 - #[serde(skip_serializing_if = "Option::is_none")] 83 - pub communication: Option<Vec<ProtocolCommunicationSpecifier>>, 84 - #[serde(skip_serializing_if = "Option::is_none")] 85 - pub defaults: Option<ProtocolAttributes>, 86 - #[serde(default)] 87 - pub configurations: Vec<ProtocolAttributes>, 88 - } 89 - 90 - #[derive(Deserialize, Serialize, Debug, Clone, Getters, Setters, MutGetters)] 91 - #[getset(get = "pub", set = "pub", get_mut = "pub(crate)")] 92 - struct UserDeviceConfigPair { 93 - identifier: UserDeviceIdentifier, 94 - config: UserDeviceDefinition, 95 - } 96 - 97 - #[derive(Deserialize, Serialize, Debug, Clone, Default, Getters, Setters, MutGetters)] 98 - #[getset(get = "pub", set = "pub", get_mut = "pub")] 99 - struct UserConfigDefinition { 100 - #[serde(skip_serializing_if = "Option::is_none")] 101 - protocols: Option<DashMap<String, ProtocolDefinition>>, 102 - #[serde(rename = "devices", default, skip_serializing_if = "Option::is_none")] 103 - user_device_configs: Option<Vec<UserDeviceConfigPair>>, 104 - } 105 - 106 - impl From<ProtocolDefinition> for ProtocolDeviceConfiguration { 107 - fn from(protocol_def: ProtocolDefinition) -> Self { 108 - let mut configurations = HashMap::new(); 109 - 110 - if let Some(defaults) = protocol_def.defaults() { 111 - let config_attrs = BaseDeviceDefinition::new( 112 - &defaults.name, 113 - &defaults.id, 114 - &defaults.protocol_variant, 115 - defaults 116 - .features 117 - .as_ref() 118 - .expect("This is a default, therefore we'll always have features."), 119 - &defaults.device_settings, 120 - ); 121 - configurations.insert(None, config_attrs); 122 - } 123 - for config in &protocol_def.configurations { 124 - if let Some(identifiers) = &config.identifier { 125 - for identifier in identifiers { 126 - let config_attrs = BaseDeviceDefinition::new( 127 - // Even subconfigurations always have names 128 - &config.name, 129 - &config.id, 130 - &config.protocol_variant, 131 - config.features.as_ref().unwrap_or( 132 - protocol_def 133 - .defaults() 134 - .as_ref() 135 - .unwrap_or(&ProtocolAttributes::default()) 136 - .features 137 - .as_ref() 138 - .unwrap_or(&vec![]), 139 - ), 140 - &config.device_settings, 141 - ); 142 - configurations.insert(Some(identifier.to_owned()), config_attrs); 143 - } 144 - } 145 - } 146 - 147 - Self::new( 148 - protocol_def.communication.unwrap_or_default(), 149 - configurations, 150 - ) 151 - } 152 - } 153 - 154 - #[derive(Deserialize, Serialize, Debug, CopyGetters, Clone, Copy)] 155 - #[getset(get_copy = "pub", get_mut = "pub")] 156 - struct ConfigVersion { 157 - pub major: u32, 158 - pub minor: u32, 159 - } 160 - 161 - impl Display for ConfigVersion { 162 - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 163 - write!(f, "{}.{}", self.major, self.minor) 164 - } 165 - } 166 - 167 - trait ConfigVersionGetter { 168 - fn version(&self) -> ConfigVersion; 169 - } 170 - 171 - #[derive(Deserialize, Serialize, Debug, Getters)] 172 - #[getset(get = "pub", get_mut = "pub", set = "pub")] 173 - pub struct BaseConfigFile { 174 - version: ConfigVersion, 175 - #[serde(default, skip_serializing_if = "Option::is_none")] 176 - protocols: Option<HashMap<String, ProtocolDefinition>>, 177 - } 178 - 179 - impl Default for BaseConfigFile { 180 - fn default() -> Self { 181 - Self { 182 - version: get_internal_config_version(), 183 - protocols: Some(HashMap::new()), 184 - } 185 - } 186 - } 187 - 188 - impl ConfigVersionGetter for BaseConfigFile { 189 - fn version(&self) -> ConfigVersion { 190 - self.version 191 - } 192 - } 193 - 194 - impl BaseConfigFile { 195 - pub fn new(major_version: u32, minor_version: u32) -> Self { 196 - Self { 197 - version: ConfigVersion { 198 - major: major_version, 199 - minor: minor_version, 200 - }, 201 - protocols: None, 202 - } 203 - } 204 - } 205 - 206 - #[derive(Deserialize, Serialize, Debug, Getters)] 207 - #[getset(get = "pub", get_mut = "pub", set = "pub")] 208 - struct UserConfigFile { 209 - version: ConfigVersion, 210 - #[serde(rename = "user-configs", default)] 211 - user_configs: Option<UserConfigDefinition>, 212 - } 213 - 214 - impl Default for UserConfigFile { 215 - fn default() -> Self { 216 - Self { 217 - version: get_internal_config_version(), 218 - user_configs: Some(UserConfigDefinition::default()), 219 - } 220 - } 221 - } 222 - 223 - impl ConfigVersionGetter for UserConfigFile { 224 - fn version(&self) -> ConfigVersion { 225 - self.version 226 - } 227 - } 228 - 229 - impl UserConfigFile { 230 - pub fn new(major_version: u32, minor_version: u32) -> Self { 231 - Self { 232 - version: ConfigVersion { 233 - major: major_version, 234 - minor: minor_version, 235 - }, 236 - user_configs: None, 237 - } 238 - } 239 - 240 - #[allow(dead_code)] 241 - pub fn to_json(&self) -> String { 242 - serde_json::to_string(self) 243 - .expect("All types below this are Serialize, so this should be infallible.") 244 - } 245 - } 246 - 247 - fn get_internal_config_version() -> ConfigVersion { 248 - let config: BaseConfigFile = serde_json::from_str(DEVICE_CONFIGURATION_JSON) 249 - .expect("If this fails, the whole library goes with it."); 250 - config.version 251 - } 252 - 253 - fn load_protocol_config_from_json<'a, T>( 254 - config_str: &'a str, 255 - skip_version_check: bool, 256 - ) -> Result<T, ButtplugDeviceError> 257 - where 258 - T: ConfigVersionGetter + Deserialize<'a>, 259 - { 260 - let config_validator = JSONValidator::new(DEVICE_CONFIGURATION_JSON_SCHEMA); 261 - match config_validator.validate(config_str) { 262 - Ok(_) => match serde_json::from_str::<T>(config_str) { 263 - Ok(protocol_config) => { 264 - let internal_config_version = get_internal_config_version(); 265 - if !skip_version_check && protocol_config.version().major != internal_config_version.major { 266 - Err(ButtplugDeviceError::DeviceConfigurationError(format!( 267 - "Device configuration file major version {} is different than internal major version {}. Cannot load external files that do not have matching major version numbers.", 268 - protocol_config.version(), 269 - internal_config_version 270 - ))) 271 - } else { 272 - Ok(protocol_config) 273 - } 274 - } 275 - Err(err) => Err(ButtplugDeviceError::DeviceConfigurationError(format!( 276 - "{err}" 277 - ))), 278 - }, 279 - Err(err) => Err(ButtplugDeviceError::DeviceConfigurationError(format!( 280 - "{err}" 281 - ))), 282 - } 283 - } 284 - 285 - fn load_main_config( 286 - main_config_str: &Option<String>, 287 - skip_version_check: bool, 288 - ) -> Result<DeviceConfigurationManagerBuilder, ButtplugDeviceError> { 289 - if main_config_str.is_some() { 290 - info!("Loading from custom base device configuration...") 291 - } else { 292 - info!("Loading from internal base device configuration...") 293 - } 294 - // Start by loading the main config 295 - let main_config = load_protocol_config_from_json::<BaseConfigFile>( 296 - main_config_str 297 - .as_ref() 298 - .unwrap_or(&DEVICE_CONFIGURATION_JSON.to_owned()), 299 - skip_version_check, 300 - )?; 301 - 302 - info!("Loaded config version {:?}", main_config.version); 303 - 304 - let mut dcm_builder = DeviceConfigurationManagerBuilder::default(); 305 - 306 - // Each protocol will need to become a ProtocolDeviceConfiguration, so we'll need to 307 - // 308 - // - take the specifiers from both the main and user configs and make a vector out of them 309 - // - for each configuration and user config, we'll need to create message lists and figure out 310 - // what to do with allow/deny/index. 311 - 312 - let mut protocol_specifiers = HashMap::new(); 313 - let mut protocol_features = HashMap::new(); 314 - 315 - // Iterate through all of the protocols in the main config first and build up a map of protocol 316 - // name to ProtocolDeviceConfiguration structs. 317 - for (protocol_name, protocol_def) in main_config.protocols.unwrap_or_default() { 318 - let protocol_device_config: ProtocolDeviceConfiguration = protocol_def.into(); 319 - protocol_specifiers.insert( 320 - protocol_name.clone(), 321 - protocol_device_config.specifiers().clone(), 322 - ); 323 - for (config_ident, config) in protocol_device_config.configurations() { 324 - let ident = BaseDeviceIdentifier::new(&protocol_name, config_ident); 325 - protocol_features.insert(ident, config.clone()); 326 - } 327 - } 328 - 329 - for (name, specifiers) in &protocol_specifiers { 330 - dcm_builder.communication_specifier(name, specifiers); 331 - } 332 - 333 - for (ident, features) in protocol_features { 334 - dcm_builder.protocol_features(&ident, &features); 335 - } 336 - 337 - Ok(dcm_builder) 338 - } 339 - 340 - fn load_user_config( 341 - user_config_str: &str, 342 - skip_version_check: bool, 343 - dcm_builder: &mut DeviceConfigurationManagerBuilder, 344 - ) -> Result<(), ButtplugDeviceError> { 345 - info!("Loading user configuration from string."); 346 - let user_config_file = 347 - load_protocol_config_from_json::<UserConfigFile>(user_config_str, skip_version_check)?; 348 - 349 - if user_config_file.user_configs.is_none() { 350 - info!("No user configurations provided in user config."); 351 - return Ok(()); 352 - } 353 - 354 - let user_config = user_config_file 355 - .user_configs 356 - .expect("Just checked validity"); 357 - 358 - let mut protocol_specifiers = HashMap::new(); 359 - let mut protocol_features = HashMap::new(); 360 - 361 - for (protocol_name, protocol_def) in user_config.protocols.unwrap_or_default() { 362 - if let Some(comm_specifiers) = protocol_def.communication() { 363 - dcm_builder.user_communication_specifier(&protocol_name, comm_specifiers); 364 - } 365 - info!("{:?}", protocol_def); 366 - info!("Adding {}", protocol_name); 367 - let protocol_device_config: ProtocolDeviceConfiguration = protocol_def.into(); 368 - protocol_specifiers.insert( 369 - protocol_name.clone(), 370 - protocol_device_config.specifiers().clone(), 371 - ); 372 - info!("{:?}", protocol_device_config); 373 - for (config_ident, config) in protocol_device_config.configurations() { 374 - info!("Adding {:?}", config_ident); 375 - let ident = BaseDeviceIdentifier::new(&protocol_name, config_ident); 376 - protocol_features.insert(ident, config.clone()); 377 - } 378 - } 379 - 380 - for (ident, features) in protocol_features { 381 - info!("Adding {}", features.id()); 382 - dcm_builder.protocol_features(&ident, &features); 383 - } 384 - 385 - for user_device_config_pair in user_config.user_device_configs.unwrap_or_default() { 386 - dcm_builder.user_protocol_features( 387 - user_device_config_pair.identifier(), 388 - user_device_config_pair.config(), 389 - ); 390 - } 391 - 392 - Ok(()) 393 - } 394 - 395 - pub fn load_protocol_configs( 396 - main_config_str: &Option<String>, 397 - user_config_str: &Option<String>, 398 - skip_version_check: bool, 399 - ) -> Result<DeviceConfigurationManagerBuilder, ButtplugDeviceError> { 400 - let mut dcm_builder = load_main_config(main_config_str, skip_version_check)?; 401 - 402 - if let Some(config_str) = user_config_str { 403 - load_user_config(config_str, skip_version_check, &mut dcm_builder)?; 404 - } else { 405 - info!("No user configuration provided."); 406 - } 407 - 408 - Ok(dcm_builder) 409 - } 410 - 411 - pub fn save_user_config(dcm: &DeviceConfigurationManager) -> Result<String, ButtplugError> { 412 - let user_specifiers = dcm.user_communication_specifiers(); 413 - let user_definitions_vec = dcm 414 - .user_device_definitions() 415 - .iter() 416 - .map(|kv| UserDeviceConfigPair { 417 - identifier: kv.key().clone(), 418 - config: kv.value().user_device().clone(), 419 - }) 420 - .collect(); 421 - let user_protos = DashMap::new(); 422 - for spec in user_specifiers { 423 - user_protos.insert( 424 - spec.key().clone(), 425 - ProtocolDefinition { 426 - communication: Some(spec.value().clone()), 427 - ..Default::default() 428 - }, 429 - ); 430 - } 431 - let user_config_definition = UserConfigDefinition { 432 - protocols: Some(user_protos.clone()), 433 - user_device_configs: Some(user_definitions_vec), 434 - }; 435 - let mut user_config_file = UserConfigFile::new(4, 0); 436 - user_config_file.user_configs = Some(user_config_definition); 437 - serde_json::to_string(&user_config_file).map_err(|e| { 438 - ButtplugError::from(ButtplugDeviceError::DeviceConfigurationError(format!( 439 - "Cannot save device configuration file: {e:?}", 440 - ))) 441 - }) 442 - }
+51 -216
crates/buttplug_server_device_config/src/device_definitions.rs
··· 1 - use std::time::Duration; 2 - 3 - use getset::{CopyGetters, Getters, MutGetters}; 4 - use serde::{Deserialize, Serialize}; 1 + use getset::{CopyGetters, Getters}; 5 2 use uuid::Uuid; 6 3 7 4 use super::device_feature::{ 8 - ServerBaseDeviceFeature, 9 5 ServerDeviceFeature, 10 - ServerUserDeviceFeature, 11 - ServerUserDeviceFeatureOutput, 12 6 }; 13 - use buttplug_core::message::OutputType; 14 - 15 - #[derive(Serialize, Deserialize, Debug, Clone, Default, CopyGetters)] 16 - pub struct DeviceSettings { 17 - #[serde( 18 - rename = "message-gap-ms", 19 - skip_serializing_if = "Option::is_none", 20 - default 21 - )] 22 - #[getset(get_copy = "pub")] 23 - message_gap_ms: Option<u32>, 24 - } 25 - 26 - impl DeviceSettings { 27 - pub fn is_none(&self) -> bool { 28 - self.message_gap_ms.is_none() 29 - } 30 - } 31 - 32 - #[derive(Serialize, Deserialize, Debug, Clone, Default, CopyGetters)] 33 - pub struct BaseFeatureSettings { 34 - #[serde( 35 - rename = "alt-protocol-index", 36 - skip_serializing_if = "Option::is_none", 37 - default 38 - )] 39 - #[getset(get_copy = "pub")] 40 - alt_protocol_index: Option<u32>, 41 - } 42 - 43 - impl BaseFeatureSettings { 44 - pub fn is_none(&self) -> bool { 45 - self.alt_protocol_index.is_none() 46 - } 47 - } 48 - 49 - #[derive(Serialize, Deserialize, Debug, Clone, Default)] 50 - pub struct UserFeatureSettings { 51 - #[serde( 52 - rename = "reverse-position", 53 - skip_serializing_if = "Option::is_none", 54 - default 55 - )] 56 - reverse_position: Option<bool>, 57 - } 58 - 59 - impl UserFeatureSettings { 60 - pub fn is_none(&self) -> bool { 61 - self.reverse_position.is_none() 62 - } 63 - } 64 - 65 7 #[derive(Debug, Clone, Getters, CopyGetters)] 66 - pub struct BaseDeviceDefinition { 8 + pub struct ServerDeviceDefinition { 67 9 #[getset(get = "pub")] 68 10 /// Given name of the device this instance represents. 69 11 name: String, 70 - #[getset(get = "pub")] 71 - /// Message attributes for this device instance. 72 - features: Vec<ServerBaseDeviceFeature>, 73 12 #[getset(get_copy = "pub")] 74 13 id: Uuid, 14 + #[getset(get_copy = "pub")] 15 + base_id: Option<Uuid>, 75 16 #[getset(get = "pub")] 76 17 protocol_variant: Option<String>, 77 - #[getset(get = "pub")] 78 - device_settings: DeviceSettings, 79 - } 80 - 81 - impl BaseDeviceDefinition { 82 - /// Create a new instance 83 - pub fn new( 84 - name: &str, 85 - id: &Uuid, 86 - protocol_variant: &Option<String>, 87 - features: &[ServerBaseDeviceFeature], 88 - device_settings: &Option<DeviceSettings>, 89 - ) -> Self { 90 - Self { 91 - name: name.to_owned(), 92 - features: features.into(), 93 - id: *id, 94 - protocol_variant: protocol_variant.clone(), 95 - device_settings: device_settings.clone().unwrap_or_default(), 96 - } 97 - } 98 - } 99 - 100 - #[derive(Serialize, Deserialize, Debug, Getters, CopyGetters, Default, Clone, MutGetters)] 101 - pub struct UserDeviceCustomization { 102 - #[serde( 103 - rename = "display-name", 104 - default, 105 - skip_serializing_if = "Option::is_none" 106 - )] 18 + #[getset(get_copy = "pub")] 19 + message_gap_ms: Option<u32>, 107 20 #[getset(get = "pub")] 108 21 display_name: Option<String>, 109 - #[serde(default)] 110 22 #[getset(get_copy = "pub")] 111 23 allow: bool, 112 - #[serde(default)] 113 24 #[getset(get_copy = "pub")] 114 25 deny: bool, 115 - #[getset(get_copy = "pub", get_mut = "pub")] 26 + #[getset(get_copy = "pub")] 116 27 index: u32, 117 - #[getset(get_copy = "pub")] 118 - #[serde( 119 - rename = "message-gap-ms", 120 - default, 121 - skip_serializing_if = "Option::is_none" 122 - )] 123 - message_gap_ms: Option<u32>, 28 + #[getset(get = "pub")] 29 + features: Vec<ServerDeviceFeature> 124 30 } 125 31 126 - impl UserDeviceCustomization { 127 - pub fn new( 128 - display_name: &Option<String>, 129 - allow: bool, 130 - deny: bool, 131 - index: u32, 132 - message_gap_ms: Option<u32>, 133 - ) -> Self { 134 - Self { 135 - display_name: display_name.clone(), 136 - allow, 137 - deny, 138 - index, 139 - message_gap_ms, 140 - } 141 - } 142 - 143 - pub fn default_with_index(index: u32) -> Self { 144 - Self::new(&None, false, false, index, None) 145 - } 32 + pub struct ServerDeviceDefinitionBuilder { 33 + def: ServerDeviceDefinition, 146 34 } 147 35 148 - #[derive(Debug, Clone, Getters, MutGetters, Serialize, Deserialize, CopyGetters)] 149 - pub struct UserDeviceDefinition { 150 - #[getset(get_copy = "pub")] 151 - id: Uuid, 152 - #[getset(get_copy = "pub")] 153 - #[serde(rename = "base-id")] 154 - base_id: Uuid, 155 - #[getset(get = "pub")] 156 - /// Message attributes for this device instance. 157 - #[getset(get = "pub", get_mut = "pub")] 158 - features: Vec<ServerUserDeviceFeature>, 159 - #[getset(get = "pub", get_mut = "pub")] 160 - #[serde(rename = "user-config")] 161 - /// Per-user configurations specific to this device instance. 162 - user_config: UserDeviceCustomization, 163 - } 164 - 165 - impl UserDeviceDefinition { 166 - fn new(index: u32, base_id: Uuid, features: &Vec<ServerUserDeviceFeature>) -> Self { 36 + impl ServerDeviceDefinitionBuilder { 37 + pub fn new(name: &str, id: &Uuid) -> Self { 167 38 Self { 168 - id: Uuid::new_v4(), 169 - base_id, 170 - features: features.clone(), 171 - user_config: UserDeviceCustomization::default_with_index(index), 39 + def: ServerDeviceDefinition { 40 + name: name.to_owned(), 41 + id: id.clone(), 42 + base_id: None, 43 + protocol_variant: None, 44 + message_gap_ms: None, 45 + display_name: None, 46 + allow: false, 47 + deny: false, 48 + index: 0, 49 + features: vec!() 50 + }, 172 51 } 173 52 } 174 - } 175 53 176 - #[derive(Debug, Clone, Getters, CopyGetters, MutGetters)] 177 - pub struct DeviceDefinition { 178 - #[getset(get = "pub")] 179 - base_device: BaseDeviceDefinition, 180 - #[getset(get = "pub", get_mut = "pub")] 181 - user_device: UserDeviceDefinition, 182 - } 183 - 184 - impl DeviceDefinition { 185 - /// Create a new instance 186 - pub fn new(base_device: &BaseDeviceDefinition, user_device: &UserDeviceDefinition) -> Self { 187 - Self { 188 - base_device: base_device.clone(), 189 - user_device: user_device.clone(), 190 - } 54 + pub fn base_id(&mut self, id: &Uuid) -> &mut Self { 55 + self.def.base_id = Some(id.clone()); 56 + self 191 57 } 192 58 193 - pub fn id(&self) -> Uuid { 194 - self.user_device.id() 59 + pub fn display_name(&mut self, name: &str) -> &mut Self { 60 + self.def.display_name = Some(name.to_owned()); 61 + self 195 62 } 196 63 197 - pub fn name(&self) -> &str { 198 - self.base_device.name() 64 + pub fn protocol_variant(&mut self, variant: &str) -> &mut Self { 65 + self.def.protocol_variant = Some(variant.to_owned()); 66 + self 199 67 } 200 68 201 - pub fn protocol_variant(&self) -> &Option<String> { 202 - self.base_device.protocol_variant() 69 + pub fn message_gap_ms(&mut self, gap: u32) -> &mut Self { 70 + self.def.message_gap_ms = Some(gap); 71 + self 203 72 } 204 73 205 - pub fn features(&self) -> Vec<ServerDeviceFeature> { 206 - // TODO Gross way to do this. 207 - let mut features: Vec<ServerDeviceFeature> = vec![]; 208 - self.base_device.features().iter().for_each(|x| { 209 - if let Some(user_feature) = self 210 - .user_device 211 - .features 212 - .iter() 213 - .find(|user_feature| user_feature.base_id() == x.id()) 214 - { 215 - features.push(ServerDeviceFeature::new(x, user_feature)); 216 - } 217 - }); 218 - features 74 + pub fn allow(&mut self) -> &mut Self { 75 + self.def.allow = true; 76 + self 219 77 } 220 78 221 - pub fn user_config(&self) -> &UserDeviceCustomization { 222 - self.user_device.user_config() 79 + pub fn deny(&mut self) -> &mut Self { 80 + self.def.deny = true; 81 + self 223 82 } 224 83 225 - pub fn message_gap(&self) -> Option<Duration> { 226 - if let Some(gap) = self.user_device.user_config().message_gap_ms() { 227 - Some(Duration::from_millis(gap.into())) 228 - } else if let Some(gap) = self.base_device.device_settings.message_gap_ms() { 229 - Some(Duration::from_millis(gap.into())) 230 - } else { 231 - None 232 - } 84 + pub fn index(&mut self, index: u32) -> &mut Self { 85 + self.def.index = index; 86 + self 233 87 } 234 88 235 - pub fn new_from_base_definition(def: &BaseDeviceDefinition, index: u32) -> Self { 236 - let user_features = def 237 - .features() 238 - .iter() 239 - .map(|x| x.as_user_device_feature()) 240 - .collect(); 241 - Self::new( 242 - def, 243 - &UserDeviceDefinition::new(index, def.id(), &user_features), 244 - ) 89 + pub fn add_feature(&mut self, feature: &ServerDeviceFeature) -> &mut Self { 90 + self.def.features.push(feature.clone()); 91 + self 245 92 } 246 93 247 - pub fn update_user_output( 248 - &mut self, 249 - feature_id: Uuid, 250 - output_type: OutputType, 251 - user_output: ServerUserDeviceFeatureOutput, 252 - ) { 253 - if let Some(feature) = self 254 - .user_device 255 - .features_mut() 256 - .iter_mut() 257 - .find(|x| x.id() == feature_id) 258 - { 259 - feature.update_output(output_type, &user_output); 260 - } 94 + pub fn finish(self) -> ServerDeviceDefinition { 95 + self.def 261 96 } 262 97 }
+148 -278
crates/buttplug_server_device_config/src/device_feature.rs
··· 5 5 // Licensed under the BSD 3-Clause license. See LICENSE file in the project root 6 6 // for full license information. 7 7 8 - use super::BaseFeatureSettings; 8 + use crate::ButtplugDeviceConfigError; 9 + 9 10 use buttplug_core::{ 10 - errors::ButtplugDeviceError, 11 11 message::{ 12 12 DeviceFeature, 13 13 DeviceFeatureInput, 14 - DeviceFeatureOutput, 15 14 InputCommandType, 16 - InputType, 17 - OutputType, 18 15 }, 19 16 }; 20 - use getset::{CopyGetters, Getters, MutGetters, Setters}; 17 + use getset::{CopyGetters, Getters}; 21 18 use serde::{ 22 - ser::{self, SerializeSeq}, 23 19 Deserialize, 24 20 Serialize, 25 21 Serializer, 22 + ser::{self, SerializeSeq}, 26 23 }; 27 24 use std::{ 28 25 collections::{HashMap, HashSet}, ··· 60 57 seq.end() 61 58 } 62 59 63 - #[derive( 64 - Clone, Debug, Default, Getters, MutGetters, Setters, Serialize, Deserialize, CopyGetters, 65 - )] 66 - pub struct ServerBaseDeviceFeature { 67 - #[getset(get = "pub", get_mut = "pub(super)")] 68 - #[serde(default)] 69 - description: String, 70 - #[getset(get = "pub")] 71 - #[serde(skip_serializing_if = "Option::is_none")] 72 - #[serde(rename = "output")] 73 - output: Option<HashMap<OutputType, ServerBaseDeviceFeatureOutput>>, 74 - #[getset(get = "pub")] 75 - #[serde(skip_serializing_if = "Option::is_none")] 76 - #[serde(rename = "input")] 77 - input: Option<HashMap<InputType, ServerDeviceFeatureInput>>, 78 - #[getset(get_copy = "pub")] 79 - id: Uuid, 80 - #[getset(get = "pub")] 81 - #[serde( 82 - rename = "feature-settings", 83 - skip_serializing_if = "BaseFeatureSettings::is_none", 84 - default 85 - )] 86 - feature_settings: BaseFeatureSettings, 60 + #[derive(Debug, Clone)] 61 + pub struct RangeWithLimit<T: PartialOrd + Clone> { 62 + base: RangeInclusive<T>, 63 + user: Option<RangeInclusive<T>>, 64 + } 65 + 66 + impl<T: PartialOrd + Clone> From<RangeInclusive<T>> for RangeWithLimit<T> { 67 + fn from(value: RangeInclusive<T>) -> Self { 68 + Self::new(&value) 69 + } 87 70 } 88 71 89 - impl ServerBaseDeviceFeature { 90 - pub fn as_user_device_feature(&self) -> ServerUserDeviceFeature { 91 - ServerUserDeviceFeature { 92 - id: Uuid::new_v4(), 93 - base_id: self.id, 94 - output: self.output.as_ref().and_then(|x| { 95 - Some( 96 - x.keys() 97 - .map(|x| (*x, ServerUserDeviceFeatureOutput::default())) 98 - .collect(), 99 - ) 100 - }), 72 + impl<T: PartialOrd + Clone> RangeWithLimit<T> { 73 + pub fn new(base: &RangeInclusive<T>) -> Self { 74 + Self { 75 + base: base.clone(), 76 + user: None 77 + } 78 + } 79 + 80 + pub fn try_new( 81 + base: &RangeInclusive<T>, 82 + user: &Option<RangeInclusive<T>>, 83 + ) -> Result<Self, ButtplugDeviceConfigError<T>> { 84 + if let Some(user) = user { 85 + if user.is_empty() { 86 + Err(ButtplugDeviceConfigError::InvalidUserRange( 87 + (*user).clone(), 88 + (*base).clone(), 89 + )) 90 + } else { 91 + if *user.start() < *base.start() 92 + || *user.end() > *base.end() 93 + || *user.start() > *base.end() 94 + || *user.end() < *base.start() 95 + { 96 + Err(ButtplugDeviceConfigError::InvalidUserRange( 97 + (*user).clone(), 98 + (*base).clone(), 99 + )) 100 + } else { 101 + Ok(Self { 102 + base: (*base).clone(), 103 + user: Some((*user).clone()), 104 + }) 105 + } 106 + } 107 + } else { 108 + if base.is_empty() { 109 + Err(ButtplugDeviceConfigError::BaseRangeRequired) 110 + } else { 111 + Ok(Self { 112 + base: (*base).clone(), 113 + user: None, 114 + }) 115 + } 101 116 } 102 117 } 103 118 } 104 119 105 - #[derive( 106 - Clone, Debug, Default, Getters, MutGetters, Setters, Serialize, Deserialize, CopyGetters, 107 - )] 108 - pub struct ServerUserDeviceFeature { 120 + #[derive(Debug, Clone, Getters, CopyGetters)] 121 + pub struct ServerDeviceFeatureOutputValueProperties { 122 + #[getset(get = "pub")] 123 + value: RangeWithLimit<i32>, 109 124 #[getset(get_copy = "pub")] 110 - id: Uuid, 111 - #[getset(get_copy = "pub")] 112 - #[serde(rename = "base-id")] 113 - base_id: Uuid, 114 - #[getset(get = "pub")] 115 - #[serde(rename = "output", skip_serializing_if = "Option::is_none")] 116 - output: Option<HashMap<OutputType, ServerUserDeviceFeatureOutput>>, 125 + disabled: bool, 117 126 } 118 127 119 - impl ServerUserDeviceFeature { 120 - pub fn update_output(&mut self, output_type: OutputType, output: &ServerUserDeviceFeatureOutput) { 121 - if let Some(ref mut output_map) = self.output { 122 - if output_map.contains_key(&output_type) { 123 - output_map.insert(output_type, output.clone()); 124 - } 128 + impl ServerDeviceFeatureOutputValueProperties { 129 + pub fn new(value: &RangeWithLimit<i32>, disabled: bool) -> Self { 130 + Self { 131 + value: value.clone(), 132 + disabled 125 133 } 126 134 } 127 135 } 128 136 129 - #[derive(Clone, Debug, Getters, MutGetters, Setters, Serialize, Deserialize, CopyGetters)] 130 - pub struct ServerBaseDeviceFeatureOutput { 137 + #[derive(Debug, Clone, Getters, CopyGetters)] 138 + pub struct ServerDeviceFeatureOutputPositionWithDurationProperties { 139 + #[getset(get = "pub")] 140 + position: RangeWithLimit<u32>, 131 141 #[getset(get = "pub")] 132 - #[serde(rename = "step-range")] 133 - step_range: RangeInclusive<u32>, 142 + duration: RangeWithLimit<u32>, 143 + #[getset(get_copy = "pub")] 144 + disabled: bool, 145 + #[getset(get_copy = "pub")] 146 + reverse_position: bool, 134 147 } 135 148 136 - impl ServerBaseDeviceFeatureOutput { 137 - pub fn new(step_range: &RangeInclusive<u32>) -> Self { 149 + impl ServerDeviceFeatureOutputPositionWithDurationProperties { 150 + pub fn new(position: &RangeWithLimit<u32>, duration: &RangeWithLimit<u32>, disabled: bool, reverse_position: bool) -> Self { 138 151 Self { 139 - step_range: step_range.clone(), 152 + position: position.clone(), 153 + duration: duration.clone(), 154 + disabled, 155 + reverse_position 140 156 } 141 157 } 142 158 } 143 159 144 - #[derive( 145 - Clone, Debug, Default, Getters, MutGetters, Setters, Serialize, Deserialize, CopyGetters, 146 - )] 147 - pub struct ServerUserDeviceFeatureOutput { 148 - #[getset(get = "pub")] 149 - #[serde( 150 - rename = "step-limit", 151 - default, 152 - skip_serializing_if = "Option::is_none", 153 - serialize_with = "range_serialize" 154 - )] 155 - step_limit: Option<RangeInclusive<u32>>, 156 - #[getset(get = "pub")] 157 - #[serde( 158 - rename = "reverse-position", 159 - default, 160 - skip_serializing_if = "Option::is_none" 161 - )] 162 - reverse_position: Option<bool>, 163 - #[getset(get = "pub")] 164 - #[serde(rename = "disabled", default, skip_serializing_if = "Option::is_none")] 165 - disabled: Option<bool>, 160 + #[derive(Clone, Debug)] 161 + pub struct ServerDeviceFeatureOutput { 162 + vibrate: Option<ServerDeviceFeatureOutputValueProperties>, 163 + rotate: Option<ServerDeviceFeatureOutputValueProperties>, 164 + rotate_with_direction: Option<ServerDeviceFeatureOutputValueProperties>, 165 + oscillate: Option<ServerDeviceFeatureOutputValueProperties>, 166 + constrict: Option<ServerDeviceFeatureOutputValueProperties>, 167 + heater: Option<ServerDeviceFeatureOutputValueProperties>, 168 + led: Option<ServerDeviceFeatureOutputValueProperties>, 169 + position: Option<ServerDeviceFeatureOutputValueProperties>, 170 + position_with_duration: Option<ServerDeviceFeatureOutputPositionWithDurationProperties>, 171 + spray: Option<ServerDeviceFeatureOutputValueProperties>, 172 + } 173 + 174 + #[derive(Clone, Debug, Getters)] 175 + #[getset(get = "pub")] 176 + pub struct ServerDeviceFeatureInputProperties { 177 + value_range: Vec<RangeInclusive<i32>>, 178 + input_commands: HashSet<InputCommandType>, 166 179 } 167 180 168 - impl ServerUserDeviceFeatureOutput { 181 + impl ServerDeviceFeatureInputProperties { 169 182 pub fn new( 170 - step_limit: Option<RangeInclusive<u32>>, 171 - reverse_position: Option<bool>, 172 - disabled: Option<bool>, 183 + value_range: &Vec<RangeInclusive<i32>>, 184 + sensor_commands: &HashSet<InputCommandType>, 173 185 ) -> Self { 174 186 Self { 175 - step_limit, 176 - reverse_position, 177 - disabled, 187 + value_range: value_range.clone(), 188 + input_commands: sensor_commands.clone(), 178 189 } 179 190 } 180 191 } 181 192 182 - #[derive(Clone, Debug, Default, Getters, MutGetters, Setters, CopyGetters)] 193 + #[derive(Clone, Debug, Getters)] 194 + #[getset(get = "pub")] 195 + pub struct ServerDeviceFeatureInput { 196 + battery: Option<ServerDeviceFeatureInputProperties>, 197 + rssi: Option<ServerDeviceFeatureInputProperties>, 198 + pressure: Option<ServerDeviceFeatureInputProperties>, 199 + button: Option<ServerDeviceFeatureInputProperties>, 200 + } 201 + 202 + #[derive(Clone, Debug, Getters, CopyGetters)] 183 203 pub struct ServerDeviceFeature { 184 - base_feature: ServerBaseDeviceFeature, 185 - #[getset(get_mut = "pub")] 186 - user_feature: ServerUserDeviceFeature, 204 + #[getset(get = "pub")] 205 + description: String, 206 + #[getset(get_copy = "pub")] 207 + id: Uuid, 208 + #[getset(get_copy = "pub")] 209 + base_id: Option<Uuid>, 210 + #[getset(get_copy = "pub")] 211 + alt_protocol_index: Option<u32>, 212 + #[getset(get = "pub")] 213 + output: Option<ServerDeviceFeatureOutput>, 187 214 #[getset(get = "pub")] 188 - output: Option<HashMap<OutputType, ServerDeviceFeatureOutput>>, 189 - // input doesn't specialize across Base/User right now so we just return the base device input 215 + input: Option<ServerDeviceFeatureInput>, 190 216 } 191 217 192 218 impl PartialEq for ServerDeviceFeature { ··· 199 225 } 200 226 201 227 impl ServerDeviceFeature { 202 - pub fn new( 203 - base_feature: &ServerBaseDeviceFeature, 204 - user_feature: &ServerUserDeviceFeature, 205 - ) -> Self { 206 - if base_feature.id() != user_feature.base_id() { 207 - // TODO panic! 208 - } 209 - let output = { 210 - if let Some(output_map) = base_feature.output() { 211 - let mut output = HashMap::new(); 212 - if let Some(user_output_map) = user_feature.output() { 213 - for (output_type, output_feature) in output_map { 214 - // TODO What if we have a key in the user map that isn't in the base map? We should remove it. 215 - if user_output_map.contains_key(output_type) { 216 - output.insert( 217 - *output_type, 218 - ServerDeviceFeatureOutput::new( 219 - output_feature, 220 - user_output_map.get(output_type).clone().unwrap(), 221 - ), 222 - ); 223 - } 224 - } 225 - } 226 - Some(output) 227 - } else { 228 - None 229 - } 230 - }; 231 - 228 + pub fn new(description: &str, id: Uuid, base_id: Option<Uuid>, alt_protocol_index: Option<u32>, output: &Option<ServerDeviceFeatureOutput>, input: &Option<ServerDeviceFeatureInput>) -> Self { 232 229 Self { 233 - output, 234 - base_feature: base_feature.clone(), 235 - user_feature: user_feature.clone(), 230 + description: description.to_owned(), 231 + id, 232 + base_id, 233 + alt_protocol_index, 234 + output: output.clone(), 235 + input: input.clone(), 236 236 } 237 237 } 238 238 239 - pub fn description(&self) -> &String { 240 - self.base_feature.description() 241 - } 242 - 243 - pub fn id(&self) -> Uuid { 244 - self.user_feature.id() 245 - } 246 - 247 - pub fn base_id(&self) -> Uuid { 248 - self.base_feature.id() 249 - } 250 - 251 - pub fn alt_protocol_index(&self) -> Option<u32> { 252 - self.base_feature.feature_settings().alt_protocol_index() 253 - } 254 - 255 - pub fn input(&self) -> &Option<HashMap<InputType, ServerDeviceFeatureInput>> { 256 - self.base_feature.input() 257 - } 258 - 259 - pub fn is_valid(&self) -> Result<(), ButtplugDeviceError> { 239 + /* 240 + pub fn as_device_feature(&self, index: u32) -> Result<DeviceFeature, ButtplugDeviceConfigError> { 241 + // try_collect() is still unstable so we extract the fallible-map call into a loop. This sucks. 242 + let mut outputs = HashMap::new(); 260 243 if let Some(output_map) = &self.output { 261 - for actuator in output_map.values() { 262 - actuator.is_valid()?; 244 + for (output_type, server_output) in output_map { 245 + outputs.insert( 246 + *output_type, 247 + server_output.as_device_feature_output_variant(*output_type)?, 248 + ); 263 249 } 264 250 } 265 - Ok(()) 266 - } 267 251 268 - pub fn as_device_feature(&self, index: u32) -> DeviceFeature { 269 - DeviceFeature::new( 252 + Ok(DeviceFeature::new( 270 253 index, 271 254 self.description(), 272 - &self.output.clone().map(|x| { 273 - x.iter() 274 - .filter(|(_, a)| !a.user_feature().disabled().as_ref().unwrap_or(&false)) 275 - .map(|(t, a)| (*t, DeviceFeatureOutput::from(a.clone()))) 276 - .collect() 277 - }), 255 + &outputs.is_empty().then_some(outputs).or(None), 278 256 &self.base_feature.input().clone().map(|x| { 279 257 x.iter() 280 258 .map(|(t, a)| (*t, DeviceFeatureInput::from(a.clone()))) 281 259 .collect() 282 260 }), 283 - ) 261 + )) 284 262 } 285 - } 286 - 287 - #[derive(Clone, Debug, Getters, MutGetters)] 288 - #[getset(get = "pub")] 289 - pub struct ServerDeviceFeatureOutput { 290 - base_feature: ServerBaseDeviceFeatureOutput, 291 - #[getset(get_mut = "pub")] 292 - user_feature: ServerUserDeviceFeatureOutput, 293 - } 294 - 295 - impl ServerDeviceFeatureOutput { 296 - pub fn new( 297 - base_feature: &ServerBaseDeviceFeatureOutput, 298 - user_feature: &ServerUserDeviceFeatureOutput, 299 - ) -> Self { 300 - Self { 301 - base_feature: base_feature.clone(), 302 - user_feature: user_feature.clone(), 303 - } 304 - } 305 - 306 - pub fn step_range(&self) -> &RangeInclusive<u32> { 307 - self.base_feature.step_range() 308 - } 309 - 310 - pub fn step_limit(&self) -> &RangeInclusive<u32> { 311 - if let Some(limit) = self.user_feature.step_limit() { 312 - limit 313 - } else { 314 - self.step_range() 315 - } 316 - } 317 - 318 - pub fn step_count(&self) -> u32 { 319 - if let Some(step_limit) = self.user_feature.step_limit() { 320 - step_limit.end() - step_limit.start() 321 - } else { 322 - self.base_feature.step_range.end() - self.base_feature.step_range.start() 323 - } 324 - } 325 - 326 - pub fn reverse_position(&self) -> bool { 327 - *self 328 - .user_feature 329 - .reverse_position() 330 - .as_ref() 331 - .unwrap_or(&false) 332 - } 333 - 334 - pub fn is_valid(&self) -> Result<(), ButtplugDeviceError> { 335 - let step_range = self.base_feature.step_range(); 336 - if step_range.is_empty() { 337 - Err(ButtplugDeviceError::DeviceConfigurationError( 338 - "Step range empty.".to_string(), 339 - )) 340 - } else if let Some(step_limit) = self.user_feature.step_limit() { 341 - if step_limit.is_empty() { 342 - Err(ButtplugDeviceError::DeviceConfigurationError( 343 - "Step limit empty.".to_string(), 344 - )) 345 - } else if step_limit.start() < step_range.start() || step_limit.end() > step_range.end() { 346 - Err(ButtplugDeviceError::DeviceConfigurationError( 347 - "Step limit outside step range.".to_string(), 348 - )) 349 - } else { 350 - Ok(()) 351 - } 352 - } else { 353 - Ok(()) 354 - } 355 - } 356 - } 357 - 358 - impl From<ServerDeviceFeatureOutput> for DeviceFeatureOutput { 359 - fn from(value: ServerDeviceFeatureOutput) -> Self { 360 - DeviceFeatureOutput::new(value.step_count()) 361 - } 362 - } 363 - 364 - #[derive( 365 - Clone, Debug, Default, PartialEq, Eq, Getters, MutGetters, Setters, Serialize, Deserialize, 366 - )] 367 - pub struct ServerDeviceFeatureInput { 368 - #[getset(get = "pub", get_mut = "pub(super)")] 369 - #[serde(rename = "value-range")] 370 - #[serde(serialize_with = "range_sequence_serialize")] 371 - value_range: Vec<RangeInclusive<i32>>, 372 - #[getset(get = "pub")] 373 - #[serde(rename = "input-commands")] 374 - input_commands: HashSet<InputCommandType>, 375 - } 376 - 377 - impl ServerDeviceFeatureInput { 378 - pub fn new( 379 - value_range: &Vec<RangeInclusive<i32>>, 380 - sensor_commands: &HashSet<InputCommandType>, 381 - ) -> Self { 382 - Self { 383 - value_range: value_range.clone(), 384 - input_commands: sensor_commands.clone(), 385 - } 386 - } 387 - } 388 - 389 - impl From<ServerDeviceFeatureInput> for DeviceFeatureInput { 390 - fn from(value: ServerDeviceFeatureInput) -> Self { 391 - // Unlike actuator, this is just a straight copy. 392 - DeviceFeatureInput::new(value.value_range(), value.input_commands()) 393 - } 263 + */ 394 264 }
-5
crates/buttplug_server_device_config/src/endpoint.rs
··· 13 13 14 14 use core::hash::Hash; 15 15 16 - // We need this array to be exposed in our WASM FFI, but the only way to do that 17 - // is to expose it at the declaration level. Therefore, we use the WASM feature 18 - // to assume we're building for WASM and attach our bindgen. The serde 19 - // de/serialization is taken care of at the FFI level. 20 - 21 16 /// Endpoint names for device communication. 22 17 /// 23 18 /// Endpoints denote different contextual communication targets on a device. For instance, for a
+9 -2
crates/buttplug_server_device_config/src/identifiers.rs
··· 44 44 } 45 45 46 46 impl BaseDeviceIdentifier { 47 - pub fn new(protocol: &str, attributes_identifier: &Option<String>) -> Self { 47 + pub fn new_default(protocol: &str) -> Self { 48 + Self { 49 + protocol: protocol.to_owned(), 50 + identifier: None, 51 + } 52 + } 53 + 54 + pub fn new(protocol: &str, attributes_identifier: &str) -> Self { 48 55 Self { 49 56 protocol: protocol.to_owned(), 50 - identifier: attributes_identifier.clone(), 57 + identifier: Some(attributes_identifier.to_owned()), 51 58 } 52 59 } 53 60 }
+21 -327
crates/buttplug_server_device_config/src/lib.rs
··· 137 137 #[macro_use] 138 138 extern crate strum_macros; 139 139 140 + #[macro_use] 141 + extern crate log; 142 + 143 + mod device_config_file; 144 + pub use device_config_file::{load_protocol_configs, save_user_config}; 145 + mod device_config_manager; 146 + pub use device_config_manager::*; 140 147 mod specifier; 141 148 pub use specifier::*; 142 149 mod identifiers; ··· 145 152 pub use device_definitions::*; 146 153 mod device_feature; 147 154 pub use device_feature::*; 148 - mod device_configuration; 149 - pub use device_configuration::*; 150 155 mod endpoint; 151 156 pub use endpoint::*; 152 157 153 - use buttplug_core::errors::ButtplugDeviceError; 154 - use dashmap::DashMap; 155 - use getset::Getters; 156 - use std::{ 157 - collections::HashMap, 158 - fmt::{self, Debug}, 159 - }; 160 158 161 - #[macro_use] 162 - extern crate log; 163 - 164 - #[derive(Default, Clone)] 165 - pub struct DeviceConfigurationManagerBuilder { 166 - communication_specifiers: HashMap<String, Vec<ProtocolCommunicationSpecifier>>, 167 - user_communication_specifiers: DashMap<String, Vec<ProtocolCommunicationSpecifier>>, 168 - base_device_definitions: HashMap<BaseDeviceIdentifier, BaseDeviceDefinition>, 169 - user_device_definitions: DashMap<UserDeviceIdentifier, DeviceDefinition>, 170 - } 171 - 172 - impl DeviceConfigurationManagerBuilder { 173 - pub fn communication_specifier( 174 - &mut self, 175 - protocol_name: &str, 176 - specifier: &[ProtocolCommunicationSpecifier], 177 - ) -> &mut Self { 178 - self 179 - .communication_specifiers 180 - .entry(protocol_name.to_owned()) 181 - .or_default() 182 - .extend(specifier.iter().cloned()); 183 - self 184 - } 185 - 186 - pub fn protocol_features( 187 - &mut self, 188 - identifier: &BaseDeviceIdentifier, 189 - features: &BaseDeviceDefinition, 190 - ) -> &mut Self { 191 - self 192 - .base_device_definitions 193 - .insert(identifier.clone(), features.clone()); 194 - self 195 - } 196 - 197 - pub fn user_communication_specifier( 198 - &mut self, 199 - protocol_name: &str, 200 - specifier: &[ProtocolCommunicationSpecifier], 201 - ) -> &mut Self { 202 - self 203 - .user_communication_specifiers 204 - .entry(protocol_name.to_owned()) 205 - .or_default() 206 - .extend(specifier.iter().cloned()); 207 - self 208 - } 209 - 210 - pub fn user_protocol_features( 211 - &mut self, 212 - identifier: &UserDeviceIdentifier, 213 - features: &UserDeviceDefinition, 214 - ) -> &mut Self { 215 - if let Some((_, base_definition)) = self 216 - .base_device_definitions 217 - .iter() 218 - .find(|(_, x)| x.id() == features.base_id()) 219 - { 220 - self.user_device_definitions.insert( 221 - identifier.clone(), 222 - DeviceDefinition::new(base_definition, features), 223 - ); 224 - } else { 225 - error!( 226 - "Cannot find protocol with base id {} for user id {}", 227 - features.base_id(), 228 - features.id() 229 - ) 230 - } 231 - self 232 - } 233 - 234 - pub fn finish(&mut self) -> Result<DeviceConfigurationManager, ButtplugDeviceError> { 235 - // Build and validate the protocol attributes tree. 236 - let mut attribute_tree_map = HashMap::new(); 237 - 238 - // Add all the defaults first, they won't have parent attributes. 239 - for (ident, attr) in &self.base_device_definitions { 240 - /* 241 - for feature in attr.features() { 242 - if let Err(e) = feature.is_valid() { 243 - error!("Feature {attr:?} for ident {ident:?} is not valid, skipping addition: {e:?}"); 244 - continue; 245 - } 246 - } 247 - */ 248 - attribute_tree_map.insert(ident.clone(), attr.clone()); 249 - } 250 - 251 - let user_attribute_tree_map = DashMap::new(); 252 - // Finally, add in user configurations, which will have an address. 253 - for kv in &self.user_device_definitions { 254 - let (ident, attr) = (kv.key(), kv.value()); 255 - for feature in attr.features() { 256 - if let Err(e) = feature.is_valid() { 257 - error!("Feature {attr:?} for ident {ident:?} is not valid, skipping addition: {e:?}"); 258 - continue; 259 - } 260 - } 261 - user_attribute_tree_map.insert(kv.key().clone(), kv.value().clone()); 262 - } 263 - 264 - Ok(DeviceConfigurationManager { 265 - base_communication_specifiers: self.communication_specifiers.clone(), 266 - user_communication_specifiers: self.user_communication_specifiers.clone(), 267 - base_device_definitions: attribute_tree_map, 268 - user_device_definitions: user_attribute_tree_map, 269 - //protocol_map, 270 - }) 271 - } 272 - } 273 - 274 - /// Correlates information about protocols and which devices they support. 275 - /// 276 - /// The [DeviceConfigurationManager] handles stores information about which device protocols the 277 - /// library supports, as well as which devices can use those protocols. When a 278 - /// [DeviceCommunicationManager](crate::server::device::communication_manager) finds a device during scanning, 279 - /// device information is given to the [DeviceConfigurationManager] to decide whether Buttplug 280 - /// should try to connect to and communicate with the device. 281 - /// 282 - /// Assuming the device is supported by the library, the [DeviceConfigurationManager] also stores 283 - /// information about what commands can be sent to the device (Vibrate, Rotate, etc...), and the 284 - /// parameters for those commands (number of power levels, stroke distances, etc...). 285 - #[derive(Getters)] 286 - #[getset(get = "pub")] 287 - pub struct DeviceConfigurationManager { 288 - /// Communication specifiers from the base device config, mapped from protocol name to vector of 289 - /// specifiers. Should not change/update during a session. 290 - base_communication_specifiers: HashMap<String, Vec<ProtocolCommunicationSpecifier>>, 291 - /// Device definitions from the base device config. Should not change/update during a session. 292 - base_device_definitions: HashMap<BaseDeviceIdentifier, BaseDeviceDefinition>, 293 - /// Communication specifiers provided by the user, mapped from protocol name to vector of 294 - /// specifiers. Loaded at session start, may change over life of session. 295 - user_communication_specifiers: DashMap<String, Vec<ProtocolCommunicationSpecifier>>, 296 - /// Device definitions from the user device config. Loaded at session start, may change over life 297 - /// of session. 298 - user_device_definitions: DashMap<UserDeviceIdentifier, DeviceDefinition>, 299 - } 300 - 301 - impl Debug for DeviceConfigurationManager { 302 - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 303 - f.debug_struct("DeviceConfigurationManager").finish() 304 - } 305 - } 306 - 307 - impl Default for DeviceConfigurationManager { 308 - fn default() -> Self { 309 - // Unwrap allowed here because we assume our built in device config will 310 - // always work. System won't pass tests or possibly even build otherwise. 311 - DeviceConfigurationManagerBuilder::default() 312 - .finish() 313 - .expect("Default creation of a DCM should always work.") 314 - } 315 - } 316 - 317 - impl DeviceConfigurationManager { 318 - pub fn add_user_communication_specifier( 319 - &self, 320 - protocol: &str, 321 - specifier: &ProtocolCommunicationSpecifier, 322 - ) -> Result<(), ButtplugDeviceError> { 323 - //self.protocol_map.contains_key(protocol); 324 - self 325 - .user_communication_specifiers 326 - .entry(protocol.to_owned()) 327 - .or_default() 328 - .push(specifier.clone()); 329 - Ok(()) 330 - } 331 - 332 - pub fn remove_user_communication_specifier( 333 - &self, 334 - protocol: &str, 335 - specifier: &ProtocolCommunicationSpecifier, 336 - ) { 337 - if let Some(mut specifiers) = self.user_communication_specifiers.get_mut(protocol) { 338 - let specifier_vec = specifiers.value_mut(); 339 - *specifier_vec = specifier_vec 340 - .iter() 341 - .filter(|s| *specifier != **s) 342 - .cloned() 343 - .collect(); 344 - } 345 - } 346 - 347 - pub fn add_user_device_definition( 348 - &self, 349 - identifier: &UserDeviceIdentifier, 350 - definition: &DeviceDefinition, 351 - ) -> Result<(), ButtplugDeviceError> { 352 - //self.protocol_map.contains_key(identifier.protocol()); 353 - // Check validity of device 354 - let mut index = definition.user_config().index(); 355 - let indexes: Vec<u32> = self.user_device_definitions().iter().map(|x| x.value().user_config().index()).collect(); 356 - // If we just added 1 to the maximum value of the current indexes, someone decides to set an 357 - // index to u32::MAX-1, then we'd have a problem. This is kind of a shit solution but it'll work 358 - // quickly for anyone that's not actively fucking with us by manually playing with user config files. 359 - while indexes.contains(&index) { 360 - index = index.wrapping_add(1); 361 - } 362 - let mut def = definition.clone(); 363 - *def.user_device_mut().user_config_mut().index_mut() = index; 364 - self 365 - .user_device_definitions 366 - .entry(identifier.clone()) 367 - .insert(def); 368 - Ok(()) 369 - } 370 - 371 - pub fn remove_user_device_definition(&self, identifier: &UserDeviceIdentifier) { 372 - self.user_device_definitions.remove(identifier); 373 - } 374 - 375 - pub fn address_allowed(&self, address: &str) -> bool { 376 - // Make sure the device isn't on the deny list 377 - if self 378 - .user_device_definitions 379 - .iter() 380 - .any(|kv| kv.key().address() == address && kv.value().user_config().deny()) 381 - { 382 - // If device is outright denied, deny 383 - info!( 384 - "Device {} denied by configuration, not connecting.", 385 - address 386 - ); 387 - false 388 - } else if self 389 - .user_device_definitions 390 - .iter() 391 - .any(|kv| kv.value().user_config().allow()) 392 - && !self 393 - .user_device_definitions 394 - .iter() 395 - .any(|kv| kv.key().address() == address && kv.value().user_config().allow()) 396 - { 397 - // If device is not on allow list and allow list isn't empty, deny 398 - info!( 399 - "Device {} not on allow list and allow list not empty, not connecting.", 400 - address 401 - ); 402 - false 403 - } else { 404 - true 405 - } 406 - } 407 - 408 - fn device_index(&self, identifier: &UserDeviceIdentifier) -> u32 { 409 - // See if we have a reserved or reusable device index here. 410 - if let Some(config) = self.user_device_definitions.get(identifier) { 411 - let index = config.user_config().index(); 412 - debug!("Found index {index} for device {identifier:?}"); 413 - return index; 414 - } 415 - 416 - let current_indexes: Vec<u32> = self 417 - .user_device_definitions 418 - .iter() 419 - .map(|x| x.user_config().index()) 420 - .collect(); 421 - 422 - // Someone is gonna make a max device index in their config file just to fuck with me, therefore 423 - // we don't do "max + 1", we fill in holes (lol) in sequences. To whomever has 4 billion sex toys: 424 - // sorry your index finding for new devices is slow and takes 16GB of allocation every time we 425 - // want to search the index space. 426 - 427 - let mut index = 0; 428 - while current_indexes.contains(&index) { 429 - index += 1; 430 - } 431 - debug!("Generating and assigning index {index:?} for device {identifier:?}"); 432 - index 433 - } 159 + use std::ops::RangeInclusive; 160 + use serde::Serializer; 161 + use thiserror::Error; 434 162 435 - /// Provides read-only access to the internal protocol/identifier map. Mainly 436 - /// used for WebBluetooth filter construction, but could also be handy for 437 - /// listing capabilities in UI, etc. 438 - pub fn protocol_device_configurations( 439 - &self, 440 - ) -> HashMap<String, Vec<ProtocolCommunicationSpecifier>> { 441 - self.base_communication_specifiers.clone() 442 - } 443 - 444 - pub fn device_definition(&self, identifier: &UserDeviceIdentifier) -> Option<DeviceDefinition> { 445 - let features = if let Some(attrs) = self.user_device_definitions.get(identifier) { 446 - debug!("User device config found for {:?}", identifier); 447 - attrs.clone() 448 - } else if let Some(attrs) = self.base_device_definitions.get(&BaseDeviceIdentifier::new( 449 - identifier.protocol(), 450 - identifier.identifier(), 451 - )) { 452 - debug!( 453 - "Protocol + Identifier device config found for {:?}", 454 - identifier 455 - ); 456 - DeviceDefinition::new_from_base_definition(attrs, self.device_index(identifier)) 457 - } else if let Some(attrs) = self 458 - .base_device_definitions 459 - .get(&BaseDeviceIdentifier::new(identifier.protocol(), &None)) 460 - { 461 - debug!("Protocol device config found for {:?}", identifier); 462 - DeviceDefinition::new_from_base_definition(attrs, self.device_index(identifier)) 463 - } else { 464 - return None; 465 - }; 466 - 467 - // If this is a new device, it needs to be added to the user device definition map. 468 - // 469 - // Device definitions are looked up before we fully initialize a device, mostly for algorithm 470 - // preparation. There is a very small chance we may save the device config then error out when 471 - // we connect to the device, but we'll assume we may connect successfully later. 472 - if self.user_device_definitions.get(identifier).is_none() { 473 - self 474 - .user_device_definitions 475 - .insert(identifier.clone(), features.clone()); 476 - } 477 - 478 - Some(features) 479 - } 163 + #[derive(Error, Debug)] 164 + pub enum ButtplugDeviceConfigError<T> { 165 + /// Conversion to client type not possible with requested property type 166 + #[error("Conversion of {0} to client type not possible with requested property type")] 167 + InvalidOutputTypeConversion(String), 168 + /// User set range exceeds bounds of possible configuration range 169 + #[error("User set range {0} exceeds bounds of possible configuration range {1}")] 170 + InvalidUserRange(RangeInclusive<T>, RangeInclusive<T>), 171 + /// Base range required 172 + #[error("Base range required for all feature outputs")] 173 + BaseRangeRequired, 480 174 }
-6
crates/buttplug_server_device_config/src/specifier.rs
··· 11 11 use std::collections::{HashMap, HashSet}; 12 12 use uuid::Uuid; 13 13 14 - // Note: There's a ton of extra structs in here just to deserialize the json 15 - // file. Just leave them and build extras (for instance, 16 - // DeviceProtocolConfiguration) if needed elsewhere in the codebase. It's not 17 - // gonna hurt anything and making a ton of serde attributes is just going to get 18 - // confusing (see the messages impl). 19 - 20 14 #[derive(Serialize, Deserialize, Debug, Clone, Getters, MutGetters, Setters, Eq)] 21 15 #[getset(get = "pub", set = "pub", get_mut = "pub(crate)")] 22 16 pub struct BluetoothLEManufacturerData {