Skip to content

Commit d24091f

Browse files
committed
feat(hlua): integrate hlua into the project
1 parent ca5a114 commit d24091f

35 files changed

+6041
-98
lines changed

Cargo.toml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,13 @@ serde_json = "1.0"
3838
sha-1 = "0.9"
3939
va_list = "0.1.3"
4040

41+
[dependencies.hlua]
42+
path = "hlua/hlua"
43+
44+
[dependencies.lua_ffi]
45+
path = "hlua/ffi"
46+
package = "ffi"
47+
4148
[features]
4249
default = ["net_box"]
4350
net_box = ["lazy_static", "refpool"]
@@ -46,6 +53,7 @@ schema = []
4653
all = ["default", "raft_node", "schema"]
4754

4855
[workspace]
56+
exclude = [ "hlua" ]
4957
members = [
5058
"tests",
5159
"examples/easy",

hlua/Cargo.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
[workspace]
2+
members = [
3+
"hlua",
4+
"ffi",
5+
]

hlua/LICENSE

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
Copyright (c) 2015 Pierre Krieger
2+
3+
Permission is hereby granted, free of charge, to any person obtaining a copy
4+
of this software and associated documentation files (the "Software"), to deal
5+
in the Software without restriction, including without limitation the rights
6+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7+
copies of the Software, and to permit persons to whom the Software is
8+
furnished to do so, subject to the following conditions:
9+
10+
The above copyright notice and this permission notice shall be included in
11+
all copies or substantial portions of the Software.
12+
13+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19+
THE SOFTWARE.

hlua/README.md

Lines changed: 296 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,296 @@
1+
## hlua
2+
3+
This library is a high-level binding for Lua 5.2. You don't have access to the Lua stack, all you can do is read/write variables (including callbacks) and execute Lua code.
4+
5+
[![Build Status](https://travis-ci.org/tomaka/hlua.svg?branch=master)](https://travis-ci.org/tomaka/hlua)
6+
7+
### How to install it?
8+
9+
Add this to the `Cargo.toml` file of your project
10+
11+
```toml
12+
[dependencies]
13+
hlua = "0.3"
14+
```
15+
16+
### How to use it?
17+
18+
```rust
19+
extern crate hlua;
20+
use hlua::Lua;
21+
```
22+
23+
The `Lua` struct is the main element of this library. It represents a context in which you can execute Lua code.
24+
25+
```rust
26+
let mut lua = Lua::new(); // mutable is mandatory
27+
```
28+
29+
**[You can check the documentation here](http://docs.rs/hlua)**.
30+
31+
#### Reading and writing variables
32+
33+
```rust
34+
lua.set("x", 2);
35+
lua.execute::<()>("x = x + 1").unwrap();
36+
let x: i32 = lua.get("x").unwrap(); // x is equal to 3
37+
```
38+
39+
Reading and writing global variables of the Lua context can be done with `set` and `get`.
40+
The `get` function returns an `Option<T>` and does a copy of the value.
41+
42+
The base types that can be read and written are: `i8`, `i16`, `i32`, `u8`, `u16`, `u32`, `f32`, `f64`, `bool`, `String`. `&str` can be written but not read.
43+
44+
If you wish so, you can also add other types by implementing the `Push` and `LuaRead` traits.
45+
46+
#### Executing Lua
47+
48+
```rust
49+
let x: u32 = lua.execute("return 6 * 2;").unwrap(); // equals 12
50+
```
51+
52+
The `execute` function takes a `&str` and returns a `Result<T, ExecutionError>` where `T: LuaRead`.
53+
54+
You can also call `execute_from_reader` which takes a `std::io::Read` as parameter.
55+
For example you can easily execute the content of a file like this:
56+
57+
```rust
58+
lua.execute_from_reader::<()>(File::open(&Path::new("script.lua")).unwrap())
59+
```
60+
61+
#### Writing functions
62+
63+
In order to write a function, you must wrap it around `hlua::functionX` where `X` is the number of parameters. This is for the moment a limitation of Rust's inferrence system.
64+
65+
```rust
66+
fn add(a: i32, b: i32) -> i32 {
67+
a + b
68+
}
69+
70+
lua.set("add", hlua::function2(add));
71+
lua.execute::<()>("local c = add(2, 4)"); // calls the `add` function above
72+
let c: i32 = lua.get("c").unwrap(); // returns 6
73+
```
74+
75+
In Lua, functions are exactly like regular variables.
76+
77+
You can write regular functions as well as closures:
78+
79+
```rust
80+
lua.set("mul", hlua::function2(|a: i32, b: i32| a * b));
81+
```
82+
83+
Note that the lifetime of the Lua context must be equal to or shorter than the lifetime of closures. This is enforced at compile-time.
84+
85+
```rust
86+
let mut a = 5i;
87+
88+
{
89+
let mut lua = Lua::new();
90+
91+
lua.set("inc", || a += 1); // borrows 'a'
92+
for i in (0 .. 15) {
93+
lua.execute::<()>("inc()").unwrap();
94+
}
95+
} // unborrows `a`
96+
97+
assert_eq!(a, 20)
98+
```
99+
100+
##### Error handling
101+
102+
If your Rust function returns a `Result` object which contains an error, then a Lua error will be triggered.
103+
104+
#### Manipulating Lua tables
105+
106+
Manipulating a Lua table can be done by reading a `LuaTable` object. This can be achieved easily by reading a `LuaTable` object.
107+
108+
```rust
109+
let mut table: hlua::LuaTable<_> = lua.get("a").unwrap();
110+
```
111+
112+
You can then iterate through the table with the `.iter()` function. Note that the value returned by the iterator is an `Option<(Key, Value)>`, the `Option` being empty when either the key or the value is not convertible to the requested type. The `filter_map` function (provided by the standard `Iterator` trait) is very useful when dealing with this.
113+
114+
```rust
115+
for (key, value) in table.iter().filter_map(|e| e) {
116+
...
117+
}
118+
```
119+
120+
You can also retreive and modify individual indices:
121+
122+
```rust
123+
let x = table.get("a").unwrap();
124+
table.set("b", "hello");
125+
```
126+
127+
#### Calling Lua functions
128+
129+
You can call Lua functions by reading a `functions_read::LuaFunction`.
130+
131+
```rust
132+
lua.execute::<()>("
133+
function get_five()
134+
return 5
135+
end");
136+
137+
let get_five: hlua::LuaFunction<_> = lua.get("get_five").unwrap();
138+
let value: i32 = get_five.call().unwrap();
139+
assert_eq!(value, 5);
140+
```
141+
142+
This object holds a mutable reference of `Lua`, so you can't read or modify anything in the Lua context while the `get_five` variable exists.
143+
It is not possible to store the function for the moment, but it may be in the future.
144+
145+
#### Reading and writing Rust containers
146+
147+
*(note: not yet possible to read all containers, see below)*
148+
149+
It is possible to read and write whole Rust containers at once:
150+
151+
```rust
152+
lua.set("a", [ 12, 13, 14, 15 ]);
153+
let hashmap: HashMap<i32, f64> = [1., 2., 3.].into_iter().enumerate().map(|(k, v)| (k as i32, *v as f64)).collect();
154+
lua.set("v", hashmap);
155+
```
156+
157+
If the container has single elements, then the indices will be numerical. For example in the code above, the `12` will be at index `1`, the `13` at index `2`, etc.
158+
159+
If the container has tuples of two elements, then the first one will be considered as the key and the second one as the value.
160+
161+
This can be useful to create APIs:
162+
163+
```rust
164+
fn foo() { }
165+
fn bar() { }
166+
167+
lua.set("mylib", [
168+
("foo", hlua::function0(foo)),
169+
("bar", hlua::function0(bar))
170+
]);
171+
172+
lua.execute::<()>("mylib.foo()");
173+
```
174+
175+
It is possible to read a `Vec<AnyLuaValue>`:
176+
177+
```rust
178+
let mut lua = Lua::new();
179+
180+
lua.execute::<()>(r#"v = { 1, 2, 3 }"#).unwrap();
181+
182+
let read: Vec<_> = lua.get("v").unwrap();
183+
assert_eq!(
184+
read,
185+
[1., 2., 3.].iter()
186+
.map(|x| AnyLuaValue::LuaNumber(*x)).collect::<Vec<_>>());
187+
```
188+
189+
In case table represents sparse array, has non-numeric keys, or
190+
indices not starting at 1, `.get()` will return `None`, as Rust's
191+
`Vec` doesn't support these features.
192+
193+
It is possible to read a `HashMap<AnyHashableLuaValue, AnyLuaValue>`:
194+
195+
```rust
196+
let mut lua = Lua::new();
197+
198+
lua.execute::<()>(r#"v = { [-1] = -1, ["foo"] = 2, [2.] = 42 }"#).unwrap();
199+
200+
let read: HashMap<_, _> = lua.get("v").unwrap();
201+
assert_eq!(read[&AnyHashableLuaValue::LuaNumber(-1)], AnyLuaValue::LuaNumber(-1.));
202+
assert_eq!(read[&AnyHashableLuaValue::LuaString("foo".to_owned())], AnyLuaValue::LuaNumber(2.));
203+
assert_eq!(read[&AnyHashableLuaValue::LuaNumber(2)], AnyLuaValue::LuaNumber(42.));
204+
assert_eq!(read.len(), 3);
205+
```
206+
207+
#### User data
208+
209+
**(note: the API here is very unstable for the moment)**
210+
211+
When you expose functions to Lua, you may wish to read or write more elaborate objects. This is called a **user data**.
212+
213+
To do so, you should implement the `Push`, `CopyRead` and `ConsumeRead` for your types.
214+
This is usually done by redirecting the call to `userdata::push_userdata`.
215+
216+
```rust
217+
struct Foo;
218+
219+
impl<L> hlua::Push<L> for Foo where L: hlua::AsMutLua<'lua> {
220+
fn push_to_lua(self, lua: L) -> hlua::PushGuard<L> {
221+
lua::userdata::push_userdata(self, lua,
222+
|mut metatable| {
223+
// you can define all the member functions of Foo here
224+
// see the official Lua documentation for metatables
225+
metatable.set("__call", hlua::function0(|| println!("hello from foo")))
226+
})
227+
}
228+
}
229+
230+
fn main() {
231+
let mut lua = lua::Lua::new();
232+
lua.set("foo", Foo);
233+
lua.execute::<()>("foo()"); // prints "hello from foo"
234+
}
235+
```
236+
237+
### Creating a Lua module
238+
239+
**Note: OBSOLETE ; this is still some pre-Rust-1.0 stuff**
240+
241+
This library also includes a second library named `rust-hl-lua-module` which allows you to create Lua modules in Rust.
242+
243+
To use it, add this to `Cargo.toml`:
244+
245+
```toml
246+
[dependencies.rust-hl-lua-modules]
247+
git = "https://github.com/tomaka/hlua"
248+
```
249+
250+
Then you can use it like this:
251+
252+
```rust
253+
#![feature(phase)]
254+
#[!plugin(rust-hl-lua-modules)]
255+
256+
#[export_lua_module]
257+
pub mod mylib { // <-- must be the name of the Lua module
258+
static PI: f32 = 3.141592;
259+
260+
fn function1(a: int, b: int) -> int {
261+
a + b
262+
}
263+
264+
fn function2(a: int) -> int {
265+
a + 5
266+
}
267+
268+
#[lua_module_init]
269+
fn init() {
270+
println!("module initialized!")
271+
}
272+
}
273+
```
274+
275+
This module will then be usable by Lua:
276+
277+
```lua
278+
> mylib = require("mylib")
279+
module initialized!
280+
> return mylib.function1(2, 4)
281+
6
282+
> return mylib.PI
283+
3.141592
284+
```
285+
286+
Two syntax extensions are defined:
287+
- `#[export_lua_module]`: Must be put in front of a module. The name of the module must be the same as the name of your Lua module.
288+
- `#[lua_module_init]`: Can be put in front of a function inside the module. This function will be executed when the module is loaded.
289+
290+
**Restrictions**:
291+
- `fail!()` will crash the program.
292+
- If you spawn tasks, they will have to end before the hand is given back to lua.
293+
294+
### Contributing
295+
296+
Contributions are welcome!

hlua/ffi/Cargo.toml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
[package]
2+
name = "ffi"
3+
version = "0.1.0"
4+
edition = "2018"
5+
authors = [ "gmoshkin@picodata.io" ]
6+
7+
[dependencies]
8+
libc = "0.2"

0 commit comments

Comments
 (0)