Buttplug sex toy control library
at dev 6.9 kB view raw
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}