Buttplug sex toy control library
1// Buttplug Rust Source Code File - See https://buttplug.io for more info.
2//
3// Copyright 2016-2022 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
8extern crate proc_macro;
9
10use proc_macro::TokenStream;
11use quote::quote;
12
13#[proc_macro_derive(ButtplugMessage)]
14pub fn buttplug_message_derive(input: TokenStream) -> TokenStream {
15 // Construct a representation of Rust code as a syntax tree
16 // that we can manipulate
17 let ast = syn::parse(input).expect("Failure will cause compile failure.");
18
19 // Build the trait implementation
20 impl_buttplug_message_macro(&ast)
21}
22
23fn impl_buttplug_message_macro(ast: &syn::DeriveInput) -> TokenStream {
24 let name = &ast.ident;
25
26 match ast.data {
27 syn::Data::Enum(ref e) => {
28 let idents = e.variants.iter().map(|x| x.ident.clone());
29 let idents2 = idents.clone();
30 let gen = quote! {
31 impl ButtplugMessage for #name {
32 fn id(&self) -> u32 {
33 match self {
34 #( #name::#idents(ref msg) => msg.id(),)*
35
36 }
37 }
38 fn set_id(&mut self, id: u32) {
39 match self {
40 #( #name::#idents2(ref mut msg) => msg.set_id(id),)*
41 }
42 }
43 }
44 };
45 gen.into()
46 }
47 syn::Data::Struct(_) => {
48 let gen = quote! {
49 impl ButtplugMessage for #name {
50 fn id(&self) -> u32 {
51 self.id
52 }
53
54 fn set_id(&mut self, id: u32) {
55 self.id = id;
56 }
57 }
58 };
59 gen.into()
60 }
61 _ => panic!("Derivation only works on structs and enums"),
62 }
63}
64
65#[proc_macro_derive(ButtplugDeviceMessage)]
66pub fn buttplug_device_message_derive(input: TokenStream) -> TokenStream {
67 // Construct a representation of Rust code as a syntax tree
68 // that we can manipulate
69 let ast = syn::parse(input).expect("Failure will cause compile failure.");
70
71 let mut tokens = impl_buttplug_message_macro(&ast);
72 tokens.extend(impl_buttplug_device_message_macro(&ast));
73 tokens
74}
75
76fn impl_buttplug_device_message_macro(ast: &syn::DeriveInput) -> TokenStream {
77 let name = &ast.ident;
78 match ast.data {
79 syn::Data::Enum(ref e) => {
80 let idents: Vec<_> = e.variants.iter().map(|x| x.ident.clone()).collect();
81 let gen = quote! {
82 impl ButtplugDeviceMessage for #name {
83 fn device_index(&self) -> u32 {
84 match self {
85 #( #name::#idents(ref msg) => msg.device_index(),)*
86
87 }
88 }
89 fn set_device_index(&mut self, id: u32) {
90 match self {
91 #( #name::#idents(ref mut msg) => msg.set_device_index(id),)*
92 }
93 }
94 }
95 };
96 gen.into()
97 }
98 syn::Data::Struct(_) => {
99 let gen = quote! {
100 impl ButtplugDeviceMessage for #name {
101 fn device_index(&self) -> u32 {
102 self.device_index
103 }
104
105 fn set_device_index(&mut self, id: u32) {
106 self.device_index = id;
107 }
108 }
109 };
110 gen.into()
111 }
112 _ => panic!("Derivation only works on structs and enums"),
113 }
114}
115
116#[proc_macro_derive(ButtplugMessageValidator)]
117pub fn buttplug_message_validator_derive(input: TokenStream) -> TokenStream {
118 // Construct a representation of Rust code as a syntax tree
119 // that we can manipulate
120 let ast = syn::parse(input).expect("Failure will cause compile failure.");
121
122 // Build the trait implementation
123 impl_buttplug_message_validator_macro(&ast)
124}
125
126fn impl_buttplug_message_validator_macro(ast: &syn::DeriveInput) -> TokenStream {
127 let name = &ast.ident;
128
129 match &ast.data {
130 syn::Data::Enum(e) => {
131 let idents: Vec<_> = e.variants.iter().map(|x| x.ident.clone()).collect();
132 let gen = quote! {
133 impl ButtplugMessageValidator for #name {
134 fn is_valid(&self) -> Result<(), ButtplugMessageError> {
135 match self {
136 #( #name::#idents(msg) => msg.is_valid(), )*
137 }
138 }
139 }
140 };
141 gen.into()
142 }
143 syn::Data::Struct(_) => {
144 let gen = quote! {
145 impl ButtplugMessageValidator for #name {
146 }
147 };
148 gen.into()
149 }
150 _ => panic!("Derivation only works on structs and enums"),
151 }
152}
153
154#[proc_macro_derive(TryFromButtplugClientMessage)]
155pub fn try_from_buttplug_client_message_derive(input: TokenStream) -> TokenStream {
156 // Construct a representation of Rust code as a syntax tree
157 // that we can manipulate
158 let ast = syn::parse(input).expect("Failure will cause compile failure.");
159
160 impl_try_from_buttplug_client_message_derive_macro(&ast)
161}
162
163fn impl_try_from_buttplug_client_message_derive_macro(ast: &syn::DeriveInput) -> TokenStream {
164 let name = &ast.ident;
165 if let syn::Data::Enum(ref e) = ast.data {
166 let idents: Vec<_> = e.variants.iter().map(|x| x.ident.clone()).collect();
167 let gen = quote! {
168 impl TryFrom<ButtplugClientMessage> for #name {
169 type Error = &'static str;
170
171 fn try_from(msg: ButtplugClientMessage) -> Result<Self, &'static str> {
172 match msg {
173 #( ButtplugClientMessage::#idents(msg) => Ok(#name::#idents(msg)),)*
174 _ => Err("ButtplugClientMessage cannot be converted to #name")
175 }
176 }
177 }
178
179 impl From<#name> for ButtplugClientMessage {
180 fn from(msg: #name) -> ButtplugClientMessage {
181 match msg {
182 #( #name::#idents(msg) => ButtplugClientMessage::#idents(msg),)*
183 }
184 }
185 }
186 };
187 gen.into()
188 } else {
189 panic!("TryFromButtplugClientMessage only works on structs");
190 }
191}
192
193#[proc_macro_derive(TryFromButtplugServerMessage)]
194pub fn try_from_buttplug_out_message_derive(input: TokenStream) -> TokenStream {
195 // Construct a representation of Rust code as a syntax tree
196 // that we can manipulate
197 let ast = syn::parse(input).expect("Failure will cause compile failure.");
198
199 impl_try_from_buttplug_server_message_derive_macro(&ast)
200}
201
202fn impl_try_from_buttplug_server_message_derive_macro(ast: &syn::DeriveInput) -> TokenStream {
203 let name = &ast.ident;
204 if let syn::Data::Enum(ref e) = ast.data {
205 let idents: Vec<_> = e.variants.iter().map(|x| x.ident.clone()).collect();
206 let gen = quote! {
207 impl TryFrom<ButtplugServerMessage> for #name {
208 type Error = ButtplugMessageError;
209
210 fn try_from(msg: ButtplugServerMessage) -> Result<Self, ButtplugMessageError> {
211 match msg {
212 #( ButtplugServerMessage::#idents(msg) => Ok(#name::#idents(msg.into())),)*
213 _ => Err(ButtplugMessageError::MessageConversionError("ButtplugServerMessage cannot be converted to #name".to_owned()))
214 }
215 }
216 }
217 };
218 gen.into()
219 } else {
220 panic!("TryFromButtplugServerMessage only works on structs");
221 }
222}
223
224#[proc_macro_derive(FromSpecificButtplugMessage)]
225pub fn from_specific_buttplug_message_derive(input: TokenStream) -> TokenStream {
226 // Construct a representation of Rust code as a syntax tree
227 // that we can manipulate
228 let ast = syn::parse(input).expect("Failure will cause compile failure.");
229
230 impl_from_specific_buttplug_message_derive_macro(&ast)
231}
232
233fn impl_from_specific_buttplug_message_derive_macro(ast: &syn::DeriveInput) -> TokenStream {
234 let name = &ast.ident;
235 if let syn::Data::Enum(ref e) = ast.data {
236 let idents: Vec<_> = e.variants.iter().map(|x| x.ident.clone()).collect();
237 let gen = quote! {
238 #(impl From<#idents> for #name {
239 fn from(msg: #idents) -> #name {
240 #name::#idents(msg)
241 }
242 })*
243 };
244 gen.into()
245 } else {
246 panic!("FromButtplugMessageUnion only works on structs");
247 }
248}
249
250#[proc_macro_derive(ButtplugClientMessageType)]
251pub fn buttplug_client_message_type_derive(input: TokenStream) -> TokenStream {
252 // Construct a representation of Rust code as a syntax tree
253 // that we can manipulate
254 let ast = syn::parse(input).expect("Failure will cause compile failure.");
255
256 // Build the trait implementation
257 impl_buttplug_client_message_type_macro(&ast)
258}
259
260fn impl_buttplug_client_message_type_macro(ast: &syn::DeriveInput) -> TokenStream {
261 let name = &ast.ident;
262 let gen = quote! {
263 impl ButtplugClientMessageType for #name {}
264 };
265 gen.into()
266}
267
268#[proc_macro_derive(ButtplugServerMessageType)]
269pub fn buttplug_server_message_type_derive(input: TokenStream) -> TokenStream {
270 // Construct a representation of Rust code as a syntax tree
271 // that we can manipulate
272 let ast = syn::parse(input).expect("Failure will cause compile failure.");
273
274 // Build the trait implementation
275 impl_buttplug_server_message_type_macro(&ast)
276}
277
278fn impl_buttplug_server_message_type_macro(ast: &syn::DeriveInput) -> TokenStream {
279 let name = &ast.ident;
280 let gen = quote! {
281 impl ButtplugServerMessageType for #name {}
282 };
283 gen.into()
284}