1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
//! rudeboy - Rlua User Data Extension library
//!
//! Provides attribute macros which allow for easily generating metamethod
//! implementations, exporting methods, and exporting rust structs and enums to
//! lua with the use of the `rlua` crate.
//!
//! # Generating metamethods
//! To generate metamethods for a struct or enum, use the [`metamethods`]
//! attribute on the type's definition, along with a list of parameters
//! indicating the metamethods to generate implementations for. With the
//! exception of the Index metamethod, which can only be generated for structs
//! with named fields and which parses the struct definition, every generated
//! metamethod expects and uses the corresponding rust trait to provide 
//! implementation details. See the [`metamethods`] attribute documentation for
//! the full list of supported metamethods and the rust traits each one uses.
//!
//! ## Examples
//! ```
//! # fn test() -> rlua::Result<()> {
//! use rudeboy::{metamethods, user_data};
//!
//! #[metamethods(Add, Eq, Index)]
//! #[user_data(MetaMethods)]
//! #[derive(PartialEq, Copy, Clone)]
//! struct Test {
//!     x: i32,
//! }
//!
//! impl std::ops::Add for Test {
//!     type Output = Self;
//!     fn add(self, other: Self) -> Self {
//!         Test { x: self.x + other.x }
//!     }
//! }
//!
//! let lua = rlua::Lua::new();
//! lua.context(|ctx| {
//!     let globals = ctx.globals();
//!     globals.set("one", Test { x: 1 })?;
//!     globals.set("two", Test { x: 2 })?;
//!     ctx.load("sum = one + one").exec()?;
//!     assert!(ctx.load("sum == two").eval::<bool>()?);
//!     assert_eq!(ctx.load("sum.x").eval::<i32>()?, 2);
//!
//!     Ok(())
//! })?;
//! # Ok(())
//! # }
//! # assert!(test().is_ok());
//! ```
//!
//! # Exporting methods
//! To export methods for a struct or enum, use the [`methods`] attribute on the
//! impl block for the corresponding type, containing the methods that you wish
//! to export to lua. Currently, there is no way to ignore a method other than
//! placing it in a separate impl block without the [`methods`] attribute.
//!
//! ## Examples
//! ```
//! # fn test() -> rlua::Result<()> {
//! use rudeboy::{methods, user_data};
//!
//! #[user_data(Methods)]
//! #[derive(PartialEq, Debug, Clone)]
//! enum Sign {
//!     Minus,
//!     Plus,
//! }
//!
//! #[methods]
//! impl Sign {
//!     // This method will be exposed to lua...
//!     pub fn flip(&self) -> Self {
//!         match self {
//!             Sign::Minus => Sign::Plus,
//!             Sign::Plus => Sign::Minus,
//!         }
//!     }
//! }
//!
//! 
//! impl Sign {
//!     // ... but this method won't
//!     pub fn apply(&self, x: i32) -> i32 {
//!         match self {
//!             Sign::Minus => -x,
//!             Sign::Plus => x,
//!         }
//!     }
//! }
//!
//! let lua = rlua::Lua::new();
//! lua.context(|ctx| {
//!     let globals = ctx.globals();
//!     globals.set("plus", Sign::Plus)?;
//!     globals.set("minus", Sign::Minus)?;
//!
//!     assert_eq!(ctx.load("plus:flip()").eval::<Sign>()?, Sign::Minus);
//!     assert_eq!(ctx.load("minus:flip()").eval::<Sign>()?, Sign::Plus);
//!     assert_eq!(ctx.load("minus:flip():flip()").eval::<Sign>()?, Sign::Minus);
//!
//!     Ok(())
//! })?;
//! # Ok(())
//! # }
//! # assert!(test().is_ok());
//! ```
//!
//! # Generating `rlua::UserData` implementation
//! To export a struct or enum for use with `rlua`, use the [`user_data`]
//! attribute on the type definition or an associated impl block. When given no
//! parameters, an empty `UserData` implementation is generated that provides no
//! metamethods or methods for use in lua. If given the `MetaMethods` parameter,
//! the metamethods generated by [`metamethods`] will be exported, and if given
//! the `Methods` parameter, the methods generated by [`methods`] will be
//! exported. Note that this attribute will prevent the user from adding any
//! additional methods to the exported type beyond what can be generated with the
//! [`methods`] and [`metamethods`] attributes. If you wish to export additional
//! methods, write your own implementation of `rlua::UserData` and call the
//! appropriate methods from the [`RudeboyMethods`] and [`RudeboyMetaMethods`]
//! traits.
//!
//! ## Examples
//! ```
//! # fn test() -> rlua::Result<()> {
//! use rudeboy::{metamethods, methods, user_data};
//!
//! // Tagging a type with user_data with no parameters means it can be passed
//! // in and out of lua, but no metamethods or methods will be available
//! #[user_data]
//! #[derive(Clone)]
//! enum Amount {
//!     Single,
//!     Double,
//! }
//!
//! #[user_data(Methods)]
//! #[derive(Clone, PartialEq)]
//! struct Test {
//!     x: f64,
//! }
//!
//! #[methods]
//! impl Test {
//!     pub fn get(&self, amount: Amount) -> f64 {
//!         match amount {
//!             Amount::Single => self.x,
//!             Amount::Double => self.x * 2.0,
//!         }
//!     }
//! }
//!
//! let lua = rlua::Lua::new();
//! lua.context(|ctx| {
//!     let globals = ctx.globals();
//!     globals.set("single", Amount::Single)?;
//!     globals.set("double", Amount::Double)?;
//!     globals.set("test", Test { x: 5.0 })?;
//!
//!     assert_eq!(ctx.load("test:get(single)").eval::<f64>()?, 5.0);
//!     assert_eq!(ctx.load("test:get(double)").eval::<f64>()?, 10.0);
//!
//!     Ok(())
//! })?;
//! # Ok(())
//! # }
//! # assert!(test().is_ok());
//! ```
//!
//! [`metamethods`]: attr.metamethods.html
//! [`methods`]: attr.methods.html
//! [`user_data`]: attr.user_data.html
//! [`RudeboyMetaMethods`]: trait.RudeboyMetaMethods.html
//! [`RudeboyMethods`]: trait.RudeboyMethods.html
pub use rudeboy_derive::{
    metamethods,
    methods,
    user_data,
};

