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
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 r#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 r#gen.into()
46 }
47 syn::Data::Struct(_) => {
48 let r#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 r#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 r#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 r#gen.into()
97 }
98 syn::Data::Struct(_) => {
99 let r#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 r#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 r#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 r#gen.into()
142 }
143 syn::Data::Struct(_) => {
144 let r#gen = quote! {
145 impl ButtplugMessageValidator for #name {
146 }
147 };
148 r#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 r#gen = quote! {
170 impl ButtplugMessageFinalizer for #name {}
171 };
172 r#gen.into()
173 }
174 syn::Data::Struct(_) => {
175 let r#gen = quote! {
176 impl ButtplugMessageFinalizer for #name {}
177 };
178 r#gen.into()
179 }
180 _ => panic!("Derivation only works on structs and enums"),
181 }
182}
183
184#[proc_macro_derive(FromSpecificButtplugMessage)]
185pub fn from_specific_buttplug_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_from_specific_buttplug_message_derive_macro(&ast)
191}
192
193fn impl_from_specific_buttplug_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 // Unlike try_from, where we expect all of our field identifiers to match, we may have different
198 // identifiers and field types when implementing from_specific. Therefore we need to parallel
199 // iterate our field identifiers and the identifier of the first member. This means we're locked
200 // to an enum style of field name([unnamed type]), but we're the only ones who use this macro,
201 // and on structs that almost never change, so hopefully leaving this comment will be enough.
202 let mut fields: Vec<_> = vec![];
203 for var in e.variants.iter() {
204 for field in var.fields.iter() {
205 fields.push(field.ty.clone());
206 }
207 }
208 let r#gen = quote! {
209 #(impl From<#fields> for #name {
210 fn from(msg: #fields) -> #name {
211 #name::#idents(msg)
212 }
213 })*
214 };
215 r#gen.into()
216 } else {
217 panic!("FromButtplugMessageUnion only works on structs");
218 }
219}