factorio_mlua/
conversion.rs

1#![allow(clippy::wrong_self_convention)]
2
3use std::borrow::Cow;
4use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
5use std::convert::TryInto;
6use std::ffi::{CStr, CString};
7use std::hash::{BuildHasher, Hash};
8use std::string::String as StdString;
9
10use bstr::{BStr, BString};
11use num_traits::cast;
12
13use crate::error::{Error, Result};
14use crate::function::Function;
15use crate::lua::Lua;
16use crate::string::String;
17use crate::table::Table;
18use crate::thread::Thread;
19use crate::types::{LightUserData, MaybeSend};
20use crate::userdata::{AnyUserData, UserData};
21use crate::value::{FromLua, Nil, ToLua, Value};
22
23impl<'lua> ToLua<'lua> for Value<'lua> {
24    #[inline]
25    fn to_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
26        Ok(self)
27    }
28}
29
30impl<'lua> FromLua<'lua> for Value<'lua> {
31    #[inline]
32    fn from_lua(lua_value: Value<'lua>, _: &'lua Lua) -> Result<Self> {
33        Ok(lua_value)
34    }
35}
36
37impl<'lua> ToLua<'lua> for String<'lua> {
38    #[inline]
39    fn to_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
40        Ok(Value::String(self))
41    }
42}
43
44impl<'lua> FromLua<'lua> for String<'lua> {
45    #[inline]
46    fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> Result<String<'lua>> {
47        let ty = value.type_name();
48        lua.coerce_string(value)?
49            .ok_or_else(|| Error::FromLuaConversionError {
50                from: ty,
51                to: "String",
52                message: Some("expected string or number".to_string()),
53            })
54    }
55}
56
57impl<'lua> ToLua<'lua> for Table<'lua> {
58    #[inline]
59    fn to_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
60        Ok(Value::Table(self))
61    }
62}
63
64impl<'lua> FromLua<'lua> for Table<'lua> {
65    #[inline]
66    fn from_lua(value: Value<'lua>, _: &'lua Lua) -> Result<Table<'lua>> {
67        match value {
68            Value::Table(table) => Ok(table),
69            _ => Err(Error::FromLuaConversionError {
70                from: value.type_name(),
71                to: "table",
72                message: None,
73            }),
74        }
75    }
76}
77
78impl<'lua> ToLua<'lua> for Function<'lua> {
79    #[inline]
80    fn to_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
81        Ok(Value::Function(self))
82    }
83}
84
85impl<'lua> FromLua<'lua> for Function<'lua> {
86    #[inline]
87    fn from_lua(value: Value<'lua>, _: &'lua Lua) -> Result<Function<'lua>> {
88        match value {
89            Value::Function(table) => Ok(table),
90            _ => Err(Error::FromLuaConversionError {
91                from: value.type_name(),
92                to: "function",
93                message: None,
94            }),
95        }
96    }
97}
98
99impl<'lua> ToLua<'lua> for Thread<'lua> {
100    #[inline]
101    fn to_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
102        Ok(Value::Thread(self))
103    }
104}
105
106impl<'lua> FromLua<'lua> for Thread<'lua> {
107    #[inline]
108    fn from_lua(value: Value<'lua>, _: &'lua Lua) -> Result<Thread<'lua>> {
109        match value {
110            Value::Thread(t) => Ok(t),
111            _ => Err(Error::FromLuaConversionError {
112                from: value.type_name(),
113                to: "thread",
114                message: None,
115            }),
116        }
117    }
118}
119
120impl<'lua> ToLua<'lua> for AnyUserData<'lua> {
121    #[inline]
122    fn to_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
123        Ok(Value::UserData(self))
124    }
125}
126
127impl<'lua> FromLua<'lua> for AnyUserData<'lua> {
128    #[inline]
129    fn from_lua(value: Value<'lua>, _: &'lua Lua) -> Result<AnyUserData<'lua>> {
130        match value {
131            Value::UserData(ud) => Ok(ud),
132            _ => Err(Error::FromLuaConversionError {
133                from: value.type_name(),
134                to: "userdata",
135                message: None,
136            }),
137        }
138    }
139}
140
141impl<'lua, T: 'static + MaybeSend + UserData> ToLua<'lua> for T {
142    #[inline]
143    fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
144        Ok(Value::UserData(lua.create_userdata(self)?))
145    }
146}
147
148impl<'lua, T: 'static + UserData + Clone> FromLua<'lua> for T {
149    #[inline]
150    fn from_lua(value: Value<'lua>, _: &'lua Lua) -> Result<T> {
151        match value {
152            Value::UserData(ud) => Ok(ud.borrow::<T>()?.clone()),
153            _ => Err(Error::FromLuaConversionError {
154                from: value.type_name(),
155                to: "userdata",
156                message: None,
157            }),
158        }
159    }
160}
161
162impl<'lua> ToLua<'lua> for Error {
163    #[inline]
164    fn to_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
165        Ok(Value::Error(self))
166    }
167}
168
169impl<'lua> FromLua<'lua> for Error {
170    #[inline]
171    fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> Result<Error> {
172        match value {
173            Value::Error(err) => Ok(err),
174            val => Ok(Error::RuntimeError(
175                lua.coerce_string(val)?
176                    .and_then(|s| Some(s.to_str().ok()?.to_owned()))
177                    .unwrap_or_else(|| "<unprintable error>".to_owned()),
178            )),
179        }
180    }
181}
182
183impl<'lua> ToLua<'lua> for bool {
184    #[inline]
185    fn to_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
186        Ok(Value::Boolean(self))
187    }
188}
189
190impl<'lua> FromLua<'lua> for bool {
191    #[inline]
192    fn from_lua(v: Value<'lua>, _: &'lua Lua) -> Result<Self> {
193        match v {
194            Value::Nil => Ok(false),
195            Value::Boolean(b) => Ok(b),
196            _ => Ok(true),
197        }
198    }
199}
200
201impl<'lua> ToLua<'lua> for LightUserData {
202    #[inline]
203    fn to_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
204        Ok(Value::LightUserData(self))
205    }
206}
207
208impl<'lua> FromLua<'lua> for LightUserData {
209    #[inline]
210    fn from_lua(value: Value<'lua>, _: &'lua Lua) -> Result<Self> {
211        match value {
212            Value::LightUserData(ud) => Ok(ud),
213            _ => Err(Error::FromLuaConversionError {
214                from: value.type_name(),
215                to: "light userdata",
216                message: None,
217            }),
218        }
219    }
220}
221
222impl<'lua> ToLua<'lua> for StdString {
223    #[inline]
224    fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
225        Ok(Value::String(lua.create_string(&self)?))
226    }
227}
228
229impl<'lua> FromLua<'lua> for StdString {
230    #[inline]
231    fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> Result<Self> {
232        let ty = value.type_name();
233        Ok(lua
234            .coerce_string(value)?
235            .ok_or_else(|| Error::FromLuaConversionError {
236                from: ty,
237                to: "String",
238                message: Some("expected string or number".to_string()),
239            })?
240            .to_str()?
241            .to_owned())
242    }
243}
244
245impl<'lua> ToLua<'lua> for &str {
246    #[inline]
247    fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
248        Ok(Value::String(lua.create_string(self)?))
249    }
250}
251
252impl<'lua> ToLua<'lua> for Cow<'_, str> {
253    #[inline]
254    fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
255        Ok(Value::String(lua.create_string(self.as_bytes())?))
256    }
257}
258
259impl<'lua> ToLua<'lua> for Box<str> {
260    #[inline]
261    fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
262        Ok(Value::String(lua.create_string(&*self)?))
263    }
264}
265
266impl<'lua> FromLua<'lua> for Box<str> {
267    #[inline]
268    fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> Result<Self> {
269        let ty = value.type_name();
270        Ok(lua
271            .coerce_string(value)?
272            .ok_or_else(|| Error::FromLuaConversionError {
273                from: ty,
274                to: "Box<str>",
275                message: Some("expected string or number".to_string()),
276            })?
277            .to_str()?
278            .to_owned()
279            .into_boxed_str())
280    }
281}
282
283impl<'lua> ToLua<'lua> for CString {
284    #[inline]
285    fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
286        Ok(Value::String(lua.create_string(self.as_bytes())?))
287    }
288}
289
290impl<'lua> FromLua<'lua> for CString {
291    #[inline]
292    fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> Result<Self> {
293        let ty = value.type_name();
294        let string = lua
295            .coerce_string(value)?
296            .ok_or_else(|| Error::FromLuaConversionError {
297                from: ty,
298                to: "CString",
299                message: Some("expected string or number".to_string()),
300            })?;
301
302        match CStr::from_bytes_with_nul(string.as_bytes_with_nul()) {
303            Ok(s) => Ok(s.into()),
304            Err(_) => Err(Error::FromLuaConversionError {
305                from: ty,
306                to: "CString",
307                message: Some("invalid C-style string".to_string()),
308            }),
309        }
310    }
311}
312
313impl<'lua> ToLua<'lua> for &CStr {
314    #[inline]
315    fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
316        Ok(Value::String(lua.create_string(self.to_bytes())?))
317    }
318}
319
320impl<'lua> ToLua<'lua> for Cow<'_, CStr> {
321    #[inline]
322    fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
323        Ok(Value::String(lua.create_string(self.to_bytes())?))
324    }
325}
326
327impl<'lua> ToLua<'lua> for BString {
328    #[inline]
329    fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
330        Ok(Value::String(lua.create_string(&self)?))
331    }
332}
333
334impl<'lua> FromLua<'lua> for BString {
335    #[inline]
336    fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> Result<Self> {
337        let ty = value.type_name();
338        Ok(BString::from(
339            lua.coerce_string(value)?
340                .ok_or_else(|| Error::FromLuaConversionError {
341                    from: ty,
342                    to: "String",
343                    message: Some("expected string or number".to_string()),
344                })?
345                .as_bytes()
346                .to_vec(),
347        ))
348    }
349}
350
351impl<'lua> ToLua<'lua> for &BStr {
352    #[inline]
353    fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
354        Ok(Value::String(lua.create_string(self)?))
355    }
356}
357
358macro_rules! lua_convert_int {
359    ($x:ty) => {
360        impl<'lua> ToLua<'lua> for $x {
361            #[inline]
362            fn to_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
363                cast(self)
364                    .map(Value::Integer)
365                    .or_else(|| cast(self).map(Value::Number))
366                    // This is impossible error because conversion to Number never fails
367                    .ok_or_else(|| Error::ToLuaConversionError {
368                        from: stringify!($x),
369                        to: "number",
370                        message: Some("out of range".to_owned()),
371                    })
372            }
373        }
374
375        impl<'lua> FromLua<'lua> for $x {
376            #[inline]
377            fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> Result<Self> {
378                let ty = value.type_name();
379                (match value {
380                    Value::Integer(i) => cast(i),
381                    Value::Number(n) => cast(n),
382                    _ => {
383                        if let Some(i) = lua.coerce_integer(value.clone())? {
384                            cast(i)
385                        } else {
386                            cast(lua.coerce_number(value)?.ok_or_else(|| {
387                                Error::FromLuaConversionError {
388                                    from: ty,
389                                    to: stringify!($x),
390                                    message: Some(
391                                        "expected number or string coercible to number".to_string(),
392                                    ),
393                                }
394                            })?)
395                        }
396                    }
397                })
398                .ok_or_else(|| Error::FromLuaConversionError {
399                    from: ty,
400                    to: stringify!($x),
401                    message: Some("out of range".to_owned()),
402                })
403            }
404        }
405    };
406}
407
408lua_convert_int!(i8);
409lua_convert_int!(u8);
410lua_convert_int!(i16);
411lua_convert_int!(u16);
412lua_convert_int!(i32);
413lua_convert_int!(u32);
414lua_convert_int!(i64);
415lua_convert_int!(u64);
416lua_convert_int!(i128);
417lua_convert_int!(u128);
418lua_convert_int!(isize);
419lua_convert_int!(usize);
420
421macro_rules! lua_convert_float {
422    ($x:ty) => {
423        impl<'lua> ToLua<'lua> for $x {
424            #[inline]
425            fn to_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
426                cast(self)
427                    .ok_or_else(|| Error::ToLuaConversionError {
428                        from: stringify!($x),
429                        to: "number",
430                        message: Some("out of range".to_string()),
431                    })
432                    .map(Value::Number)
433            }
434        }
435
436        impl<'lua> FromLua<'lua> for $x {
437            #[inline]
438            fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> Result<Self> {
439                let ty = value.type_name();
440                lua.coerce_number(value)?
441                    .ok_or_else(|| Error::FromLuaConversionError {
442                        from: ty,
443                        to: stringify!($x),
444                        message: Some("expected number or string coercible to number".to_string()),
445                    })
446                    .and_then(|n| {
447                        cast(n).ok_or_else(|| Error::FromLuaConversionError {
448                            from: ty,
449                            to: stringify!($x),
450                            message: Some("number out of range".to_string()),
451                        })
452                    })
453            }
454        }
455    };
456}
457
458lua_convert_float!(f32);
459lua_convert_float!(f64);
460
461impl<'lua, T> ToLua<'lua> for &[T]
462where
463    T: Clone + ToLua<'lua>,
464{
465    #[inline]
466    fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
467        Ok(Value::Table(
468            lua.create_sequence_from(self.iter().cloned())?,
469        ))
470    }
471}
472
473impl<'lua, T, const N: usize> ToLua<'lua> for [T; N]
474where
475    T: ToLua<'lua>,
476{
477    #[inline]
478    fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
479        Ok(Value::Table(lua.create_sequence_from(self)?))
480    }
481}
482
483impl<'lua, T, const N: usize> FromLua<'lua> for [T; N]
484where
485    T: FromLua<'lua>,
486{
487    #[inline]
488    fn from_lua(value: Value<'lua>, _lua: &'lua Lua) -> Result<Self> {
489        match value {
490            #[cfg(feature = "luau")]
491            Value::Vector(x, y, z) if N == 3 => Ok(mlua_expect!(
492                vec![
493                    T::from_lua(Value::Number(x as _), _lua)?,
494                    T::from_lua(Value::Number(y as _), _lua)?,
495                    T::from_lua(Value::Number(z as _), _lua)?,
496                ]
497                .try_into()
498                .map_err(|_| ()),
499                "cannot convert vector to array"
500            )),
501            Value::Table(table) => {
502                let vec = table.sequence_values().collect::<Result<Vec<_>>>()?;
503                vec.try_into()
504                    .map_err(|vec: Vec<T>| Error::FromLuaConversionError {
505                        from: "Table",
506                        to: "Array",
507                        message: Some(format!("expected table of length {}, got {}", N, vec.len())),
508                    })
509            }
510            _ => Err(Error::FromLuaConversionError {
511                from: value.type_name(),
512                to: "Array",
513                message: Some("expected table".to_string()),
514            }),
515        }
516    }
517}
518
519impl<'lua, T: ToLua<'lua>> ToLua<'lua> for Box<[T]> {
520    #[inline]
521    fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
522        Ok(Value::Table(lua.create_sequence_from(self.into_vec())?))
523    }
524}
525
526impl<'lua, T: FromLua<'lua>> FromLua<'lua> for Box<[T]> {
527    #[inline]
528    fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> Result<Self> {
529        Ok(Vec::<T>::from_lua(value, lua)?.into_boxed_slice())
530    }
531}
532
533impl<'lua, T: ToLua<'lua>> ToLua<'lua> for Vec<T> {
534    #[inline]
535    fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
536        Ok(Value::Table(lua.create_sequence_from(self)?))
537    }
538}
539
540impl<'lua, T: FromLua<'lua>> FromLua<'lua> for Vec<T> {
541    #[inline]
542    fn from_lua(value: Value<'lua>, _lua: &'lua Lua) -> Result<Self> {
543        match value {
544            #[cfg(feature = "luau")]
545            Value::Vector(x, y, z) => Ok(vec![
546                T::from_lua(Value::Number(x as _), _lua)?,
547                T::from_lua(Value::Number(y as _), _lua)?,
548                T::from_lua(Value::Number(z as _), _lua)?,
549            ]),
550            Value::Table(table) => table.sequence_values().collect(),
551            _ => Err(Error::FromLuaConversionError {
552                from: value.type_name(),
553                to: "Vec",
554                message: Some("expected table".to_string()),
555            }),
556        }
557    }
558}
559
560impl<'lua, K: Eq + Hash + ToLua<'lua>, V: ToLua<'lua>, S: BuildHasher> ToLua<'lua>
561    for HashMap<K, V, S>
562{
563    #[inline]
564    fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
565        Ok(Value::Table(lua.create_table_from(self)?))
566    }
567}
568
569impl<'lua, K: Eq + Hash + FromLua<'lua>, V: FromLua<'lua>, S: BuildHasher + Default> FromLua<'lua>
570    for HashMap<K, V, S>
571{
572    #[inline]
573    fn from_lua(value: Value<'lua>, _: &'lua Lua) -> Result<Self> {
574        if let Value::Table(table) = value {
575            table.pairs().collect()
576        } else {
577            Err(Error::FromLuaConversionError {
578                from: value.type_name(),
579                to: "HashMap",
580                message: Some("expected table".to_string()),
581            })
582        }
583    }
584}
585
586impl<'lua, K: Ord + ToLua<'lua>, V: ToLua<'lua>> ToLua<'lua> for BTreeMap<K, V> {
587    #[inline]
588    fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
589        Ok(Value::Table(lua.create_table_from(self)?))
590    }
591}
592
593impl<'lua, K: Ord + FromLua<'lua>, V: FromLua<'lua>> FromLua<'lua> for BTreeMap<K, V> {
594    #[inline]
595    fn from_lua(value: Value<'lua>, _: &'lua Lua) -> Result<Self> {
596        if let Value::Table(table) = value {
597            table.pairs().collect()
598        } else {
599            Err(Error::FromLuaConversionError {
600                from: value.type_name(),
601                to: "BTreeMap",
602                message: Some("expected table".to_string()),
603            })
604        }
605    }
606}
607
608impl<'lua, T: Eq + Hash + ToLua<'lua>, S: BuildHasher> ToLua<'lua> for HashSet<T, S> {
609    #[inline]
610    fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
611        Ok(Value::Table(lua.create_table_from(
612            self.into_iter().map(|val| (val, true)),
613        )?))
614    }
615}
616
617impl<'lua, T: Eq + Hash + FromLua<'lua>, S: BuildHasher + Default> FromLua<'lua> for HashSet<T, S> {
618    #[inline]
619    fn from_lua(value: Value<'lua>, _: &'lua Lua) -> Result<Self> {
620        match value {
621            Value::Table(table) if table.len()? > 0 => table.sequence_values().collect(),
622            Value::Table(table) => table
623                .pairs::<T, Value<'lua>>()
624                .map(|res| res.map(|(k, _)| k))
625                .collect(),
626            _ => Err(Error::FromLuaConversionError {
627                from: value.type_name(),
628                to: "HashSet",
629                message: Some("expected table".to_string()),
630            }),
631        }
632    }
633}
634
635impl<'lua, T: Ord + ToLua<'lua>> ToLua<'lua> for BTreeSet<T> {
636    #[inline]
637    fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
638        Ok(Value::Table(lua.create_table_from(
639            self.into_iter().map(|val| (val, true)),
640        )?))
641    }
642}
643
644impl<'lua, T: Ord + FromLua<'lua>> FromLua<'lua> for BTreeSet<T> {
645    #[inline]
646    fn from_lua(value: Value<'lua>, _: &'lua Lua) -> Result<Self> {
647        match value {
648            Value::Table(table) if table.len()? > 0 => table.sequence_values().collect(),
649            Value::Table(table) => table
650                .pairs::<T, Value<'lua>>()
651                .map(|res| res.map(|(k, _)| k))
652                .collect(),
653            _ => Err(Error::FromLuaConversionError {
654                from: value.type_name(),
655                to: "BTreeSet",
656                message: Some("expected table".to_string()),
657            }),
658        }
659    }
660}
661
662impl<'lua, T: ToLua<'lua>> ToLua<'lua> for Option<T> {
663    #[inline]
664    fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
665        match self {
666            Some(val) => val.to_lua(lua),
667            None => Ok(Nil),
668        }
669    }
670}
671
672impl<'lua, T: FromLua<'lua>> FromLua<'lua> for Option<T> {
673    #[inline]
674    fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> Result<Self> {
675        match value {
676            Nil => Ok(None),
677            value => Ok(Some(T::from_lua(value, lua)?)),
678        }
679    }
680}