luaext/types/
function.rs

1use lua::{Index, ToLua, FromLua, State, MULTRET};
2use types::{LuaStackable, LuaGeneric};
3use context::Context;
4use error;
5
6/// Reperesents a callable function on the Lua stack
7///
8/// Functions that can be called may be defined in Lua, Rust, C, or any other language with Lua
9/// bindings.
10///
11/// # Examples
12///
13/// ```
14/// # use luaext::lua::State;
15/// # use luaext::context::Context;
16/// # use luaext::types::function::LuaFunction;
17/// # let mut state = State::new();
18/// # let mut context = Context::new(&mut state);
19/// context.do_string("function double(x) return x * 2 end").unwrap();
20/// let lua_double: LuaFunction = context.push_global("double")
21///     .get_value(&mut context).unwrap();
22/// let result = lua_double.call_singleret(&mut context, &[&4])
23///     .and_then(|v| v.get_value(&mut context));
24/// assert_eq!(result, Some(8));
25/// ```
26pub struct LuaFunction {
27    index: Index
28}
29
30fn get_callback_index(val: &Option<&LuaFunction>) -> Index {
31    match *val {
32        Some(val) => val.get_pos(),
33        None => 0
34    }
35}
36
37impl LuaFunction {
38    /// Create a new LuaFunction given an index
39    pub fn new(i: Index) -> LuaFunction {
40        LuaFunction {
41            index: i
42        }
43    }
44
45    /// Protected function call, errors return an Err
46    /// If the function call is successful, returns a list of all return values (with a maximum
47    /// size nresults)
48    ///
49    /// # Errors
50    ///
51    /// Returns an Error if Lua encounters a runtime error while running this function.
52    /// If an errfunc is given, then the given function will be called with the error message as
53    /// an argument, and the function should return a new error message.
54    /// errfunc will not be called if no error was encountered, the error that occured was an
55    /// out of memeory error, or if another error occurred while running errfunc.
56    pub fn pcall(&self, context: &mut Context, args: &[&ToLua], errfunc: Option<&LuaFunction>,
57            nresults: i32) -> error::Result<Vec<LuaGeneric>> {
58        let top_prev = context.get_state().get_top();
59        context.get_state().push_value(self.get_pos());
60        for arg in args {
61            arg.to_lua(context.get_state());
62        }
63        let threadstatus = context.get_state().pcall(
64            args.len() as i32, nresults, get_callback_index(&errfunc));
65        match error::get_status_from_threadstatus(threadstatus) {
66            Err(status) => {
67                error::new_luaresult_err(status, error::pop_error_from_state(context.get_state()))
68            },
69            Ok(_) => {
70                let top_post = context.get_state().get_top();
71                error::new_luaresult_ok((top_prev..top_post)
72                    .map(|i| LuaGeneric::new(i+1))
73                    .collect())
74            }
75        }
76    }
77
78    /// Same as pcall, but returns all return values
79    pub fn pcall_multiret(&self, context: &mut Context, args: &[&ToLua],
80            errfunc: Option<&LuaFunction>) -> error::Result<Vec<LuaGeneric>> {
81        self.pcall(context, args, errfunc, MULTRET)
82    }
83
84    /// Same as pcall, but only returns at most one return value.
85    pub fn pcall_singleret(&self, context: &mut Context, args: &[&ToLua],
86            errfunc: Option<&LuaFunction>) -> error::Result<Option<LuaGeneric>> {
87        self.pcall(context, args, errfunc, 1)
88            .map(|mut v| {
89                match v.len() {
90                    0 => None,
91                    _ => Some(v.swap_remove(0))
92                }
93            })
94    }
95
96    /// Same as pcall, but returns nothing.
97    pub fn pcall_noret(&self, context: &mut Context, args: &[&ToLua],
98            errfunc: Option<&LuaFunction>) -> error::Result<()> {
99        self.pcall(context, args, errfunc, 0)
100            .map(|_|())
101    }
102
103    /// Call this function, returns a list of return values (with a maximum
104    /// size nresults).
105    ///
106    /// # Panics
107    ///
108    /// This function will panic if Lua encounters a runtime error. It's not really a panic, it's
109    /// actually a longjmp, but it might as well be a panic.
110    pub fn call(&self, context: &mut Context, args: &[&ToLua], nresults: i32) -> Vec<LuaGeneric> {
111        let top_prev = context.get_state().get_top();
112        context.get_state().push_value(self.get_pos());
113        for arg in args {
114            arg.to_lua(context.get_state());
115        }
116        context.get_state().call(args.len() as i32, nresults);
117        let top_post = context.get_state().get_top();
118        (top_prev..top_post)
119            .map(|i| LuaGeneric::new(i+1))
120            .collect()
121    }
122
123    /// Same as call, but returns at most one return value.
124    pub fn call_singleret(&self, context: &mut Context, args: &[&ToLua]) -> Option<LuaGeneric> {
125        let mut result = self.call(context, args, 1);
126        match result.len() {
127            0 => None,
128            _ => Some(result.swap_remove(0))
129        }
130    }
131
132    /// Same as call, but returns all return values.
133    pub fn call_multiret(&self, context: &mut Context, args: &[&ToLua]) -> Vec<LuaGeneric> {
134        self.call(context, args, MULTRET)
135    }
136
137    /// Same as call, but does not return any return values.
138    pub fn call_noret(&self, context: &mut Context, args: &[&ToLua]) {
139        self.call(context, args, 0);
140    }
141}
142
143impl LuaStackable for LuaFunction {
144    fn get_pos(&self) -> Index {
145        self.index
146    }
147}
148
149impl ToLua for LuaFunction {
150    fn to_lua(&self, state: &mut State) {
151        state.push_value(self.get_pos());
152    }
153}
154
155impl FromLua for LuaFunction {
156    fn from_lua(state: &mut State, index: Index) -> Option<LuaFunction> {
157        if state.is_fn(index) {
158            Some(LuaFunction::new(index))
159        } else {
160            None
161        }
162    }
163}