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 _ => 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 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 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}