Buttplug sex toy control library
1// Buttplug Rust Source Code File - See https://buttplug.io for more info.
2//
3// Copyright 2016-2024 Nonpolynomial Labs LLC. All rights reserved.
4//
5// Licensed under the BSD 3-Clause license. See LICENSE file in the project root
6// for full license information.
7
8//! Representations of low level [Buttplug Protocol](https://buttplug-spec.docs.buttplug.io)
9//! messages
10//!
11//! The core communication types for the Buttplug protocol. There are structs for each message type,
12//! with only the current message spec being included here (older message specs are only used for
13//! backward compatibilty and are in the server::message module). There are also enum types that are
14//! used to classify messages into categories, for instance, messages that only should be sent by a
15//! client or server.
16
17pub mod v0;
18pub mod v4;
19
20mod device_feature;
21pub mod serializer;
22
23pub use device_feature::*;
24pub use v0::*;
25pub use v4::*;
26
27use crate::errors::ButtplugMessageError;
28use serde_repr::{Deserialize_repr, Serialize_repr};
29use std::convert::TryFrom;
30
31use super::errors::ButtplugError;
32
33/// Enum of possible [Buttplug Message
34/// Spec](https://buttplug-spec.docs.buttplug.io) versions.
35#[derive(
36 Debug, Clone, Copy, Display, PartialEq, Eq, PartialOrd, Ord, Serialize_repr, Deserialize_repr,
37)]
38#[repr(u32)]
39pub enum ButtplugMessageSpecVersion {
40 Version0 = 0,
41 Version1 = 1,
42 Version2 = 2,
43 Version3 = 3,
44 Version4 = 4,
45}
46
47impl TryFrom<i32> for ButtplugMessageSpecVersion {
48 type Error = ButtplugError;
49
50 // There's probably another crate to make this easier but eh.
51 fn try_from(value: i32) -> Result<Self, Self::Error> {
52 match value {
53 0 => Ok(ButtplugMessageSpecVersion::Version0),
54 1 => Ok(ButtplugMessageSpecVersion::Version1),
55 2 => Ok(ButtplugMessageSpecVersion::Version2),
56 3 => Ok(ButtplugMessageSpecVersion::Version3),
57 4 => Ok(ButtplugMessageSpecVersion::Version4),
58 _ => Err(
59 ButtplugMessageError::InvalidMessageContents(format!(
60 "Message spec version {value} is not valid"
61 ))
62 .into(),
63 ),
64 }
65 }
66}
67
68/// Message Id for events sent from the server, which are not in response to a
69/// client request.
70pub const BUTTPLUG_SERVER_EVENT_ID: u32 = 0;
71
72/// The current latest version of the spec implemented by the library.
73pub const BUTTPLUG_CURRENT_API_MAJOR_VERSION: ButtplugMessageSpecVersion =
74 ButtplugMessageSpecVersion::Version4;
75
76pub const BUTTPLUG_CURRENT_API_MINOR_VERSION: u32 = 0;
77
78pub trait ButtplugMessageFinalizer {
79 fn finalize(&mut self) {
80 }
81}
82
83/// Base trait for all Buttplug Protocol Message Structs. Handles management of
84/// message ids, as well as implementing conveinence functions for converting
85/// between message structs and various message enums, serialization, etc...
86pub trait ButtplugMessage:
87 ButtplugMessageValidator + ButtplugMessageFinalizer + Send + Sync + Clone
88{
89 /// Returns the id number of the message
90 fn id(&self) -> u32;
91 /// Sets the id number of the message.
92 fn set_id(&mut self, id: u32);
93 /// True if the message is an event (message id of 0) from the server.
94 fn is_server_event(&self) -> bool {
95 self.id() == BUTTPLUG_SERVER_EVENT_ID
96 }
97}
98
99/// Validation function for message contents. Can be run before message is
100/// transmitted, as message may be formed and mutated at multiple points in the
101/// library, or may need to be checked after deserialization. Message enums will
102/// run this on whatever their variant is.
103pub trait ButtplugMessageValidator {
104 /// Returns () if the message is valid, otherwise returns a message error.
105 fn is_valid(&self) -> Result<(), ButtplugMessageError> {
106 // By default, return Ok, as many messages won't have any checks.
107 Ok(())
108 }
109
110 fn is_system_id(&self, id: u32) -> Result<(), ButtplugMessageError> {
111 if id == 0 {
112 Ok(())
113 } else {
114 Err(ButtplugMessageError::InvalidMessageContents(
115 "Message should have id of 0, as it is a system message.".to_string(),
116 ))
117 }
118 }
119
120 fn is_not_system_id(&self, id: u32) -> Result<(), ButtplugMessageError> {
121 if id == 0 {
122 Err(ButtplugMessageError::InvalidMessageContents(
123 "Message should not have 0 for an Id. Id of 0 is reserved for system messages.".to_string(),
124 ))
125 } else {
126 Ok(())
127 }
128 }
129
130 fn is_in_command_range(&self, value: f64, error_msg: String) -> Result<(), ButtplugMessageError> {
131 if !(0.0..=1.0).contains(&value) {
132 Err(ButtplugMessageError::InvalidMessageContents(error_msg))
133 } else {
134 Ok(())
135 }
136 }
137}
138
139/// Adds device index handling to the [ButtplugMessage] trait.
140pub trait ButtplugDeviceMessage: ButtplugMessage {
141 fn device_index(&self) -> u32;
142 fn set_device_index(&mut self, id: u32);
143}
144
145/// Type alias for the latest version of client-to-server messages.
146pub type ButtplugClientMessageCurrent = ButtplugClientMessageV4;
147/// Type alias for the latest version of server-to-client messages.
148pub type ButtplugServerMessageCurrent = ButtplugServerMessageV4;