actix_lua/
message.rs

1use ::actix::dev::{MessageResponse, ResponseChannel};
2use ::actix::prelude::*;
3use regex::Regex;
4use rlua::Result as LuaResult;
5use rlua::{Context, FromLua, ToLua, Value};
6
7use std::collections::HashMap;
8
9#[derive(Debug, PartialEq, Clone)]
10pub enum LuaMessage {
11    String(String),
12    Integer(i64),
13    Number(f64),
14    Boolean(bool),
15    Nil,
16    Table(HashMap<String, LuaMessage>),
17    ThreadYield(String),
18}
19
20impl<A, M> MessageResponse<A, M> for LuaMessage
21where
22    A: Actor,
23    M: Message<Result = LuaMessage>,
24{
25    fn handle<R: ResponseChannel<M>>(self, _: &mut A::Context, tx: Option<R>) {
26        if let Some(tx) = tx {
27            tx.send(self);
28        }
29    }
30}
31
32impl Message for LuaMessage {
33    type Result = LuaMessage;
34}
35
36impl From<bool> for LuaMessage {
37    fn from(s: bool) -> Self {
38        LuaMessage::Boolean(s)
39    }
40}
41
42impl<'l> From<&'l str> for LuaMessage {
43    fn from(s: &'l str) -> Self {
44        LuaMessage::String(s.to_string())
45    }
46}
47
48impl From<String> for LuaMessage {
49    fn from(s: String) -> Self {
50        LuaMessage::String(s)
51    }
52}
53
54macro_rules! lua_message_convert_int {
55    ($x:ty) => {
56        impl From<$x> for LuaMessage {
57            fn from(s: $x) -> Self {
58                LuaMessage::Integer(i64::from(s))
59            }
60        }
61    };
62}
63
64lua_message_convert_int!(i8);
65lua_message_convert_int!(u8);
66lua_message_convert_int!(i16);
67lua_message_convert_int!(u16);
68lua_message_convert_int!(i32);
69lua_message_convert_int!(u32);
70lua_message_convert_int!(i64);
71
72impl From<usize> for LuaMessage {
73    fn from(s: usize) -> Self {
74        LuaMessage::Integer(s as i64)
75    }
76}
77
78impl From<isize> for LuaMessage {
79    fn from(s: isize) -> Self {
80        LuaMessage::Integer(s as i64)
81    }
82}
83
84impl From<HashMap<String, LuaMessage>> for LuaMessage {
85    fn from(s: HashMap<String, LuaMessage>) -> Self {
86        LuaMessage::Table(s)
87    }
88}
89
90macro_rules! lua_message_convert_float {
91    ($x:ty) => {
92        impl From<$x> for LuaMessage {
93            fn from(s: $x) -> Self {
94                LuaMessage::Number(f64::from(s))
95            }
96        }
97    };
98}
99
100lua_message_convert_float!(f32);
101lua_message_convert_float!(f64);
102
103impl<'lua> FromLua<'lua> for LuaMessage {
104    fn from_lua(v: Value<'lua>, ctx: Context<'lua>) -> LuaResult<LuaMessage> {
105        match v {
106            Value::String(x) => {
107                let re = Regex::new(r"__suspended__(.+)").unwrap();
108                let s = Value::String(x);
109                if let Some(cap) = re.captures(&String::from_lua(s.clone(), ctx)?) {
110                    let tid = cap.get(1).unwrap().as_str();
111                    Ok(LuaMessage::ThreadYield(tid.to_string()))
112                } else {
113                    Ok(LuaMessage::String(String::from_lua(s.clone(), ctx)?))
114                }
115            }
116            Value::Integer(n) => Ok(LuaMessage::Integer(n as i64)),
117            Value::Number(n) => Ok(LuaMessage::Number(n as f64)),
118            Value::Boolean(b) => Ok(LuaMessage::Boolean(b)),
119            Value::Nil => Ok(LuaMessage::Nil),
120            Value::Table(t) => Ok(LuaMessage::Table(HashMap::from_lua(Value::Table(t), ctx)?)),
121            Value::Error(err) => {
122                panic!("Lua error: {:?}", err);
123            }
124            _ => unimplemented!(),
125        }
126    }
127}
128
129impl<'lua> ToLua<'lua> for LuaMessage {
130    fn to_lua(self, ctx: Context<'lua>) -> LuaResult<Value<'lua>> {
131        match self {
132            LuaMessage::String(x) => Ok(Value::String(ctx.create_string(&x)?)),
133            LuaMessage::Integer(x) => Ok(Value::Integer(x)),
134            LuaMessage::Number(x) => Ok(Value::Number(x)),
135            LuaMessage::Boolean(x) => Ok(Value::Boolean(x)),
136            LuaMessage::Nil => Ok(Value::Nil),
137            LuaMessage::Table(x) => Ok(Value::Table(ctx.create_table_from(x)?)),
138
139            // TODO: passing rust error to lua error?
140            _ => unimplemented!(),
141        }
142    }
143}
144
145#[cfg(test)]
146mod tests {
147    use super::*;
148    use rlua::Lua;
149    use std::mem::discriminant;
150
151    #[test]
152    fn constructors() {
153        assert_eq!(LuaMessage::from(42), LuaMessage::Integer(42));
154        assert_eq!(LuaMessage::from(0), LuaMessage::Integer(0));
155        assert_eq!(
156            LuaMessage::from("foo"),
157            LuaMessage::String("foo".to_string())
158        );
159        assert_eq!(LuaMessage::from(42.5), LuaMessage::Number(42.5));
160        assert_eq!(LuaMessage::from(true), LuaMessage::Boolean(true));
161
162        let mut t = HashMap::new();
163        t.insert("bar".to_string(), LuaMessage::from("abc"));
164        let mut t2 = HashMap::new();
165        t2.insert("bar".to_string(), LuaMessage::from("abc"));
166        assert_eq!(LuaMessage::from(t), LuaMessage::Table(t2));
167    }
168
169    #[test]
170    fn to_lua() {
171        // we only check if they have the correct variant
172        let lua = Lua::new();
173        lua.context(|ctx| {
174            assert_eq!(
175                discriminant(&LuaMessage::Integer(42).to_lua(ctx).unwrap()),
176                discriminant(&Value::Integer(42))
177            );
178            assert_eq!(
179                discriminant(&LuaMessage::String("foo".to_string()).to_lua(ctx).unwrap()),
180                discriminant(&Value::String(ctx.create_string("foo").unwrap()))
181            );
182            assert_eq!(
183                discriminant(&LuaMessage::Number(42.5).to_lua(ctx).unwrap()),
184                discriminant(&Value::Number(42.5))
185            );
186            assert_eq!(
187                discriminant(&LuaMessage::Boolean(true).to_lua(ctx).unwrap()),
188                discriminant(&Value::Boolean(true))
189            );
190            assert_eq!(
191                discriminant(&LuaMessage::Nil.to_lua(ctx).unwrap()),
192                discriminant(&Value::Nil)
193            );
194
195            let mut t = HashMap::new();
196            t.insert("bar".to_string(), LuaMessage::from("abc"));
197            assert_eq!(
198                discriminant(&LuaMessage::Table(t).to_lua(ctx).unwrap()),
199                discriminant(&Value::Table(ctx.create_table().unwrap()))
200            );
201        })
202    }
203
204    #[test]
205    fn from_lua() {
206        // we only check if they have the correct variant
207        let lua = Lua::new();
208        lua.context(|ctx| {
209            assert_eq!(
210                discriminant(&LuaMessage::from_lua(Value::Integer(42), ctx).unwrap()),
211                discriminant(&LuaMessage::Integer(42))
212            );
213            assert_eq!(
214                discriminant(&LuaMessage::from_lua(Value::Number(42.5), ctx).unwrap()),
215                discriminant(&LuaMessage::Number(42.5))
216            );
217            assert_eq!(
218                discriminant(
219                    &LuaMessage::from_lua(Value::String(ctx.create_string("foo").unwrap()), ctx)
220                        .unwrap()
221                ),
222                discriminant(&LuaMessage::String("foo".to_string()))
223            );
224            assert_eq!(
225                discriminant(&LuaMessage::from_lua(Value::Boolean(true), ctx).unwrap()),
226                discriminant(&LuaMessage::Boolean(true))
227            );
228            assert_eq!(
229                discriminant(&LuaMessage::from_lua(Value::Nil, ctx).unwrap()),
230                discriminant(&LuaMessage::Nil)
231            );
232
233            let mut t = HashMap::new();
234            t.insert("bar".to_string(), LuaMessage::from("abc"));
235            assert_eq!(
236                discriminant(
237                    &LuaMessage::from_lua(Value::Table(ctx.create_table().unwrap()), ctx).unwrap()
238                ),
239                discriminant(&LuaMessage::Table(t))
240            );
241        })
242    }
243
244    #[should_panic]
245    #[test]
246    fn from_lua_error() {
247        use rlua::Error;
248
249        let lua = Lua::new();
250        lua.context(|ctx| {
251            &LuaMessage::from_lua(Value::Error(Error::RuntimeError("foo".to_string())), ctx)
252                .unwrap();
253        })
254    }
255}