1#![recursion_limit = "256"]
2
3extern crate proc_macro;
4use proc_macro2::{Ident, Literal};
5use std::collections::HashMap;
6
7use syn::{
8 parse::{Parse, ParseStream, Result},
9 parse_macro_input,
10 punctuated::Punctuated,
11 Error, LitStr,
12};
13
14use quote::quote;
15
16struct WeechatPluginInfo {
17 plugin: syn::Ident,
18 name: (usize, Literal),
19 author: (usize, Literal),
20 description: (usize, Literal),
21 version: (usize, Literal),
22 license: (usize, Literal),
23}
24
25enum WeechatVariable {
26 Name(syn::LitStr),
27 Author(syn::LitStr),
28 Description(syn::LitStr),
29 Version(syn::LitStr),
30 License(syn::LitStr),
31}
32
33impl WeechatVariable {
34 #[allow(clippy::wrong_self_convention)]
35 fn to_pair(string: &LitStr) -> (usize, Literal) {
36 let mut bytes = string.value().into_bytes();
37 bytes.push(0);
39
40 (bytes.len(), Literal::byte_string(&bytes))
41 }
42
43 fn as_pair(&self) -> (usize, Literal) {
44 match self {
45 WeechatVariable::Name(string) => WeechatVariable::to_pair(string),
46 WeechatVariable::Author(string) => WeechatVariable::to_pair(string),
47 WeechatVariable::Description(string) => WeechatVariable::to_pair(string),
48 WeechatVariable::Version(string) => WeechatVariable::to_pair(string),
49 WeechatVariable::License(string) => WeechatVariable::to_pair(string),
50 }
51 }
52
53 fn default_literal() -> (usize, Literal) {
54 let bytes = vec![0];
55 (bytes.len(), Literal::byte_string(&bytes))
56 }
57}
58
59impl Parse for WeechatVariable {
60 fn parse(input: ParseStream) -> Result<Self> {
61 let key: Ident = input.parse()?;
62 input.parse::<syn::Token![:]>()?;
63 let value = input.parse()?;
64
65 match key.to_string().to_lowercase().as_ref() {
66 "name" => Ok(WeechatVariable::Name(value)),
67 "author" => Ok(WeechatVariable::Author(value)),
68 "description" => Ok(WeechatVariable::Description(value)),
69 "version" => Ok(WeechatVariable::Version(value)),
70 "license" => Ok(WeechatVariable::License(value)),
71 _ => Err(Error::new(
72 key.span(),
73 "expected one of name, author, description, version or license",
74 )),
75 }
76 }
77}
78
79impl Parse for WeechatPluginInfo {
80 fn parse(input: ParseStream) -> Result<Self> {
81 let plugin: syn::Ident = input.parse().map_err(|_e| {
82 Error::new(
83 input.span(),
84 "a struct that implements the Plugin trait needs to be given",
85 )
86 })?;
87 input.parse::<syn::Token![,]>()?;
88
89 let args: Punctuated<WeechatVariable, syn::Token![,]> =
90 input.parse_terminated(WeechatVariable::parse)?;
91 let mut variables = HashMap::new();
92
93 for arg in args.pairs() {
94 let variable = arg.value();
95 match variable {
96 WeechatVariable::Name(_) => variables.insert("name", *variable),
97 WeechatVariable::Author(_) => variables.insert("author", *variable),
98 WeechatVariable::Description(_) => variables.insert("description", *variable),
99 WeechatVariable::Version(_) => variables.insert("version", *variable),
100 WeechatVariable::License(_) => variables.insert("license", *variable),
101 };
102 }
103
104 Ok(WeechatPluginInfo {
105 plugin,
106 name: variables.remove("name").map_or_else(
107 || {
108 Err(Error::new(
109 input.span(),
110 "the name of the plugin needs to be defined",
111 ))
112 },
113 |v| Ok(v.as_pair()),
114 )?,
115 author: variables
116 .remove("author")
117 .map_or_else(WeechatVariable::default_literal, |v| v.as_pair()),
118 description: variables
119 .remove("description")
120 .map_or_else(WeechatVariable::default_literal, |v| v.as_pair()),
121 version: variables
122 .remove("version")
123 .map_or_else(WeechatVariable::default_literal, |v| v.as_pair()),
124 license: variables
125 .remove("license")
126 .map_or_else(WeechatVariable::default_literal, |v| v.as_pair()),
127 })
128 }
129}
130
131#[proc_macro]
155pub fn plugin(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
156 let WeechatPluginInfo {
157 plugin,
158 name,
159 author,
160 description,
161 version,
162 license,
163 } = parse_macro_input!(input as WeechatPluginInfo);
164
165 let (name_len, name) = name;
166 let (author_len, author) = author;
167 let (description_len, description) = description;
168 let (license_len, license) = license;
169 let (version_len, version) = version;
170
171 let result = quote! {
172 #[doc(hidden)]
173 #[no_mangle]
174 pub static weechat_plugin_api_version: [u8; weechat::weechat_sys::WEECHAT_PLUGIN_API_VERSION_LENGTH] =
175 *weechat::weechat_sys::WEECHAT_PLUGIN_API_VERSION;
176
177 #[doc(hidden)]
178 #[no_mangle]
179 pub static weechat_plugin_name: [u8; #name_len] = *#name;
180
181 #[doc(hidden)]
182 #[no_mangle]
183 pub static weechat_plugin_author: [u8; #author_len] = *#author;
184
185 #[doc(hidden)]
186 #[no_mangle]
187 pub static weechat_plugin_description: [u8; #description_len] = *#description;
188
189 #[doc(hidden)]
190 #[no_mangle]
191 pub static weechat_plugin_version: [u8; #version_len] = *#version;
192
193 #[doc(hidden)]
194 #[no_mangle]
195 pub static weechat_plugin_license: [u8; #license_len] = *#license;
196
197 #[doc(hidden)]
198 static mut __PLUGIN: Option<#plugin> = None;
199
200 #[doc(hidden)]
206 #[no_mangle]
207 pub unsafe extern "C" fn weechat_plugin_init(
208 plugin: *mut weechat::weechat_sys::t_weechat_plugin,
209 argc: weechat::libc::c_int,
210 argv: *mut *mut weechat::libc::c_char,
211 ) -> weechat::libc::c_int {
212 let weechat = unsafe {
213 Weechat::init_from_ptr(plugin)
214 };
215 let args = Args::new(argc, argv);
216 match <#plugin as ::weechat::Plugin>::init(&weechat, args) {
217 Ok(p) => {
218 unsafe {
219 __PLUGIN = Some(p);
220 }
221 return weechat::weechat_sys::WEECHAT_RC_OK;
222 }
223 Err(_e) => {
224 return weechat::weechat_sys::WEECHAT_RC_ERROR;
225 }
226 }
227 }
228
229 #[doc(hidden)]
235 #[no_mangle]
236 pub unsafe extern "C" fn weechat_plugin_end(
237 _plugin: *mut weechat::weechat_sys::t_weechat_plugin
238 ) -> weechat::libc::c_int {
239 unsafe {
240 __PLUGIN = None;
241 Weechat::free();
242 }
243 weechat::weechat_sys::WEECHAT_RC_OK
244 }
245
246 impl #plugin {
247 pub fn get() -> &'static mut #plugin {
254 unsafe {
255 match &mut __PLUGIN {
256 Some(p) => p,
257 None => panic!("Weechat plugin isn't initialized"),
258 }
259 }
260 }
261 }
262 };
263
264 result.into()
265}