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(ButtplugMessageFinalizer)]
155pub fn buttplug_message_finalizer_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 // Build the trait implementation
161 impl_buttplug_message_finalizer_macro(&ast)
162}
163
164fn impl_buttplug_message_finalizer_macro(ast: &syn::DeriveInput) -> TokenStream {
165 let name = &ast.ident;
166
167 match &ast.data {
168 syn::Data::Enum(_) => {
169 let gen = quote! {
170 impl ButtplugMessageFinalizer for #name {}
171 };
172 gen.into()
173 }
174 syn::Data::Struct(_) => {
175 let gen = quote! {
176 impl ButtplugMessageFinalizer for #name {}
177 };
178 gen.into()
179 }
180 _ => panic!("Derivation only works on structs and enums"),
181 }
182}
183
184#[proc_macro_derive(TryFromButtplugClientMessage)]
185pub fn try_from_buttplug_client_message_derive(input: TokenStream) -> TokenStream {
186 // Construct a representation of Rust code as a syntax tree
187 // that we can manipulate
188 let ast = syn::parse(input).expect("Failure will cause compile failure.");
189
190 impl_try_from_buttplug_client_message_derive_macro(&ast)
191}
192
193fn impl_try_from_buttplug_client_message_derive_macro(ast: &syn::DeriveInput) -> TokenStream {
194 let name = &ast.ident;
195 if let syn::Data::Enum(ref e) = ast.data {
196 let idents: Vec<_> = e.variants.iter().map(|x| x.ident.clone()).collect();
197 let gen = quote! {
198 impl TryFrom<ButtplugClientMessage> for #name {
199 type Error = &'static str;
200
201 fn try_from(msg: ButtplugClientMessage) -> Result<Self, &'static str> {
202 match msg {
203 #( ButtplugClientMessage::#idents(msg) => Ok(#name::#idents(msg)),)*
204 _ => Err("ButtplugClientMessage cannot be converted to #name")
205 }
206 }
207 }
208
209 impl From<#name> for ButtplugClientMessage {
210 fn from(msg: #name) -> ButtplugClientMessage {
211 match msg {
212 #( #name::#idents(msg) => ButtplugClientMessage::#idents(msg),)*
213 }
214 }
215 }
216 };
217 gen.into()
218 } else {
219 panic!("TryFromButtplugClientMessage only works on structs");
220 }
221}
222
223#[proc_macro_derive(TryFromButtplugServerMessage)]
224pub fn try_from_buttplug_out_message_derive(input: TokenStream) -> TokenStream {
225 // Construct a representation of Rust code as a syntax tree
226 // that we can manipulate
227 let ast = syn::parse(input).expect("Failure will cause compile failure.");
228
229 impl_try_from_buttplug_server_message_derive_macro(&ast)
230}
231
232fn impl_try_from_buttplug_server_message_derive_macro(ast: &syn::DeriveInput) -> TokenStream {
233 let name = &ast.ident;
234 if let syn::Data::Enum(ref e) = ast.data {
235 let idents: Vec<_> = e.variants.iter().map(|x| x.ident.clone()).collect();
236 let gen = quote! {
237 impl TryFrom<ButtplugServerMessage> for #name {
238 type Error = ButtplugMessageError;
239
240 fn try_from(msg: ButtplugServerMessage) -> Result<Self, ButtplugMessageError> {
241 match msg {
242 #( ButtplugServerMessage::#idents(msg) => Ok(#name::#idents(msg.into())),)*
243 _ => Err(ButtplugMessageError::MessageConversionError("ButtplugServerMessage cannot be converted to #name".to_owned()))
244 }
245 }
246 }
247 };
248 gen.into()
249 } else {
250 panic!("TryFromButtplugServerMessage only works on structs");
251 }
252}
253
254#[proc_macro_derive(FromSpecificButtplugMessage)]
255pub fn from_specific_buttplug_message_derive(input: TokenStream) -> TokenStream {
256 // Construct a representation of Rust code as a syntax tree
257 // that we can manipulate
258 let ast = syn::parse(input).expect("Failure will cause compile failure.");
259
260 impl_from_specific_buttplug_message_derive_macro(&ast)
261}
262
263fn impl_from_specific_buttplug_message_derive_macro(ast: &syn::DeriveInput) -> TokenStream {
264 let name = &ast.ident;
265 if let syn::Data::Enum(ref e) = ast.data {
266 let idents: Vec<_> = e.variants.iter().map(|x| x.ident.clone()).collect();
267 let gen = quote! {
268 #(impl From<#idents> for #name {
269 fn from(msg: #idents) -> #name {
270 #name::#idents(msg)
271 }
272 })*
273 };
274 gen.into()
275 } else {
276 panic!("FromButtplugMessageUnion only works on structs");
277 }
278}
279
280#[proc_macro_derive(ButtplugClientMessageType)]
281pub fn buttplug_client_message_type_derive(input: TokenStream) -> TokenStream {
282 // Construct a representation of Rust code as a syntax tree
283 // that we can manipulate
284 let ast = syn::parse(input).expect("Failure will cause compile failure.");
285
286 // Build the trait implementation
287 impl_buttplug_client_message_type_macro(&ast)
288}
289
290fn impl_buttplug_client_message_type_macro(ast: &syn::DeriveInput) -> TokenStream {
291 let name = &ast.ident;
292 let gen = quote! {
293 impl ButtplugClientMessageType for #name {}
294 };
295 gen.into()
296}
297
298#[proc_macro_derive(ButtplugServerMessageType)]
299pub fn buttplug_server_message_type_derive(input: TokenStream) -> TokenStream {
300 // Construct a representation of Rust code as a syntax tree
301 // that we can manipulate
302 let ast = syn::parse(input).expect("Failure will cause compile failure.");
303
304 // Build the trait implementation
305 impl_buttplug_server_message_type_macro(&ast)
306}
307
308fn impl_buttplug_server_message_type_macro(ast: &syn::DeriveInput) -> TokenStream {
309 let name = &ast.ident;
310 let gen = quote! {
311 impl ButtplugServerMessageType for #name {}
312 };
313 gen.into()
314}