use rlua::{UserData, UserDataMethods};

/// Provides methods for registering each supported metamethod. The
/// `generate_metamethods` method will call all of them. Generated by the
/// [`metamethods`] attribute macro.
///
/// [`metamethods`]: attr.metamethods.html
pub trait RudeboyMetaMethods : Sized + UserData {
    /// The index metamethod for accessing fields using the dot syntax:
    /// `instance.field`
    fn generate_index<'lua, M: UserDataMethods<'lua, Self>>(_methods: &mut M) {}

    /// The equality metamethod for the binary `==` operator
    fn generate_eq<'lua, M: UserDataMethods<'lua, Self>>(_methods: &mut M) {}

    /// The addition metamethod for the binary `+` operator
    fn generate_add<'lua, M: UserDataMethods<'lua, Self>>(_methods: &mut M) {}

    /// The subtraction metamethod for the binary `-` operator
    fn generate_sub<'lua, M: UserDataMethods<'lua, Self>>(_methods: &mut M) {}

    /// The multiplication metamethod for the binary `*` operator
    fn generate_mul<'lua, M: UserDataMethods<'lua, Self>>(_methods: &mut M) {}

    /// The division metamethod for the binary `/` operator
    fn generate_div<'lua, M: UserDataMethods<'lua, Self>>(_methods: &mut M) {}

    /// The modulo metamethod for the binary `%` operator
    fn generate_mod<'lua, M: UserDataMethods<'lua, Self>>(_methods: &mut M) {}

    /// The unary minus metamethod for the unary `-` operator
    fn generate_unm<'lua, M: UserDataMethods<'lua, Self>>(_methods: &mut M) {}

    /// The bitwise and metamethod for the binary `&` operator
    fn generate_band<'lua, M: UserDataMethods<'lua, Self>>(_methods: &mut M) {}

    /// The bitwise or metamethod for the binary `|` operator
    fn generate_bor<'lua, M: UserDataMethods<'lua, Self>>(_methods: &mut M) {}

    /// The bitwise xor metamethod for the binary `~` operator
    fn generate_bxor<'lua, M: UserDataMethods<'lua, Self>>(_methods: &mut M) {}

    /// The bitwise not metamethod for the unary `~` operator
    fn generate_bnot<'lua, M: UserDataMethods<'lua, Self>>(_methods: &mut M) {}

    /// The shift left metamethod for the binary `<<` operator
    fn generate_shl<'lua, M: UserDataMethods<'lua, Self>>(_methods: &mut M) {}

    /// The shift right metamethod for the binary `>>` operator
    fn generate_shr<'lua, M: UserDataMethods<'lua, Self>>(_methods: &mut M) {}

    /// The less than metamethod for the binary `<` operator
    fn generate_lt<'lua, M: UserDataMethods<'lua, Self>>(_methods: &mut M) {}

    /// The less-than-or-equal metamethod for the binary `<=` operator
    fn generate_le<'lua, M: UserDataMethods<'lua, Self>>(_methods: &mut M) {}

    /// Calls every individual `generate_*` method in this trait
    fn generate_metamethods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
        Self::generate_index(methods);
        Self::generate_eq(methods);
        Self::generate_add(methods);
        Self::generate_sub(methods);
        Self::generate_mul(methods);
        Self::generate_div(methods);
        Self::generate_mod(methods);
        Self::generate_unm(methods);
        Self::generate_band(methods);
        Self::generate_bor(methods);
        Self::generate_bxor(methods);
        Self::generate_bnot(methods);
        Self::generate_shl(methods);
        Self::generate_shr(methods);
        Self::generate_lt(methods);
        Self::generate_le(methods);
    }
}

/// Used to expose, to rlua, rust methods for a UserData struct
///
/// Implementations provided by [`methods`]
///
/// [`methods`]: attr.methods.html
pub trait RudeboyMethods : Sized + UserData {
    fn generate_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M);
}