Skip to main content

luaur_rt/
conversion.rs

1//! `FromLua` / `IntoLua` / `FromLuaMulti` / `IntoLuaMulti` impls for the common
2//! Rust types. Mirrors the impls in `mlua::conversion`.
3
4use crate::error::{Error, Result};
5use crate::function::Function;
6use crate::multi::{MultiValue, Variadic};
7use crate::state::Lua;
8use crate::string::LuaString;
9use crate::table::Table;
10use crate::traits::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti};
11use crate::value::{Integer, Number, Value};
12
13// ---------------------------------------------------------------------------
14// Value itself
15// ---------------------------------------------------------------------------
16
17impl IntoLua for Value {
18    fn into_lua(self, _lua: &Lua) -> Result<Value> {
19        Ok(self)
20    }
21}
22
23impl FromLua for Value {
24    fn from_lua(value: Value, _lua: &Lua) -> Result<Self> {
25        Ok(value)
26    }
27}
28
29// ---------------------------------------------------------------------------
30// Unit / nil
31// ---------------------------------------------------------------------------
32//
33// NOTE: `()` is deliberately NOT a single-value (`IntoLua`/`FromLua`) type.
34// In Lua, `()` means *zero* values, not one nil — so it implements only the
35// multi-value traits below (producing/consuming no stack values). This also
36// avoids a coherence clash with the blanket `impl<T: IntoLua> IntoLuaMulti`.
37
38// `()` as a *multi* value means "no values" in both directions.
39impl IntoLuaMulti for () {
40    fn into_lua_multi(self, _lua: &Lua) -> Result<MultiValue> {
41        Ok(MultiValue::new())
42    }
43}
44
45impl FromLuaMulti for () {
46    fn from_lua_multi(_values: MultiValue, _lua: &Lua) -> Result<Self> {
47        Ok(())
48    }
49}
50
51// ---------------------------------------------------------------------------
52// bool
53// ---------------------------------------------------------------------------
54
55impl IntoLua for bool {
56    fn into_lua(self, _lua: &Lua) -> Result<Value> {
57        Ok(Value::Boolean(self))
58    }
59}
60
61impl FromLua for bool {
62    fn from_lua(value: Value, _lua: &Lua) -> Result<Self> {
63        // Lua truthiness: nil and false are false, everything else is true.
64        Ok(match value {
65            Value::Nil => false,
66            Value::Boolean(b) => b,
67            _ => true,
68        })
69    }
70}
71
72// ---------------------------------------------------------------------------
73// Integers (range-checked)
74// ---------------------------------------------------------------------------
75
76macro_rules! impl_integer {
77    ($($ty:ty),*) => {$(
78        impl IntoLua for $ty {
79            fn into_lua(self, _lua: &Lua) -> Result<Value> {
80                let as_i64 = i64::try_from(self).map_err(|_| Error::ToLuaConversionError {
81                    from: stringify!($ty),
82                    to: "integer",
83                    message: Some("value out of i64 range".to_string()),
84                })?;
85                Ok(Value::Integer(as_i64))
86            }
87        }
88
89        impl FromLua for $ty {
90            fn from_lua(value: Value, _lua: &Lua) -> Result<Self> {
91                // Convert an (already truncated, finite) float directly into the
92                // target type with a range check against its f64 bounds, so a
93                // value beyond `i64` range (e.g. `2^64` for `u64`) is correctly
94                // rejected rather than silently saturating through `i64`.
95                fn from_float(f: f64) -> Result<$ty> {
96                    // Truncate toward zero, then range-check in `i128` space so a
97                    // value beyond the target's range (e.g. `2^64` for `u64`) is
98                    // rejected rather than saturating. Values whose magnitude
99                    // exceeds `i128` are likewise out of range for any `$ty`.
100                    let t = f.trunc();
101                    if t < (i128::MIN as f64) || t > (i128::MAX as f64) {
102                        return Err(Error::FromLuaConversionError {
103                            from: "number",
104                            to: stringify!($ty).to_string(),
105                            message: Some("out of range".to_string()),
106                        });
107                    }
108                    let wide = t as i128;
109                    <$ty>::try_from(wide).map_err(|_| Error::FromLuaConversionError {
110                        from: "number",
111                        to: stringify!($ty).to_string(),
112                        message: Some("out of range".to_string()),
113                    })
114                }
115                match value {
116                    Value::Integer(i) => {
117                        <$ty>::try_from(i).map_err(|_| Error::FromLuaConversionError {
118                            from: "number",
119                            to: stringify!($ty).to_string(),
120                            message: Some("out of range".to_string()),
121                        })
122                    }
123                    Value::Number(f) => {
124                        // Luau is f64-backed: `tonumber`/`lua_tointegerx`
125                        // **truncate** a fractional float toward zero rather than
126                        // rejecting it (matching mlua on the Luau backend). A
127                        // non-finite number has no integer representation.
128                        if !f.is_finite() {
129                            return Err(Error::FromLuaConversionError {
130                                from: "number",
131                                to: stringify!($ty).to_string(),
132                                message: Some("number has no integer representation".to_string()),
133                            });
134                        }
135                        from_float(f)
136                    }
137                    // Lua coerces numeric strings to numbers (mirrors mlua's
138                    // string fallback for integer conversions).
139                    Value::String(ref s) => {
140                        let text = s.to_string_lossy();
141                        let trimmed = text.trim();
142                        if let Ok(i) = trimmed.parse::<i64>() {
143                            <$ty>::try_from(i).map_err(|_| Error::FromLuaConversionError {
144                                from: "string",
145                                to: stringify!($ty).to_string(),
146                                message: Some("out of range".to_string()),
147                            })
148                        } else if let Ok(f) = trimmed.parse::<f64>() {
149                            if !f.is_finite() {
150                                return Err(Error::FromLuaConversionError {
151                                    from: "string",
152                                    to: stringify!($ty).to_string(),
153                                    message: Some("number has no integer representation".to_string()),
154                                });
155                            }
156                            from_float(f)
157                        } else {
158                            Err(Error::FromLuaConversionError {
159                                from: "string",
160                                to: stringify!($ty).to_string(),
161                                message: Some("not a number".to_string()),
162                            })
163                        }
164                    }
165                    other => {
166                        Err(Error::FromLuaConversionError {
167                            from: other.type_name(),
168                            to: stringify!($ty).to_string(),
169                            message: None,
170                        })
171                    }
172                }
173            }
174        }
175    )*};
176}
177
178impl_integer!(i8, u8, i16, u16, i32, u32, i64, u64, isize, usize);
179
180// 128-bit integers exceed Luau's f64-backed number range, so they round-trip
181// **lossily** through `f64` (matching mlua, which has no 128-bit Lua number).
182// Values within f64's exactly-representable range survive the round trip.
183macro_rules! impl_integer_128 {
184    ($($ty:ty),*) => {$(
185        impl IntoLua for $ty {
186            fn into_lua(self, _lua: &Lua) -> Result<Value> {
187                Ok(Value::Number(self as f64))
188            }
189        }
190
191        impl FromLua for $ty {
192            fn from_lua(value: Value, _lua: &Lua) -> Result<Self> {
193                let n: f64 = match value {
194                    Value::Integer(i) => i as f64,
195                    Value::Number(f) => f,
196                    Value::String(ref s) => {
197                        s.to_string_lossy().trim().parse::<f64>().map_err(|_| {
198                            Error::FromLuaConversionError {
199                                from: "string",
200                                to: stringify!($ty).to_string(),
201                                message: Some("not a number".to_string()),
202                            }
203                        })?
204                    }
205                    other => {
206                        return Err(Error::FromLuaConversionError {
207                            from: other.type_name(),
208                            to: stringify!($ty).to_string(),
209                            message: None,
210                        });
211                    }
212                };
213                if n.fract() != 0.0 || !n.is_finite() {
214                    return Err(Error::FromLuaConversionError {
215                        from: "number",
216                        to: stringify!($ty).to_string(),
217                        message: Some("number has no integer representation".to_string()),
218                    });
219                }
220                Ok(n as $ty)
221            }
222        }
223    )*};
224}
225
226impl_integer_128!(i128, u128);
227
228// `Integer` is `i64`, already covered by impl_integer.
229const _: () = {
230    // Compile-time assertion that Integer == i64.
231    fn _assert(_x: Integer) -> i64 {
232        _x
233    }
234};
235
236// ---------------------------------------------------------------------------
237// Floats
238// ---------------------------------------------------------------------------
239
240impl IntoLua for f64 {
241    fn into_lua(self, _lua: &Lua) -> Result<Value> {
242        Ok(Value::Number(self))
243    }
244}
245
246impl FromLua for f64 {
247    fn from_lua(value: Value, _lua: &Lua) -> Result<Self> {
248        match value {
249            Value::Number(n) => Ok(n),
250            Value::Integer(i) => Ok(i as f64),
251            // Lua coerces numeric strings to numbers (mirrors mlua).
252            Value::String(ref s) => {
253                let text = s.to_string_lossy();
254                text.trim()
255                    .parse::<f64>()
256                    .map_err(|_| Error::FromLuaConversionError {
257                        from: "string",
258                        to: "f64".to_string(),
259                        message: Some("not a number".to_string()),
260                    })
261            }
262            other => Err(Error::FromLuaConversionError {
263                from: other.type_name(),
264                to: "f64".to_string(),
265                message: None,
266            }),
267        }
268    }
269}
270
271impl IntoLua for f32 {
272    fn into_lua(self, _lua: &Lua) -> Result<Value> {
273        Ok(Value::Number(self as Number))
274    }
275}
276
277impl FromLua for f32 {
278    fn from_lua(value: Value, lua: &Lua) -> Result<Self> {
279        Ok(f64::from_lua(value, lua)? as f32)
280    }
281}
282
283// ---------------------------------------------------------------------------
284// Strings
285// ---------------------------------------------------------------------------
286
287impl IntoLua for String {
288    fn into_lua(self, lua: &Lua) -> Result<Value> {
289        Ok(Value::String(lua.create_string(&self)))
290    }
291}
292
293impl IntoLua for &str {
294    fn into_lua(self, lua: &Lua) -> Result<Value> {
295        Ok(Value::String(lua.create_string(self)))
296    }
297}
298
299impl IntoLua for &String {
300    fn into_lua(self, lua: &Lua) -> Result<Value> {
301        Ok(Value::String(lua.create_string(self)))
302    }
303}
304
305impl FromLua for String {
306    fn from_lua(value: Value, _lua: &Lua) -> Result<Self> {
307        match value {
308            Value::String(s) => s.to_str(),
309            // Lua coerces numbers to strings in many contexts; mirror that.
310            Value::Integer(i) => Ok(i.to_string()),
311            Value::Number(n) => Ok(n.to_string()),
312            other => Err(Error::FromLuaConversionError {
313                from: other.type_name(),
314                to: "String".to_string(),
315                message: None,
316            }),
317        }
318    }
319}
320
321impl IntoLua for LuaString {
322    fn into_lua(self, _lua: &Lua) -> Result<Value> {
323        Ok(Value::String(self))
324    }
325}
326
327impl IntoLua for &LuaString {
328    fn into_lua(self, _lua: &Lua) -> Result<Value> {
329        Ok(Value::String(self.clone()))
330    }
331}
332
333impl FromLua for LuaString {
334    fn from_lua(value: Value, _lua: &Lua) -> Result<Self> {
335        match value {
336            Value::String(s) => Ok(s),
337            Value::Integer(i) => Ok(_lua.create_string(i.to_string())),
338            Value::Number(n) => Ok(_lua.create_string(n.to_string())),
339            other => Err(Error::FromLuaConversionError {
340                from: other.type_name(),
341                to: "String".to_string(),
342                message: None,
343            }),
344        }
345    }
346}
347
348// ---------------------------------------------------------------------------
349// Handles (Table, Function)
350// ---------------------------------------------------------------------------
351
352impl IntoLua for Table {
353    fn into_lua(self, _lua: &Lua) -> Result<Value> {
354        Ok(Value::Table(self))
355    }
356}
357
358impl IntoLua for &Table {
359    fn into_lua(self, _lua: &Lua) -> Result<Value> {
360        Ok(Value::Table(self.clone()))
361    }
362}
363
364impl FromLua for Table {
365    fn from_lua(value: Value, _lua: &Lua) -> Result<Self> {
366        match value {
367            Value::Table(t) => Ok(t),
368            other => Err(Error::FromLuaConversionError {
369                from: other.type_name(),
370                to: "Table".to_string(),
371                message: None,
372            }),
373        }
374    }
375}
376
377impl IntoLua for Function {
378    fn into_lua(self, _lua: &Lua) -> Result<Value> {
379        Ok(Value::Function(self))
380    }
381}
382
383impl IntoLua for &Function {
384    fn into_lua(self, _lua: &Lua) -> Result<Value> {
385        Ok(Value::Function(self.clone()))
386    }
387}
388
389impl FromLua for Function {
390    fn from_lua(value: Value, _lua: &Lua) -> Result<Self> {
391        match value {
392            Value::Function(f) => Ok(f),
393            other => Err(Error::FromLuaConversionError {
394                from: other.type_name(),
395                to: "Function".to_string(),
396                message: None,
397            }),
398        }
399    }
400}
401
402// ---------------------------------------------------------------------------
403// Vector (Luau) <-> Value::Vector
404// ---------------------------------------------------------------------------
405
406impl IntoLua for crate::Vector {
407    fn into_lua(self, _lua: &Lua) -> Result<Value> {
408        Ok(Value::Vector(self))
409    }
410}
411
412impl FromLua for crate::Vector {
413    fn from_lua(value: Value, _lua: &Lua) -> Result<Self> {
414        match value {
415            Value::Vector(v) => Ok(v),
416            other => Err(Error::FromLuaConversionError {
417                from: other.type_name(),
418                to: "vector".to_string(),
419                message: None,
420            }),
421        }
422    }
423}
424
425// ---------------------------------------------------------------------------
426// Buffer (Luau) <-> Value::Buffer
427// ---------------------------------------------------------------------------
428
429impl IntoLua for crate::Buffer {
430    fn into_lua(self, _lua: &Lua) -> Result<Value> {
431        Ok(Value::Buffer(self))
432    }
433}
434
435impl IntoLua for &crate::Buffer {
436    fn into_lua(self, _lua: &Lua) -> Result<Value> {
437        Ok(Value::Buffer(self.clone()))
438    }
439}
440
441impl FromLua for crate::Buffer {
442    fn from_lua(value: Value, _lua: &Lua) -> Result<Self> {
443        match value {
444            Value::Buffer(buf) => Ok(buf),
445            other => Err(Error::FromLuaConversionError {
446                from: other.type_name(),
447                to: "buffer".to_string(),
448                message: None,
449            }),
450        }
451    }
452}
453
454// ---------------------------------------------------------------------------
455// Option<T>
456// ---------------------------------------------------------------------------
457
458impl<T: IntoLua> IntoLua for Option<T> {
459    fn into_lua(self, lua: &Lua) -> Result<Value> {
460        match self {
461            Some(v) => v.into_lua(lua),
462            None => Ok(Value::Nil),
463        }
464    }
465}
466
467impl<T: FromLua> FromLua for Option<T> {
468    fn from_lua(value: Value, lua: &Lua) -> Result<Self> {
469        match value {
470            Value::Nil => Ok(None),
471            other => Ok(Some(T::from_lua(other, lua)?)),
472        }
473    }
474}
475
476// ---------------------------------------------------------------------------
477// Vec<T> <-> sequence table
478// ---------------------------------------------------------------------------
479
480impl<T: IntoLua> IntoLua for Vec<T> {
481    fn into_lua(self, lua: &Lua) -> Result<Value> {
482        let table = lua.create_table();
483        for (i, item) in self.into_iter().enumerate() {
484            // Lua sequences are 1-based.
485            table.set((i + 1) as i64, item)?;
486        }
487        Ok(Value::Table(table))
488    }
489}
490
491impl<T: IntoLua + Clone> IntoLua for &[T] {
492    fn into_lua(self, lua: &Lua) -> Result<Value> {
493        let table = lua.create_table();
494        for (i, item) in self.iter().enumerate() {
495            table.raw_set((i + 1) as i64, item.clone())?;
496        }
497        Ok(Value::Table(table))
498    }
499}
500
501impl<T: FromLua> FromLua for Vec<T> {
502    fn from_lua(value: Value, _lua: &Lua) -> Result<Self> {
503        match value {
504            Value::Table(t) => {
505                let len = t.raw_len();
506                let mut out = Vec::with_capacity(len);
507                for i in 1..=len {
508                    out.push(t.raw_get::<T>(i as i64)?);
509                }
510                Ok(out)
511            }
512            other => Err(Error::FromLuaConversionError {
513                from: other.type_name(),
514                to: "Vec".to_string(),
515                message: None,
516            }),
517        }
518    }
519}
520
521// ---------------------------------------------------------------------------
522// Fixed-size arrays [T; N] <-> sequence table
523// ---------------------------------------------------------------------------
524
525impl<T: IntoLua, const N: usize> IntoLua for [T; N] {
526    fn into_lua(self, lua: &Lua) -> Result<Value> {
527        let table = lua.create_table();
528        for (i, item) in self.into_iter().enumerate() {
529            table.raw_set((i + 1) as i64, item)?;
530        }
531        Ok(Value::Table(table))
532    }
533}
534
535impl<T: FromLua, const N: usize> FromLua for [T; N] {
536    fn from_lua(value: Value, lua: &Lua) -> Result<Self> {
537        // A Luau vector converts to an array of its components (matching mlua:
538        // `let v: [f64; 3] = lua.load("vector.create(1,2,3)").eval()?`).
539        if let Value::Vector(v) = value {
540            if N == crate::Vector::SIZE {
541                let x = T::from_lua(Value::Number(v.x() as f64), lua)?;
542                let y = T::from_lua(Value::Number(v.y() as f64), lua)?;
543                let z = T::from_lua(Value::Number(v.z() as f64), lua)?;
544                let comps = vec![x, y, z];
545                return <[T; N]>::try_from(comps).map_err(|_| Error::FromLuaConversionError {
546                    from: "vector",
547                    to: format!("[T; {N}]"),
548                    message: None,
549                });
550            }
551            return Err(Error::FromLuaConversionError {
552                from: "vector",
553                to: format!("[T; {N}]"),
554                message: Some(format!(
555                    "expected array of length {}, got {N}",
556                    crate::Vector::SIZE
557                )),
558            });
559        }
560        let vec: Vec<T> = Vec::from_lua(value, lua)?;
561        let len = vec.len();
562        <[T; N]>::try_from(vec).map_err(|_| Error::FromLuaConversionError {
563            from: "table",
564            to: format!("[T; {N}]"),
565            message: Some(format!("expected table of length {N}, got {len}")),
566        })
567    }
568}
569
570// ---------------------------------------------------------------------------
571// HashMap / BTreeMap <-> table
572// ---------------------------------------------------------------------------
573
574use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
575use std::hash::Hash;
576
577impl<K: IntoLua + Eq + Hash, V: IntoLua, S: std::hash::BuildHasher> IntoLua for HashMap<K, V, S> {
578    fn into_lua(self, lua: &Lua) -> Result<Value> {
579        let table = lua.create_table();
580        for (k, v) in self {
581            table.raw_set(k, v)?;
582        }
583        Ok(Value::Table(table))
584    }
585}
586
587impl<K: FromLua + Eq + Hash, V: FromLua, S: std::hash::BuildHasher + Default> FromLua
588    for HashMap<K, V, S>
589{
590    fn from_lua(value: Value, _lua: &Lua) -> Result<Self> {
591        match value {
592            Value::Table(t) => {
593                let mut out = HashMap::with_hasher(S::default());
594                for pair in t.pairs::<K, V>() {
595                    let (k, v) = pair?;
596                    out.insert(k, v);
597                }
598                Ok(out)
599            }
600            other => Err(Error::FromLuaConversionError {
601                from: other.type_name(),
602                to: "HashMap".to_string(),
603                message: None,
604            }),
605        }
606    }
607}
608
609impl<K: IntoLua + Ord, V: IntoLua> IntoLua for BTreeMap<K, V> {
610    fn into_lua(self, lua: &Lua) -> Result<Value> {
611        let table = lua.create_table();
612        for (k, v) in self {
613            table.raw_set(k, v)?;
614        }
615        Ok(Value::Table(table))
616    }
617}
618
619impl<K: FromLua + Ord, V: FromLua> FromLua for BTreeMap<K, V> {
620    fn from_lua(value: Value, _lua: &Lua) -> Result<Self> {
621        match value {
622            Value::Table(t) => {
623                let mut out = BTreeMap::new();
624                for pair in t.pairs::<K, V>() {
625                    let (k, v) = pair?;
626                    out.insert(k, v);
627                }
628                Ok(out)
629            }
630            other => Err(Error::FromLuaConversionError {
631                from: other.type_name(),
632                to: "BTreeMap".to_string(),
633                message: None,
634            }),
635        }
636    }
637}
638
639// ---------------------------------------------------------------------------
640// HashSet / BTreeSet <-> table (values become keys mapped to `true`)
641// ---------------------------------------------------------------------------
642
643impl<T: IntoLua + Eq + Hash, S: std::hash::BuildHasher> IntoLua for HashSet<T, S> {
644    fn into_lua(self, lua: &Lua) -> Result<Value> {
645        let table = lua.create_table();
646        for item in self {
647            table.raw_set(item, true)?;
648        }
649        Ok(Value::Table(table))
650    }
651}
652
653impl<T: FromLua + Eq + Hash, S: std::hash::BuildHasher + Default> FromLua for HashSet<T, S> {
654    fn from_lua(value: Value, lua: &Lua) -> Result<Self> {
655        from_lua_set(value, lua, "HashSet", |it| {
656            let mut out = HashSet::with_hasher(S::default());
657            for v in it {
658                out.insert(v?);
659            }
660            Ok(out)
661        })
662    }
663}
664
665impl<T: IntoLua + Ord> IntoLua for BTreeSet<T> {
666    fn into_lua(self, lua: &Lua) -> Result<Value> {
667        let table = lua.create_table();
668        for item in self {
669            table.raw_set(item, true)?;
670        }
671        Ok(Value::Table(table))
672    }
673}
674
675impl<T: FromLua + Ord> FromLua for BTreeSet<T> {
676    fn from_lua(value: Value, lua: &Lua) -> Result<Self> {
677        from_lua_set(value, lua, "BTreeSet", |it| {
678            let mut out = BTreeSet::new();
679            for v in it {
680                out.insert(v?);
681            }
682            Ok(out)
683        })
684    }
685}
686
687/// A Lua table can represent a set in two ways (matching mlua): as a sequence
688/// of values `{a, b, c}`, or as a map of keys `{[a] = true, ...}`. We support
689/// both: if the table has a non-empty sequence part, take its values;
690/// otherwise take its keys.
691fn from_lua_set<T: FromLua, C>(
692    value: Value,
693    _lua: &Lua,
694    to: &'static str,
695    build: impl FnOnce(SetIter<T>) -> Result<C>,
696) -> Result<C> {
697    match value {
698        Value::Table(t) => {
699            if t.raw_len() > 0 {
700                build(SetIter::Seq(t.sequence_values::<T>()))
701            } else {
702                let keys: Vec<Result<T>> =
703                    t.pairs::<T, Value>().map(|p| p.map(|(k, _)| k)).collect();
704                build(SetIter::Keys(keys.into_iter()))
705            }
706        }
707        other => Err(Error::FromLuaConversionError {
708            from: other.type_name(),
709            to: to.to_string(),
710            message: None,
711        }),
712    }
713}
714
715enum SetIter<T: FromLua> {
716    Seq(crate::table::TableSequence<T>),
717    Keys(std::vec::IntoIter<Result<T>>),
718}
719
720impl<T: FromLua> Iterator for SetIter<T> {
721    type Item = Result<T>;
722    fn next(&mut self) -> Option<Self::Item> {
723        match self {
724            SetIter::Seq(s) => s.next(),
725            SetIter::Keys(k) => k.next(),
726        }
727    }
728}
729
730// ---------------------------------------------------------------------------
731// char
732// ---------------------------------------------------------------------------
733
734impl IntoLua for char {
735    fn into_lua(self, lua: &Lua) -> Result<Value> {
736        Ok(Value::String(lua.create_string(self.to_string())))
737    }
738}
739
740impl FromLua for char {
741    fn from_lua(value: Value, lua: &Lua) -> Result<Self> {
742        match value {
743            Value::String(_) | Value::Integer(_) | Value::Number(_) => {}
744            other => {
745                return Err(Error::FromLuaConversionError {
746                    from: other.type_name(),
747                    to: "char".to_string(),
748                    message: Some("expected string or integer".to_string()),
749                })
750            }
751        }
752        if let Value::Integer(_) | Value::Number(_) = value {
753            let i = i64::from_lua(value, lua)?;
754            let cp = u32::try_from(i).ok().and_then(char::from_u32);
755            return cp.ok_or(Error::FromLuaConversionError {
756                from: "number",
757                to: "char".to_string(),
758                message: Some("integer out of range for a unicode char".to_string()),
759            });
760        }
761        let s = String::from_lua(value, lua)?;
762        let mut chars = s.chars();
763        match (chars.next(), chars.next()) {
764            (Some(c), None) => Ok(c),
765            _ => Err(Error::FromLuaConversionError {
766                from: "string",
767                to: "char".to_string(),
768                message: Some("expected string to have exactly one char".to_string()),
769            }),
770        }
771    }
772}
773
774// ---------------------------------------------------------------------------
775// Cow<str>, Box<str>, CString
776// ---------------------------------------------------------------------------
777
778impl IntoLua for std::borrow::Cow<'_, str> {
779    fn into_lua(self, lua: &Lua) -> Result<Value> {
780        Ok(Value::String(lua.create_string(self.as_ref())))
781    }
782}
783
784impl IntoLua for Box<str> {
785    fn into_lua(self, lua: &Lua) -> Result<Value> {
786        Ok(Value::String(lua.create_string(&*self)))
787    }
788}
789
790impl FromLua for Box<str> {
791    fn from_lua(value: Value, lua: &Lua) -> Result<Self> {
792        Ok(String::from_lua(value, lua)?.into_boxed_str())
793    }
794}
795
796impl IntoLua for std::ffi::CString {
797    fn into_lua(self, lua: &Lua) -> Result<Value> {
798        Ok(Value::String(lua.create_string(self.as_bytes())))
799    }
800}
801
802impl FromLua for std::ffi::CString {
803    fn from_lua(value: Value, _lua: &Lua) -> Result<Self> {
804        let bytes = match value {
805            Value::String(s) => s.as_bytes(),
806            other => {
807                return Err(Error::FromLuaConversionError {
808                    from: other.type_name(),
809                    to: "CString".to_string(),
810                    message: None,
811                })
812            }
813        };
814        std::ffi::CString::new(bytes).map_err(|e| Error::FromLuaConversionError {
815            from: "string",
816            to: "CString".to_string(),
817            message: Some(format!("interior nul byte: {e}")),
818        })
819    }
820}
821
822impl<T: IntoLua> IntoLua for Box<[T]> {
823    fn into_lua(self, lua: &Lua) -> Result<Value> {
824        self.into_vec().into_lua(lua)
825    }
826}
827
828impl<T: FromLua> FromLua for Box<[T]> {
829    fn from_lua(value: Value, lua: &Lua) -> Result<Self> {
830        Ok(Vec::<T>::from_lua(value, lua)?.into_boxed_slice())
831    }
832}
833
834// ---------------------------------------------------------------------------
835// Variadic<T>
836// ---------------------------------------------------------------------------
837
838impl<T: IntoLua> IntoLuaMulti for Variadic<T> {
839    fn into_lua_multi(self, lua: &Lua) -> Result<MultiValue> {
840        let vec: Vec<T> = self.into();
841        let mut m = MultiValue::with_capacity(vec.len());
842        for item in vec {
843            m.push_back(item.into_lua(lua)?);
844        }
845        Ok(m)
846    }
847}
848
849impl<T: FromLua> FromLuaMulti for Variadic<T> {
850    fn from_lua_multi(values: MultiValue, lua: &Lua) -> Result<Self> {
851        let mut out = Vec::with_capacity(values.len());
852        for v in values {
853            out.push(T::from_lua(v, lua)?);
854        }
855        Ok(Variadic::from(out))
856    }
857}
858
859// ---------------------------------------------------------------------------
860// Error <-> Value::Error  +  Result<T, E> : IntoLuaMulti
861// ---------------------------------------------------------------------------
862
863impl IntoLua for crate::light_userdata::LightUserData {
864    fn into_lua(self, _lua: &Lua) -> Result<Value> {
865        Ok(Value::LightUserData(self))
866    }
867}
868
869impl FromLua for crate::light_userdata::LightUserData {
870    fn from_lua(value: Value, _lua: &Lua) -> Result<Self> {
871        match value {
872            Value::LightUserData(lud) => Ok(lud),
873            other => Err(Error::FromLuaConversionError {
874                from: other.type_name(),
875                to: "LightUserData".to_string(),
876                message: None,
877            }),
878        }
879    }
880}
881
882impl IntoLua for Error {
883    fn into_lua(self, _lua: &Lua) -> Result<Value> {
884        Ok(Value::Error(Box::new(self)))
885    }
886}
887
888impl FromLua for Error {
889    fn from_lua(value: Value, _lua: &Lua) -> Result<Self> {
890        Ok(match value {
891            Value::Error(e) => *e,
892            // Any other Lua value converts to a runtime error carrying its
893            // string form (mirrors mlua's `convert::<Error>`).
894            Value::String(s) => Error::RuntimeError(s.to_string_lossy()),
895            other => Error::RuntimeError(other.to_string().unwrap_or_default()),
896        })
897    }
898}
899
900/// `Result<T, E>` spreads as the success values on `Ok`, or as `(nil, error)`
901/// on `Err` — mirroring mlua's `IntoLuaMulti for Result`.
902impl<T: IntoLuaMulti, E: IntoLua> IntoLuaMulti for std::result::Result<T, E> {
903    fn into_lua_multi(self, lua: &Lua) -> Result<MultiValue> {
904        match self {
905            Ok(v) => v.into_lua_multi(lua),
906            Err(e) => {
907                let mut m = MultiValue::with_capacity(2);
908                m.push_back(Value::Nil);
909                m.push_back(e.into_lua(lua)?);
910                Ok(m)
911            }
912        }
913    }
914}
915
916// ---------------------------------------------------------------------------
917// MultiValue passthrough
918// ---------------------------------------------------------------------------
919
920impl IntoLuaMulti for MultiValue {
921    fn into_lua_multi(self, _lua: &Lua) -> Result<MultiValue> {
922        Ok(self)
923    }
924}
925
926impl FromLuaMulti for MultiValue {
927    fn from_lua_multi(values: MultiValue, _lua: &Lua) -> Result<Self> {
928        Ok(values)
929    }
930}
931
932// ---------------------------------------------------------------------------
933// Tuples (IntoLuaMulti / FromLuaMulti) up to 12
934// ---------------------------------------------------------------------------
935
936// `IntoLuaMulti` for tuples: each element may itself spread to multiple values
937// (e.g. a trailing `Variadic<T>`), so concatenate their `MultiValue`s.
938macro_rules! impl_tuple_into {
939    () => {};
940    ($first:ident $($rest:ident)*) => {
941        impl_tuple_into!($($rest)*);
942
943        #[allow(non_snake_case)]
944        impl<$first: IntoLuaMulti, $($rest: IntoLuaMulti,)*> IntoLuaMulti for ($first, $($rest,)*) {
945            fn into_lua_multi(self, lua: &Lua) -> Result<MultiValue> {
946                let ($first, $($rest,)*) = self;
947                let mut m = MultiValue::new();
948                for v in $first.into_lua_multi(lua)? { m.push_back(v); }
949                $( for v in $rest.into_lua_multi(lua)? { m.push_back(v); } )*
950                Ok(m)
951            }
952        }
953    };
954}
955
956impl_tuple_into!(A B C D E F G H I J K L);
957
958// `FromLuaMulti` for tuples. The **last** element is parsed as a `FromLuaMulti`
959// (so it may be a trailing `Variadic<T>` that consumes every remaining value);
960// the preceding elements each consume exactly one value via `FromLua`.
961//
962// Because every `FromLua` type is also `FromLuaMulti` (the blanket impl), a
963// single impl per arity with the last slot bound `FromLuaMulti` subsumes the
964// all-`FromLua` case as well — no overlapping impls. Mirrors mlua's tuple
965// `FromLuaMulti`, which lets the final slot soak up the rest.
966macro_rules! impl_tuple_from {
967    // Base: a 1-tuple is just its single `FromLuaMulti` element.
968    ($last:ident;) => {
969        #[allow(non_snake_case)]
970        impl<$last: FromLuaMulti> FromLuaMulti for ($last,) {
971            fn from_lua_multi(values: MultiValue, lua: &Lua) -> Result<Self> {
972                Ok(($last::from_lua_multi(values, lua)?,))
973            }
974        }
975    };
976    ($last:ident; $head0:ident $($head:ident)*) => {
977        impl_tuple_from!($last; $($head)*);
978
979        #[allow(non_snake_case)]
980        impl<$head0: FromLua, $($head: FromLua,)* $last: FromLuaMulti>
981            FromLuaMulti for ($head0, $($head,)* $last,)
982        {
983            fn from_lua_multi(mut values: MultiValue, lua: &Lua) -> Result<Self> {
984                let $head0 = $head0::from_lua(values.pop_front().unwrap_or(Value::Nil), lua)?;
985                $( let $head = $head::from_lua(values.pop_front().unwrap_or(Value::Nil), lua)?; )*
986                let $last = $last::from_lua_multi(values, lua)?;
987                Ok(($head0, $($head,)* $last,))
988            }
989        }
990    };
991}
992
993// Arities 1..=12 (last element `FromLuaMulti`, the rest `FromLua`).
994impl_tuple_from!(L; A B C D E F G H I J K);