1use ordered_hash_map::OrderedHashMap;
4
5use std::ffi::{CStr, CString, IntoStringError};
6use std::fmt::{Debug, Display};
7use std::hash::{Hash, Hasher};
8use std::string::FromUtf8Error;
9
10pub const LUA_FLOAT_MARKER: u8 = 0;
12pub const LUA_STRING_MARKER: u8 = 1;
14pub const LUA_NIL_MARKER: u8 = 2;
16pub const LUA_BOOL_MARKER: u8 = 3;
18pub const LUA_TABLE_MARKER: u8 = 4;
20pub const LUA_END_MARKER: u8 = 5;
22
23pub type LuaTable = OrderedHashMap<LuaObject, LuaObject>;
24
25#[macro_export]
56macro_rules! lua {
57 () => (
58 $crate::lua::LuaObject::Nil
59 );
60 (nil) => (
61 $crate::lua::LuaObject::Nil
62 );
63 ($item:expr) => (
64 $crate::lua::LuaObject::from($item)
65 );
66 (cstr $item:literal) => (
67 $crate::lua::LuaObject::from(std::ffi::CString::new($item).unwrap())
68 );
69 ($($key:ident = $val:expr),* $(,)?) => (
70 $crate::lua::LuaObject::Table(
71 <ordered_hash_map::OrderedHashMap<$crate::lua::LuaObject, $crate::lua::LuaObject> as std::iter::FromIterator<($crate::lua::LuaObject, $crate::lua::LuaObject)>>::from_iter([
72 $(
73 (lua!(stringify!($key)), lua!($val))
74 ),*
75 ])
76 )
77 );
78 ($([$key:expr] = $val:expr),* $(,)?) => (
79 $crate::lua::LuaObject::Table(
80 <ordered_hash_map::OrderedHashMap<$crate::lua::LuaObject, $crate::lua::LuaObject> as std::iter::FromIterator<($crate::lua::LuaObject, $crate::lua::LuaObject)>>::from_iter([
81 $(
82 (lua!($key), lua!($val))
83 ),*
84 ])
85 )
86 );
87}
88
89#[derive(Clone, Debug)]
91pub struct LuaTypeError {}
92
93#[derive(Clone, Debug)]
94pub enum LuaObject {
95 Float(f32),
96 String(CString),
97 Unicode(String),
98 Nil,
99 Bool(bool),
100 Table(LuaTable),
101}
102
103impl From<FromUtf8Error> for LuaTypeError {
104 fn from(_err: FromUtf8Error) -> LuaTypeError {
105 LuaTypeError {}
106 }
107}
108
109impl From<IntoStringError> for LuaTypeError {
110 fn from(_err: IntoStringError) -> LuaTypeError {
111 LuaTypeError {}
112 }
113}
114
115impl From<f32> for LuaObject {
116 fn from(data: f32) -> LuaObject {
117 LuaObject::Float(data)
118 }
119}
120
121impl From<CString> for LuaObject {
122 fn from(data: CString) -> LuaObject {
123 LuaObject::String(data)
124 }
125}
126
127impl From<String> for LuaObject {
128 fn from(data: String) -> LuaObject {
129 LuaObject::Unicode(data)
130 }
131}
132
133impl From<Box<CStr>> for LuaObject {
134 fn from(data: Box<CStr>) -> LuaObject {
135 LuaObject::String(data.into_c_string())
136 }
137}
138
139impl From<&str> for LuaObject {
140 fn from(data: &str) -> LuaObject {
141 LuaObject::Unicode(data.to_string())
142 }
143}
144
145impl From<bool> for LuaObject {
146 fn from(data: bool) -> LuaObject {
147 LuaObject::Bool(data)
148 }
149}
150
151impl Eq for LuaObject {}
152impl PartialEq for LuaObject {
153 fn eq(&self, other: &LuaObject) -> bool {
154 use LuaObject::*;
155
156 match (self, other) {
157 (Float(f1), Float(f2)) => f1.eq(f2),
158 (String(s1), String(s2)) => s1.eq(s2),
160 (Unicode(s1), Unicode(s2)) => s1.eq(s2),
161 (String(s1), Unicode(s2)) => s1.as_bytes().eq(s2.as_bytes()),
162 (Unicode(s1), String(s2)) => s1.as_bytes().eq(s2.as_bytes()),
163 (Nil, Nil) => true,
165 (Bool(b1), Bool(b2)) => b1.eq(b2),
166 (Table(t1), Table(t2)) => t1.eq(t2),
167 _ => false,
168 }
169 }
170}
171
172impl Hash for LuaObject {
173 fn hash<H: Hasher>(&self, state: &mut H) {
174 use LuaObject::*;
175
176 match self {
177 Float(f) => unsafe {
178 let ptr = if f.is_nan() { &std::f32::NAN } else { f } as *const f32;
179 state.write(&*(ptr as *const [u8; 4]))
180 },
181 String(s) => s.hash(state),
182 Unicode(s) => s.hash(state),
183 Nil => ().hash(state),
184 Bool(b) => b.hash(state),
185 Table(_) => panic!("Unhashable type 'table'"),
186 }
187 }
188}
189
190impl Display for LuaObject {
191 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
192 use LuaObject::*;
193
194 match self {
195 Float(f_) => Display::fmt(f_, f),
196 String(s) => write!(f, "{}", s.to_string_lossy()),
197 Unicode(s) => Display::fmt(s, f),
198 Nil => f.write_str("Nil"),
199 Bool(b) => Display::fmt(b, f),
200 Table(ref t) => f
201 .debug_map()
202 .entries(
203 t.iter()
204 .map(|(k, v)| (DebugTableInner(k), DebugTableInner(v))),
205 )
206 .finish(),
207 }
208 }
209}
210
211struct DebugTableInner<'a>(&'a LuaObject);
215impl std::fmt::Debug for DebugTableInner<'_> {
216 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
217 use LuaObject::*;
218
219 match self.0 {
220 String(ref i) => Debug::fmt(i, f),
221 Unicode(ref i) => Debug::fmt(i, f),
222 Bool(ref i) => Debug::fmt(i, f),
223 _ => Display::fmt(self.0, f),
224 }
225 }
226}
227
228impl LuaObject {
229 pub fn evaluate_as_bool(&self) -> bool {
235 use LuaObject::*;
236 match self {
237 Float(f) => *f == 0.0,
238 String(s) => !s.as_bytes().is_empty(),
239 Unicode(s) => !s.is_empty(),
240 Nil => false,
241 Bool(b) => *b,
242 Table(t) => !t.is_empty(),
243 }
244 }
245
246 pub fn as_float(&self) -> Result<f32, LuaTypeError> {
247 match self {
248 LuaObject::Float(f) => Ok(*f),
249 _ => Err(LuaTypeError {}),
250 }
251 }
252
253 pub fn into_string(self) -> Result<String, LuaTypeError> {
254 match self {
255 LuaObject::String(s) => Ok(s.into_string()?),
256 LuaObject::Unicode(s) => Ok(s),
257 _ => Err(LuaTypeError {}),
258 }
259 }
260
261 pub fn to_string(&self) -> Result<String, LuaTypeError> {
262 match self {
263 LuaObject::String(s) => Ok(String::from_utf8(s.as_bytes().to_vec())?),
264 LuaObject::Unicode(s) => Ok(s.clone()),
265 _ => Err(LuaTypeError {}),
266 }
267 }
268
269 pub fn to_string_lossy(&self) -> Result<String, LuaTypeError> {
270 match self {
271 LuaObject::String(s) => Ok(String::from_utf8_lossy(s.as_bytes()).to_string()),
272 LuaObject::Unicode(s) => Ok(s.clone()),
273 _ => Err(LuaTypeError {}),
274 }
275 }
276
277 pub fn as_nil(&self) -> Result<(), LuaTypeError> {
278 match self {
279 LuaObject::Nil => Ok(()),
280 _ => Err(LuaTypeError {}),
281 }
282 }
283
284 pub fn as_bool(&self) -> Result<bool, LuaTypeError> {
285 match self {
286 LuaObject::Bool(b) => Ok(*b),
287 _ => Err(LuaTypeError {}),
288 }
289 }
290
291 pub fn into_hashmap(self) -> Result<LuaTable, LuaTypeError> {
292 match self {
293 LuaObject::Table(t) => Ok(t),
294 _ => Err(LuaTypeError {}),
295 }
296 }
297
298 pub fn as_hashmap(&self) -> Result<&LuaTable, LuaTypeError> {
299 match self {
300 LuaObject::Table(ref t) => Ok(t),
301 _ => Err(LuaTypeError {}),
302 }
303 }
304}
305
306#[cfg(test)]
307mod tests {
308 use super::*;
309 use pretty_assertions::assert_eq;
310
311 use std::iter::FromIterator;
312
313 #[test]
314 fn test_macro_simple() {
315 assert_eq!(lua!(10.), LuaObject::Float(10.));
316 assert_eq!(
317 lua!(cstr "foo"),
318 LuaObject::String(CString::new("foo").unwrap())
319 );
320 assert_eq!(lua!("foo"), LuaObject::Unicode("foo".to_string()));
321 assert_eq!(lua!(), LuaObject::Nil);
322 assert_eq!(lua!(nil), LuaObject::Nil);
323 assert_eq!(lua!(false), LuaObject::Bool(false));
324 assert_eq!(lua!(10.), LuaObject::Float(10.));
325 }
326
327 #[test]
328 fn test_macro_table_sugar() {
329 let table = lua! {
330 foo = 1.0,
331 bar = 2.0 + 3.0
332 };
333
334 let expected = LuaObject::Table(OrderedHashMap::from_iter([
335 (LuaObject::Unicode("foo".to_string()), LuaObject::Float(1.0)),
336 (LuaObject::Unicode("bar".to_string()), LuaObject::Float(5.0)),
337 ]));
338
339 assert_eq!(table, expected);
340 }
341
342 #[test]
343 fn test_macro_table_verbose() {
344 let table = lua! {
345 ["foo"] = "bar",
346 [10.] = true,
347 [false] = 100.
348 };
349
350 let expected = LuaObject::Table(OrderedHashMap::from_iter([
351 (
352 LuaObject::Unicode("foo".to_string()),
353 LuaObject::Unicode("bar".to_string()),
354 ),
355 (LuaObject::Float(10.), LuaObject::Bool(true)),
356 (LuaObject::Bool(false), LuaObject::Float(100.)),
357 ]));
358
359 assert_eq!(table, expected);
360 }
361
362 #[test]
363 fn test_display() {
364 assert_eq!(format!("{}", LuaObject::Float(100.)), "100");
365 assert_eq!(
366 format!(
367 "{}",
368 LuaObject::String(CString::new("Hello World").unwrap())
369 ),
370 "Hello World"
371 );
372 assert_eq!(
373 format!("{}", LuaObject::Unicode("Hello World".into())),
374 "Hello World"
375 );
376 assert_eq!(format!("{}", LuaObject::Nil), "Nil");
377 assert_eq!(format!("{}", LuaObject::Bool(false)), "false");
378 assert_eq!(format!("{}", LuaObject::Bool(true)), "true");
379 assert_eq!(format!("{}", LuaObject::Table(LuaTable::new())), "{}");
380 assert_eq!(format!("{}", LuaObject::Table(LuaTable::new())), "{}");
381 let mut table = LuaTable::new();
383 table.insert(LuaObject::Nil, LuaObject::Float(100.));
384 table.insert(LuaObject::Unicode("Foo Bar".into()), LuaObject::Bool(false));
385 table.insert(
386 LuaObject::String(CString::new("Hello World").unwrap()),
387 LuaObject::Unicode("A new day".into()),
388 );
389 let mut inner_table = LuaTable::new();
390 inner_table.insert(LuaObject::Float(1.), LuaObject::Bool(true));
391 table.insert(LuaObject::Float(2.), LuaObject::Table(inner_table));
392 assert_eq!(
393 format!("{}", LuaObject::Table(table)),
394 "{Nil: 100, \"Foo Bar\": false, \"Hello World\": \"A new day\", 2: {1: true}}"
395 );
396 }
397}