Skip to main content

rilua/
conversion.rs

1//! Type conversion traits for moving values between Rust and Lua.
2//!
3//! Provides `IntoLua` / `FromLua` for single-value conversions and
4//! `IntoLuaMulti` / `FromLuaMulti` for multi-value (vararg/return)
5//! conversions. Standard implementations cover Rust primitives,
6//! strings, `Option<T>`, and tuple arities up to 8.
7
8use crate::Lua;
9use crate::error::{LuaError, LuaResult, RuntimeError};
10use crate::handles::{AnyUserData, Function, Table, Thread};
11use crate::vm::value::Val;
12
13// ---------------------------------------------------------------------------
14// Single-value traits
15// ---------------------------------------------------------------------------
16
17/// Converts a Rust value into a Lua `Val`.
18///
19/// Takes `&mut Lua` because operations like string interning require
20/// mutable access to the GC.
21pub trait IntoLua {
22    /// Performs the conversion.
23    fn into_lua(self, lua: &mut Lua) -> LuaResult<Val>;
24}
25
26/// Extracts a Rust value from a Lua `Val`.
27///
28/// Takes `&Lua` (immutable) because reading does not mutate state.
29pub trait FromLua: Sized {
30    /// Performs the conversion.
31    fn from_lua(val: Val, lua: &Lua) -> LuaResult<Self>;
32}
33
34// ---------------------------------------------------------------------------
35// Multi-value traits
36// ---------------------------------------------------------------------------
37
38/// Converts a Rust value into multiple Lua values.
39pub trait IntoLuaMulti {
40    /// Performs the conversion.
41    fn into_lua_multi(self, lua: &mut Lua) -> LuaResult<Vec<Val>>;
42}
43
44/// Extracts a Rust value from multiple Lua values.
45pub trait FromLuaMulti: Sized {
46    /// Performs the conversion.
47    fn from_lua_multi(values: &[Val], lua: &Lua) -> LuaResult<Self>;
48}
49
50// ---------------------------------------------------------------------------
51// Val passthrough
52// ---------------------------------------------------------------------------
53
54impl IntoLua for Val {
55    fn into_lua(self, _lua: &mut Lua) -> LuaResult<Val> {
56        Ok(self)
57    }
58}
59
60impl FromLua for Val {
61    fn from_lua(val: Val, _lua: &Lua) -> LuaResult<Self> {
62        Ok(val)
63    }
64}
65
66// ---------------------------------------------------------------------------
67// () <-> Nil
68// ---------------------------------------------------------------------------
69
70impl IntoLua for () {
71    fn into_lua(self, _lua: &mut Lua) -> LuaResult<Val> {
72        Ok(Val::Nil)
73    }
74}
75
76impl FromLua for () {
77    fn from_lua(_val: Val, _lua: &Lua) -> LuaResult<Self> {
78        Ok(())
79    }
80}
81
82// ---------------------------------------------------------------------------
83// bool <-> Bool
84// ---------------------------------------------------------------------------
85
86impl IntoLua for bool {
87    fn into_lua(self, _lua: &mut Lua) -> LuaResult<Val> {
88        Ok(Val::Bool(self))
89    }
90}
91
92impl FromLua for bool {
93    fn from_lua(val: Val, _lua: &Lua) -> LuaResult<Self> {
94        match val {
95            Val::Bool(b) => Ok(b),
96            Val::Nil => Ok(false),
97            _ => Ok(true),
98        }
99    }
100}
101
102// ---------------------------------------------------------------------------
103// f64 / f32 <-> Num
104// ---------------------------------------------------------------------------
105
106impl IntoLua for f64 {
107    fn into_lua(self, _lua: &mut Lua) -> LuaResult<Val> {
108        Ok(Val::Num(self))
109    }
110}
111
112impl FromLua for f64 {
113    fn from_lua(val: Val, _lua: &Lua) -> LuaResult<Self> {
114        match val {
115            Val::Num(n) => Ok(n),
116            _ => Err(conversion_error("number", val.type_name())),
117        }
118    }
119}
120
121impl IntoLua for f32 {
122    fn into_lua(self, _lua: &mut Lua) -> LuaResult<Val> {
123        Ok(Val::Num(f64::from(self)))
124    }
125}
126
127impl FromLua for f32 {
128    fn from_lua(val: Val, _lua: &Lua) -> LuaResult<Self> {
129        match val {
130            Val::Num(n) => Ok(n as Self),
131            _ => Err(conversion_error("number", val.type_name())),
132        }
133    }
134}
135
136// ---------------------------------------------------------------------------
137// Integer types <-> Num (with range checks on FromLua)
138// ---------------------------------------------------------------------------
139
140macro_rules! impl_integer_into_lua {
141    ($($ty:ty),*) => {
142        $(
143            impl IntoLua for $ty {
144                #[allow(clippy::cast_lossless, trivial_numeric_casts)]
145                fn into_lua(self, _lua: &mut Lua) -> LuaResult<Val> {
146                    Ok(Val::Num(self as f64))
147                }
148            }
149        )*
150    };
151}
152
153impl_integer_into_lua!(i8, i16, i32, i64, u8, u16, u32, u64, isize, usize);
154
155macro_rules! impl_integer_from_lua {
156    ($($ty:ty),*) => {
157        $(
158            impl FromLua for $ty {
159                #[allow(clippy::cast_lossless, trivial_numeric_casts)]
160                fn from_lua(val: Val, _lua: &Lua) -> LuaResult<Self> {
161                    match val {
162                        Val::Num(n) => {
163                            if n.fract() != 0.0 {
164                                return Err(conversion_error(
165                                    concat!("integer (", stringify!($ty), ")"),
166                                    "float",
167                                ));
168                            }
169                            let min = <$ty>::MIN as f64;
170                            let max = <$ty>::MAX as f64;
171                            if n < min || n > max {
172                                return Err(conversion_error(
173                                    concat!("integer (", stringify!($ty), ")"),
174                                    "number out of range",
175                                ));
176                            }
177                            Ok(n as $ty)
178                        }
179                        _ => Err(conversion_error(
180                            concat!("integer (", stringify!($ty), ")"),
181                            val.type_name(),
182                        )),
183                    }
184                }
185            }
186        )*
187    };
188}
189
190impl_integer_from_lua!(i8, i16, i32, i64, u8, u16, u32, u64, isize, usize);
191
192// ---------------------------------------------------------------------------
193// String <-> Str
194// ---------------------------------------------------------------------------
195
196impl IntoLua for String {
197    fn into_lua(self, lua: &mut Lua) -> LuaResult<Val> {
198        let r = lua.state_mut().gc.intern_string(self.as_bytes());
199        Ok(Val::Str(r))
200    }
201}
202
203impl IntoLua for &str {
204    fn into_lua(self, lua: &mut Lua) -> LuaResult<Val> {
205        let r = lua.state_mut().gc.intern_string(self.as_bytes());
206        Ok(Val::Str(r))
207    }
208}
209
210impl IntoLua for &[u8] {
211    fn into_lua(self, lua: &mut Lua) -> LuaResult<Val> {
212        let r = lua.state_mut().gc.intern_string(self);
213        Ok(Val::Str(r))
214    }
215}
216
217impl FromLua for String {
218    fn from_lua(val: Val, lua: &Lua) -> LuaResult<Self> {
219        match val {
220            Val::Str(r) => {
221                let s = lua
222                    .state()
223                    .gc
224                    .string_arena
225                    .get(r)
226                    .ok_or_else(|| conversion_error("string", "collected string"))?;
227                Ok(Self::from_utf8_lossy(s.data()).into_owned())
228            }
229            _ => Err(conversion_error("string", val.type_name())),
230        }
231    }
232}
233
234impl FromLua for Vec<u8> {
235    fn from_lua(val: Val, lua: &Lua) -> LuaResult<Self> {
236        match val {
237            Val::Str(r) => {
238                let s = lua
239                    .state()
240                    .gc
241                    .string_arena
242                    .get(r)
243                    .ok_or_else(|| conversion_error("string", "collected string"))?;
244                Ok(s.data().to_vec())
245            }
246            _ => Err(conversion_error("string", val.type_name())),
247        }
248    }
249}
250
251// ---------------------------------------------------------------------------
252// Option<T> <-> nil / value
253// ---------------------------------------------------------------------------
254
255impl<T: IntoLua> IntoLua for Option<T> {
256    fn into_lua(self, lua: &mut Lua) -> LuaResult<Val> {
257        match self {
258            Some(v) => v.into_lua(lua),
259            None => Ok(Val::Nil),
260        }
261    }
262}
263
264impl<T: FromLua> FromLua for Option<T> {
265    fn from_lua(val: Val, lua: &Lua) -> LuaResult<Self> {
266        match val {
267            Val::Nil => Ok(None),
268            other => Ok(Some(T::from_lua(other, lua)?)),
269        }
270    }
271}
272
273// ---------------------------------------------------------------------------
274// Handle types <-> Val
275// ---------------------------------------------------------------------------
276
277impl IntoLua for Table {
278    fn into_lua(self, _lua: &mut Lua) -> LuaResult<Val> {
279        Ok(Val::Table(self.gc_ref()))
280    }
281}
282
283impl FromLua for Table {
284    fn from_lua(val: Val, _lua: &Lua) -> LuaResult<Self> {
285        match val {
286            Val::Table(r) => Ok(Self(r)),
287            _ => Err(conversion_error("table", val.type_name())),
288        }
289    }
290}
291
292impl IntoLua for Function {
293    fn into_lua(self, _lua: &mut Lua) -> LuaResult<Val> {
294        Ok(Val::Function(self.gc_ref()))
295    }
296}
297
298impl FromLua for Function {
299    fn from_lua(val: Val, _lua: &Lua) -> LuaResult<Self> {
300        match val {
301            Val::Function(r) => Ok(Self(r)),
302            _ => Err(conversion_error("function", val.type_name())),
303        }
304    }
305}
306
307impl IntoLua for Thread {
308    fn into_lua(self, _lua: &mut Lua) -> LuaResult<Val> {
309        Ok(Val::Thread(self.gc_ref()))
310    }
311}
312
313impl FromLua for Thread {
314    fn from_lua(val: Val, _lua: &Lua) -> LuaResult<Self> {
315        match val {
316            Val::Thread(r) => Ok(Self(r)),
317            _ => Err(conversion_error("thread", val.type_name())),
318        }
319    }
320}
321
322impl IntoLua for AnyUserData {
323    fn into_lua(self, _lua: &mut Lua) -> LuaResult<Val> {
324        Ok(Val::Userdata(self.gc_ref()))
325    }
326}
327
328impl FromLua for AnyUserData {
329    fn from_lua(val: Val, _lua: &Lua) -> LuaResult<Self> {
330        match val {
331            Val::Userdata(r) => Ok(Self(r)),
332            _ => Err(conversion_error("userdata", val.type_name())),
333        }
334    }
335}
336
337// ---------------------------------------------------------------------------
338// Vec<Val> passthrough for Multi traits
339// ---------------------------------------------------------------------------
340
341impl IntoLuaMulti for Vec<Val> {
342    fn into_lua_multi(self, _lua: &mut Lua) -> LuaResult<Vec<Val>> {
343        Ok(self)
344    }
345}
346
347impl FromLuaMulti for Vec<Val> {
348    fn from_lua_multi(values: &[Val], _lua: &Lua) -> LuaResult<Self> {
349        Ok(values.to_vec())
350    }
351}
352
353// ---------------------------------------------------------------------------
354// () -> empty multi
355// ---------------------------------------------------------------------------
356
357impl IntoLuaMulti for () {
358    fn into_lua_multi(self, _lua: &mut Lua) -> LuaResult<Vec<Val>> {
359        Ok(vec![])
360    }
361}
362
363impl FromLuaMulti for () {
364    fn from_lua_multi(_values: &[Val], _lua: &Lua) -> LuaResult<Self> {
365        Ok(())
366    }
367}
368
369// ---------------------------------------------------------------------------
370// Tuple impls for IntoLuaMulti / FromLuaMulti (arity 1..=8)
371// ---------------------------------------------------------------------------
372
373macro_rules! impl_tuple_multi {
374    ($($idx:tt : $T:ident),+) => {
375        impl<$($T: IntoLua),+> IntoLuaMulti for ($($T,)+) {
376            fn into_lua_multi(self, lua: &mut Lua) -> LuaResult<Vec<Val>> {
377                Ok(vec![
378                    $(self.$idx.into_lua(lua)?,)+
379                ])
380            }
381        }
382
383        impl<$($T: FromLua),+> FromLuaMulti for ($($T,)+) {
384            fn from_lua_multi(values: &[Val], lua: &Lua) -> LuaResult<Self> {
385                Ok((
386                    $(
387                        $T::from_lua(
388                            values.get($idx).copied().unwrap_or(Val::Nil),
389                            lua,
390                        )?,
391                    )+
392                ))
393            }
394        }
395    };
396}
397
398impl_tuple_multi!(0: A);
399impl_tuple_multi!(0: A, 1: B);
400impl_tuple_multi!(0: A, 1: B, 2: C);
401impl_tuple_multi!(0: A, 1: B, 2: C, 3: D);
402impl_tuple_multi!(0: A, 1: B, 2: C, 3: D, 4: E);
403impl_tuple_multi!(0: A, 1: B, 2: C, 3: D, 4: E, 5: F);
404impl_tuple_multi!(0: A, 1: B, 2: C, 3: D, 4: E, 5: F, 6: G);
405impl_tuple_multi!(0: A, 1: B, 2: C, 3: D, 4: E, 5: F, 6: G, 7: H);
406
407// ---------------------------------------------------------------------------
408// Helpers
409// ---------------------------------------------------------------------------
410
411fn conversion_error(expected: &str, got: &str) -> LuaError {
412    LuaError::Runtime(RuntimeError {
413        message: format!("{expected} expected, got {got}"),
414        level: 0,
415        traceback: vec![],
416    })
417}
418
419// ---------------------------------------------------------------------------
420// Tests
421// ---------------------------------------------------------------------------
422
423#[cfg(test)]
424mod tests {
425    use super::*;
426
427    fn make_lua() -> Lua {
428        Lua::new_empty()
429    }
430
431    // --- Val passthrough ---
432
433    #[test]
434    fn val_round_trip() {
435        let mut lua = make_lua();
436        let v = Val::Num(7.5);
437        let converted = v.into_lua(&mut lua);
438        assert!(converted.is_ok());
439        assert_eq!(converted.ok(), Some(Val::Num(7.5)));
440
441        let back = Val::from_lua(Val::Num(7.5), &lua);
442        assert!(back.is_ok());
443        assert_eq!(back.ok(), Some(Val::Num(7.5)));
444    }
445
446    // --- () <-> Nil ---
447
448    #[test]
449    fn unit_to_nil() {
450        let mut lua = make_lua();
451        let v = ().into_lua(&mut lua);
452        assert_eq!(v.ok(), Some(Val::Nil));
453    }
454
455    #[test]
456    fn unit_from_anything() {
457        let lua = make_lua();
458        let v = <()>::from_lua(Val::Num(42.0), &lua);
459        assert!(v.is_ok());
460    }
461
462    // --- bool ---
463
464    #[test]
465    fn bool_round_trip() {
466        let mut lua = make_lua();
467        let v = true.into_lua(&mut lua);
468        assert_eq!(v.ok(), Some(Val::Bool(true)));
469
470        let back = bool::from_lua(Val::Bool(true), &lua);
471        assert_eq!(back.ok(), Some(true));
472    }
473
474    #[test]
475    fn bool_from_nil_is_false() {
476        let lua = make_lua();
477        let v = bool::from_lua(Val::Nil, &lua);
478        assert_eq!(v.ok(), Some(false));
479    }
480
481    #[test]
482    fn bool_from_number_is_true() {
483        let lua = make_lua();
484        let v = bool::from_lua(Val::Num(0.0), &lua);
485        assert_eq!(v.ok(), Some(true));
486    }
487
488    // --- f64 / f32 ---
489
490    #[test]
491    fn f64_round_trip() {
492        let mut lua = make_lua();
493        let v = 9.75f64.into_lua(&mut lua);
494        assert_eq!(v.ok(), Some(Val::Num(9.75)));
495
496        let back = f64::from_lua(Val::Num(9.75), &lua);
497        assert_eq!(back.ok(), Some(9.75));
498    }
499
500    #[test]
501    fn f64_from_string_fails() {
502        let mut lua = make_lua();
503        let r = lua.state_mut().gc.intern_string(b"hello");
504        let v = f64::from_lua(Val::Str(r), &lua);
505        assert!(v.is_err());
506    }
507
508    // --- Integer types ---
509
510    #[test]
511    fn i32_round_trip() {
512        let mut lua = make_lua();
513        let v = 42i32.into_lua(&mut lua);
514        assert_eq!(v.ok(), Some(Val::Num(42.0)));
515
516        let back = i32::from_lua(Val::Num(42.0), &lua);
517        assert_eq!(back.ok(), Some(42));
518    }
519
520    #[test]
521    fn i32_from_float_fails() {
522        let lua = make_lua();
523        let v = i32::from_lua(Val::Num(5.75), &lua);
524        assert!(v.is_err());
525    }
526
527    #[test]
528    fn u8_overflow_fails() {
529        let lua = make_lua();
530        let v = u8::from_lua(Val::Num(256.0), &lua);
531        assert!(v.is_err());
532    }
533
534    #[test]
535    fn u8_negative_fails() {
536        let lua = make_lua();
537        let v = u8::from_lua(Val::Num(-1.0), &lua);
538        assert!(v.is_err());
539    }
540
541    // --- String ---
542
543    #[test]
544    fn string_round_trip() {
545        let mut lua = make_lua();
546        let val = "hello".into_lua(&mut lua).ok().unwrap_or(Val::Nil);
547        assert!(matches!(val, Val::Str(_)));
548
549        let back = String::from_lua(val, &lua);
550        assert_eq!(back.ok(), Some("hello".to_string()));
551    }
552
553    #[test]
554    fn string_owned_round_trip() {
555        let mut lua = make_lua();
556        let v = String::from("world").into_lua(&mut lua);
557        assert!(v.is_ok());
558    }
559
560    #[test]
561    fn bytes_round_trip() {
562        let mut lua = make_lua();
563        let val = b"binary\x00data"
564            .as_slice()
565            .into_lua(&mut lua)
566            .ok()
567            .unwrap_or(Val::Nil);
568        assert!(matches!(val, Val::Str(_)));
569
570        let back = Vec::<u8>::from_lua(val, &lua);
571        assert_eq!(back.ok(), Some(b"binary\x00data".to_vec()));
572    }
573
574    // --- Option ---
575
576    #[test]
577    fn option_some() {
578        let mut lua = make_lua();
579        let v = Some(42.0f64).into_lua(&mut lua);
580        assert_eq!(v.ok(), Some(Val::Num(42.0)));
581
582        let back = Option::<f64>::from_lua(Val::Num(42.0), &lua);
583        assert_eq!(back.ok(), Some(Some(42.0)));
584    }
585
586    #[test]
587    fn option_none() {
588        let mut lua = make_lua();
589        let v = Option::<f64>::None.into_lua(&mut lua);
590        assert_eq!(v.ok(), Some(Val::Nil));
591
592        let back = Option::<f64>::from_lua(Val::Nil, &lua);
593        assert_eq!(back.ok(), Some(None));
594    }
595
596    // --- AnyUserData ---
597
598    #[test]
599    fn anyuserdata_into_from_lua() {
600        let mut lua = make_lua();
601        let ud = crate::vm::value::Userdata::new(Box::new(123i64));
602        let r = lua.state_mut().gc.alloc_userdata(ud);
603        let handle = AnyUserData(r);
604
605        let val = handle.into_lua(&mut lua).ok().unwrap_or(Val::Nil);
606        assert!(matches!(val, Val::Userdata(_)));
607
608        let back = AnyUserData::from_lua(val, &lua);
609        assert!(back.is_ok());
610        assert_eq!(back.ok().map(AnyUserData::gc_ref), Some(r));
611    }
612
613    #[test]
614    fn anyuserdata_from_wrong_type() {
615        let lua = make_lua();
616        let result = AnyUserData::from_lua(Val::Num(42.0), &lua);
617        assert!(result.is_err());
618    }
619
620    // --- Handle types ---
621
622    #[test]
623    fn table_handle_conversion() {
624        let mut lua = make_lua();
625        let t = lua.create_table();
626        let val = t.into_lua(&mut lua).ok().unwrap_or(Val::Nil);
627        assert!(matches!(val, Val::Table(_)));
628
629        let back = Table::from_lua(val, &lua);
630        assert!(back.is_ok());
631        assert_eq!(back.ok().map(Table::gc_ref), Some(t.gc_ref()));
632    }
633
634    // --- Multi ---
635
636    #[test]
637    fn vec_val_multi_round_trip() {
638        let mut lua = make_lua();
639        let vals = vec![Val::Num(1.0), Val::Num(2.0)];
640        let multi = vals.into_lua_multi(&mut lua);
641        assert_eq!(multi.ok().map(|v| v.len()), Some(2));
642    }
643
644    #[test]
645    fn unit_multi() {
646        let mut lua = make_lua();
647        let multi = ().into_lua_multi(&mut lua);
648        assert_eq!(multi.ok().map(|v| v.len()), Some(0));
649    }
650
651    #[test]
652    fn tuple_from_multi() {
653        let lua = make_lua();
654        let vals = [Val::Num(1.0), Val::Num(2.0)];
655        let result = <(f64, f64)>::from_lua_multi(&vals, &lua);
656        assert_eq!(result.ok(), Some((1.0, 2.0)));
657    }
658
659    #[test]
660    fn tuple_from_multi_missing_values() {
661        let lua = make_lua();
662        let vals = [Val::Num(1.0)];
663        let result = <(f64, Option<f64>)>::from_lua_multi(&vals, &lua);
664        assert_eq!(result.ok(), Some((1.0, None)));
665    }
666}