1use std::cell::RefCell;
2use std::cmp::Ordering;
3use std::collections::{vec_deque, HashSet, VecDeque};
4use std::ops::{Deref, DerefMut};
5use std::os::raw::{c_int, c_void};
6use std::string::String as StdString;
7use std::sync::Arc;
8use std::{fmt, mem, ptr, str};
9
10use num_traits::FromPrimitive;
11
12use crate::error::{Error, Result};
13use crate::function::Function;
14use crate::state::{Lua, RawLua};
15use crate::string::{BorrowedStr, String};
16use crate::table::Table;
17use crate::thread::Thread;
18use crate::types::{Integer, LightUserData, Number, SubtypeId};
19use crate::userdata::AnyUserData;
20use crate::util::{check_stack, StackGuard};
21
22#[cfg(feature = "serialize")]
23use {
24 crate::table::SerializableTable,
25 rustc_hash::FxHashSet,
26 serde::ser::{self, Serialize, Serializer},
27 std::{rc::Rc, result::Result as StdResult},
28};
29
30#[derive(Clone)]
34pub enum Value {
35 Nil,
37 Boolean(bool),
39 LightUserData(LightUserData),
41 Integer(Integer),
45 Number(Number),
47 #[cfg(any(feature = "luau", doc))]
49 #[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
50 Vector(crate::types::Vector),
51 String(String),
55 Table(Table),
57 Function(Function),
59 Thread(Thread),
61 UserData(AnyUserData),
64 Error(Box<Error>),
66}
67
68pub use self::Value::Nil;
69
70impl Value {
71 pub const NULL: Value = Value::LightUserData(LightUserData(ptr::null_mut()));
75
76 pub const fn type_name(&self) -> &'static str {
78 match *self {
79 Value::Nil => "nil",
80 Value::Boolean(_) => "boolean",
81 Value::LightUserData(_) => "lightuserdata",
82 Value::Integer(_) => "integer",
83 Value::Number(_) => "number",
84 #[cfg(feature = "luau")]
85 Value::Vector(_) => "vector",
86 Value::String(_) => "string",
87 Value::Table(_) => "table",
88 Value::Function(_) => "function",
89 Value::Thread(_) => "thread",
90 Value::UserData(AnyUserData(_, SubtypeId::None)) => "userdata",
91 #[cfg(feature = "luau")]
92 Value::UserData(AnyUserData(_, SubtypeId::Buffer)) => "buffer",
93 #[cfg(feature = "luajit")]
94 Value::UserData(AnyUserData(_, SubtypeId::CData)) => "cdata",
95 Value::Error(_) => "error",
96 }
97 }
98
99 pub fn equals<T: AsRef<Self>>(&self, other: T) -> Result<bool> {
110 match (self, other.as_ref()) {
111 (Value::Table(a), Value::Table(b)) => a.equals(b),
112 (Value::UserData(a), Value::UserData(b)) => a.equals(b),
113 (a, b) => Ok(a == b),
114 }
115 }
116
117 #[inline]
125 pub fn to_pointer(&self) -> *const c_void {
126 match self {
127 Value::LightUserData(ud) => ud.0,
128 Value::String(String(r))
129 | Value::Table(Table(r))
130 | Value::Function(Function(r))
131 | Value::Thread(Thread(r, ..))
132 | Value::UserData(AnyUserData(r, ..)) => r.to_pointer(),
133 _ => ptr::null(),
134 }
135 }
136
137 pub fn to_string(&self) -> Result<StdString> {
142 match self {
143 Value::Nil => Ok("nil".to_string()),
144 Value::Boolean(b) => Ok(b.to_string()),
145 Value::LightUserData(ud) if ud.0.is_null() => Ok("null".to_string()),
146 Value::LightUserData(ud) => Ok(format!("lightuserdata: {:p}", ud.0)),
147 Value::Integer(i) => Ok(i.to_string()),
148 Value::Number(n) => Ok(n.to_string()),
149 #[cfg(feature = "luau")]
150 Value::Vector(v) => Ok(v.to_string()),
151 Value::String(s) => Ok(s.to_str()?.to_string()),
152 Value::Table(Table(r))
153 | Value::Function(Function(r))
154 | Value::Thread(Thread(r, ..))
155 | Value::UserData(AnyUserData(r, ..)) => unsafe {
156 let lua = r.lua.lock();
157 let state = lua.state();
158 let _guard = StackGuard::new(state);
159 check_stack(state, 3)?;
160
161 lua.push_ref(r);
162 protect_lua!(state, 1, 1, fn(state) {
163 ffi::luaL_tolstring(state, -1, ptr::null_mut());
164 })?;
165 Ok(String(lua.pop_ref()).to_str()?.to_string())
166 },
167 Value::Error(err) => Ok(err.to_string()),
168 }
169 }
170
171 #[inline]
173 pub fn is_nil(&self) -> bool {
174 self == &Nil
175 }
176
177 #[inline]
179 pub fn is_null(&self) -> bool {
180 self == &Self::NULL
181 }
182
183 #[inline]
185 pub fn is_boolean(&self) -> bool {
186 self.as_boolean().is_some()
187 }
188
189 #[inline]
193 pub fn as_boolean(&self) -> Option<bool> {
194 match *self {
195 Value::Boolean(b) => Some(b),
196 _ => None,
197 }
198 }
199
200 #[inline]
202 pub fn is_light_userdata(&self) -> bool {
203 self.as_light_userdata().is_some()
204 }
205
206 #[inline]
210 pub fn as_light_userdata(&self) -> Option<LightUserData> {
211 match *self {
212 Value::LightUserData(l) => Some(l),
213 _ => None,
214 }
215 }
216
217 #[inline]
219 pub fn is_integer(&self) -> bool {
220 self.as_integer().is_some()
221 }
222
223 #[inline]
227 pub fn as_integer(&self) -> Option<Integer> {
228 match *self {
229 Value::Integer(i) => Some(i),
230 _ => None,
231 }
232 }
233
234 #[inline]
238 pub fn as_i32(&self) -> Option<i32> {
239 #[allow(clippy::useless_conversion)]
240 self.as_integer().and_then(|i| i32::try_from(i).ok())
241 }
242
243 #[inline]
247 pub fn as_u32(&self) -> Option<u32> {
248 self.as_integer().and_then(|i| u32::try_from(i).ok())
249 }
250
251 #[inline]
255 pub fn as_i64(&self) -> Option<i64> {
256 self.as_integer().map(i64::from)
257 }
258
259 #[inline]
263 pub fn as_u64(&self) -> Option<u64> {
264 self.as_integer().and_then(|i| u64::try_from(i).ok())
265 }
266
267 #[inline]
271 pub fn as_isize(&self) -> Option<isize> {
272 self.as_integer().and_then(|i| isize::try_from(i).ok())
273 }
274
275 #[inline]
279 pub fn as_usize(&self) -> Option<usize> {
280 self.as_integer().and_then(|i| usize::try_from(i).ok())
281 }
282
283 #[inline]
285 pub fn is_number(&self) -> bool {
286 self.as_number().is_some()
287 }
288
289 #[inline]
293 pub fn as_number(&self) -> Option<Number> {
294 match *self {
295 Value::Number(n) => Some(n),
296 _ => None,
297 }
298 }
299
300 #[inline]
304 pub fn as_f32(&self) -> Option<f32> {
305 self.as_number().and_then(f32::from_f64)
306 }
307
308 #[inline]
312 pub fn as_f64(&self) -> Option<f64> {
313 self.as_number()
314 }
315
316 #[inline]
318 pub fn is_string(&self) -> bool {
319 self.as_string().is_some()
320 }
321
322 #[inline]
326 pub fn as_string(&self) -> Option<&String> {
327 match self {
328 Value::String(s) => Some(s),
329 _ => None,
330 }
331 }
332
333 #[inline]
338 pub fn as_str(&self) -> Option<BorrowedStr> {
339 self.as_string().and_then(|s| s.to_str().ok())
340 }
341
342 #[inline]
346 pub fn as_string_lossy(&self) -> Option<StdString> {
347 self.as_string().map(|s| s.to_string_lossy())
348 }
349
350 #[inline]
352 pub fn is_table(&self) -> bool {
353 self.as_table().is_some()
354 }
355
356 #[inline]
360 pub fn as_table(&self) -> Option<&Table> {
361 match self {
362 Value::Table(t) => Some(t),
363 _ => None,
364 }
365 }
366
367 #[inline]
369 pub fn is_thread(&self) -> bool {
370 self.as_thread().is_some()
371 }
372
373 #[inline]
377 pub fn as_thread(&self) -> Option<&Thread> {
378 match self {
379 Value::Thread(t) => Some(t),
380 _ => None,
381 }
382 }
383
384 #[inline]
386 pub fn is_function(&self) -> bool {
387 self.as_function().is_some()
388 }
389
390 #[inline]
394 pub fn as_function(&self) -> Option<&Function> {
395 match self {
396 Value::Function(f) => Some(f),
397 _ => None,
398 }
399 }
400
401 #[inline]
403 pub fn is_userdata(&self) -> bool {
404 self.as_userdata().is_some()
405 }
406
407 #[inline]
411 pub fn as_userdata(&self) -> Option<&AnyUserData> {
412 match self {
413 Value::UserData(ud) => Some(ud),
414 _ => None,
415 }
416 }
417
418 #[cfg(any(feature = "luau", doc))]
420 #[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
421 #[doc(hidden)]
422 #[inline]
423 pub fn is_buffer(&self) -> bool {
424 self.as_userdata()
425 .map(|ud| ud.1 == SubtypeId::Buffer)
426 .unwrap_or_default()
427 }
428
429 #[cfg(any(feature = "luajit", doc))]
431 #[cfg_attr(docsrs, doc(cfg(feature = "luajit")))]
432 #[doc(hidden)]
433 #[inline]
434 pub fn is_cdata(&self) -> bool {
435 self.as_userdata()
436 .map(|ud| ud.1 == SubtypeId::CData)
437 .unwrap_or_default()
438 }
439
440 #[cfg(feature = "serialize")]
444 #[cfg_attr(docsrs, doc(cfg(feature = "serialize")))]
445 #[doc(hidden)]
446 pub fn to_serializable(&self) -> SerializableValue {
447 SerializableValue::new(self, Default::default(), None)
448 }
449
450 pub(crate) fn cmp(&self, other: &Self) -> Ordering {
453 fn cmp_num(a: Number, b: Number) -> Ordering {
454 match (a, b) {
455 _ if a < b => Ordering::Less,
456 _ if a > b => Ordering::Greater,
457 _ => Ordering::Equal,
458 }
459 }
460
461 match (self, other) {
462 (Value::Nil, Value::Nil) => Ordering::Equal,
464 (Value::Nil, _) => Ordering::Less,
465 (_, Value::Nil) => Ordering::Greater,
466 (Value::LightUserData(ud1), Value::LightUserData(ud2)) if ud1 == ud2 => Ordering::Equal,
468 (Value::LightUserData(ud1), _) if ud1.0.is_null() => Ordering::Less,
469 (_, Value::LightUserData(ud2)) if ud2.0.is_null() => Ordering::Greater,
470 (Value::Boolean(a), Value::Boolean(b)) => a.cmp(b),
472 (Value::Boolean(_), _) => Ordering::Less,
473 (_, Value::Boolean(_)) => Ordering::Greater,
474 (Value::Integer(a), Value::Integer(b)) => a.cmp(b),
476 (&Value::Integer(a), &Value::Number(b)) => cmp_num(a as Number, b),
477 (&Value::Number(a), &Value::Integer(b)) => cmp_num(a, b as Number),
478 (&Value::Number(a), &Value::Number(b)) => cmp_num(a, b),
479 (Value::Integer(_) | Value::Number(_), _) => Ordering::Less,
480 (_, Value::Integer(_) | Value::Number(_)) => Ordering::Greater,
481 (Value::String(a), Value::String(b)) => a.as_bytes().cmp(&b.as_bytes()),
483 (Value::String(_), _) => Ordering::Less,
484 (_, Value::String(_)) => Ordering::Greater,
485 (a, b) => a.to_pointer().cmp(&b.to_pointer()),
487 }
488 }
489
490 pub(crate) fn fmt_pretty(
491 &self,
492 fmt: &mut fmt::Formatter,
493 recursive: bool,
494 ident: usize,
495 visited: &mut HashSet<*const c_void>,
496 ) -> fmt::Result {
497 match self {
498 Value::Nil => write!(fmt, "nil"),
499 Value::Boolean(b) => write!(fmt, "{b}"),
500 Value::LightUserData(ud) if ud.0.is_null() => write!(fmt, "null"),
501 Value::LightUserData(ud) => write!(fmt, "lightuserdata: {:?}", ud.0),
502 Value::Integer(i) => write!(fmt, "{i}"),
503 Value::Number(n) => write!(fmt, "{n}"),
504 #[cfg(feature = "luau")]
505 Value::Vector(v) => write!(fmt, "{v}"),
506 Value::String(s) => write!(fmt, "{s:?}"),
507 Value::Table(t) if recursive && !visited.contains(&t.to_pointer()) => {
508 t.fmt_pretty(fmt, ident, visited)
509 }
510 t @ Value::Table(_) => write!(fmt, "table: {:?}", t.to_pointer()),
511 f @ Value::Function(_) => write!(fmt, "function: {:?}", f.to_pointer()),
512 t @ Value::Thread(_) => write!(fmt, "thread: {:?}", t.to_pointer()),
513 u @ Value::UserData(ud) => {
514 let name = ud.type_name().ok().flatten();
516 let s = name
517 .map(|name| format!("{name}: {:?}", u.to_pointer()))
518 .or_else(|| u.to_string().ok())
519 .unwrap_or_else(|| format!("userdata: {:?}", u.to_pointer()));
520 write!(fmt, "{s}")
521 }
522 Value::Error(e) if recursive => write!(fmt, "{e:?}"),
523 Value::Error(_) => write!(fmt, "error"),
524 }
525 }
526}
527
528impl fmt::Debug for Value {
529 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
530 if fmt.alternate() {
531 return self.fmt_pretty(fmt, true, 0, &mut HashSet::new());
532 }
533 match self {
534 Value::Nil => write!(fmt, "Nil"),
535 Value::Boolean(b) => write!(fmt, "Boolean({b})"),
536 Value::LightUserData(ud) => write!(fmt, "{ud:?}"),
537 Value::Integer(i) => write!(fmt, "Integer({i})"),
538 Value::Number(n) => write!(fmt, "Number({n})"),
539 #[cfg(feature = "luau")]
540 Value::Vector(v) => write!(fmt, "{v:?}"),
541 Value::String(s) => write!(fmt, "String({s:?})"),
542 Value::Table(t) => write!(fmt, "{t:?}"),
543 Value::Function(f) => write!(fmt, "{f:?}"),
544 Value::Thread(t) => write!(fmt, "{t:?}"),
545 Value::UserData(ud) => write!(fmt, "{ud:?}"),
546 Value::Error(e) => write!(fmt, "Error({e:?})"),
547 }
548 }
549}
550
551impl PartialEq for Value {
552 fn eq(&self, other: &Self) -> bool {
553 match (self, other) {
554 (Value::Nil, Value::Nil) => true,
555 (Value::Boolean(a), Value::Boolean(b)) => a == b,
556 (Value::LightUserData(a), Value::LightUserData(b)) => a == b,
557 (Value::Integer(a), Value::Integer(b)) => *a == *b,
558 (Value::Integer(a), Value::Number(b)) => *a as Number == *b,
559 (Value::Number(a), Value::Integer(b)) => *a == *b as Number,
560 (Value::Number(a), Value::Number(b)) => *a == *b,
561 #[cfg(feature = "luau")]
562 (Value::Vector(v1), Value::Vector(v2)) => v1 == v2,
563 (Value::String(a), Value::String(b)) => a == b,
564 (Value::Table(a), Value::Table(b)) => a == b,
565 (Value::Function(a), Value::Function(b)) => a == b,
566 (Value::Thread(a), Value::Thread(b)) => a == b,
567 (Value::UserData(a), Value::UserData(b)) => a == b,
568 _ => false,
569 }
570 }
571}
572
573impl AsRef<Value> for Value {
574 #[inline]
575 fn as_ref(&self) -> &Self {
576 self
577 }
578}
579
580#[cfg(feature = "serialize")]
582#[cfg_attr(docsrs, doc(cfg(feature = "serialize")))]
583pub struct SerializableValue<'a> {
584 value: &'a Value,
585 options: crate::serde::de::Options,
586 visited: Option<Rc<RefCell<FxHashSet<*const c_void>>>>,
588}
589
590#[cfg(feature = "serialize")]
591impl Serialize for Value {
592 #[inline]
593 fn serialize<S: Serializer>(&self, serializer: S) -> StdResult<S::Ok, S::Error> {
594 SerializableValue::new(self, Default::default(), None).serialize(serializer)
595 }
596}
597
598#[cfg(feature = "serialize")]
599impl<'a> SerializableValue<'a> {
600 #[inline]
601 pub(crate) fn new(
602 value: &'a Value,
603 options: crate::serde::de::Options,
604 visited: Option<&Rc<RefCell<FxHashSet<*const c_void>>>>,
605 ) -> Self {
606 if let Value::Table(_) = value {
607 return Self {
608 value,
609 options,
610 visited: visited.cloned().or_else(|| Some(Default::default())),
612 };
613 }
614 Self {
615 value,
616 options,
617 visited: None,
618 }
619 }
620
621 #[must_use]
627 pub const fn deny_unsupported_types(mut self, enabled: bool) -> Self {
628 self.options.deny_unsupported_types = enabled;
629 self
630 }
631
632 #[must_use]
638 pub const fn deny_recursive_tables(mut self, enabled: bool) -> Self {
639 self.options.deny_recursive_tables = enabled;
640 self
641 }
642
643 #[must_use]
647 pub const fn sort_keys(mut self, enabled: bool) -> Self {
648 self.options.sort_keys = enabled;
649 self
650 }
651}
652
653#[cfg(feature = "serialize")]
654impl<'a> Serialize for SerializableValue<'a> {
655 fn serialize<S>(&self, serializer: S) -> StdResult<S::Ok, S::Error>
656 where
657 S: Serializer,
658 {
659 match self.value {
660 Value::Nil => serializer.serialize_unit(),
661 Value::Boolean(b) => serializer.serialize_bool(*b),
662 #[allow(clippy::useless_conversion)]
663 Value::Integer(i) => serializer.serialize_i64((*i).into()),
664 Value::Number(n) => serializer.serialize_f64(*n),
665 #[cfg(feature = "luau")]
666 Value::Vector(v) => v.serialize(serializer),
667 Value::String(s) => s.serialize(serializer),
668 Value::Table(t) => {
669 let visited = self.visited.as_ref().unwrap().clone();
670 SerializableTable::new(t, self.options, visited).serialize(serializer)
671 }
672 Value::LightUserData(ud) if ud.0.is_null() => serializer.serialize_none(),
673 Value::UserData(ud) if ud.is_serializable() || self.options.deny_unsupported_types => {
674 ud.serialize(serializer)
675 }
676 Value::Function(_)
677 | Value::Thread(_)
678 | Value::UserData(_)
679 | Value::LightUserData(_)
680 | Value::Error(_) => {
681 if self.options.deny_unsupported_types {
682 let msg = format!("cannot serialize <{}>", self.value.type_name());
683 Err(ser::Error::custom(msg))
684 } else {
685 serializer.serialize_unit()
686 }
687 }
688 }
689 }
690}
691
692pub trait IntoLua: Sized {
694 fn into_lua(self, lua: &Lua) -> Result<Value>;
696
697 #[doc(hidden)]
702 #[inline]
703 unsafe fn push_into_stack(self, lua: &RawLua) -> Result<()> {
704 lua.push_value(&self.into_lua(lua.lua())?)
705 }
706}
707
708pub trait FromLua: Sized {
710 fn from_lua(value: Value, lua: &Lua) -> Result<Self>;
712
713 #[doc(hidden)]
718 #[inline]
719 fn from_lua_arg(arg: Value, i: usize, to: Option<&str>, lua: &Lua) -> Result<Self> {
720 Self::from_lua(arg, lua).map_err(|err| Error::BadArgument {
721 to: to.map(|s| s.to_string()),
722 pos: i,
723 name: None,
724 cause: Arc::new(err),
725 })
726 }
727
728 #[doc(hidden)]
730 #[inline]
731 unsafe fn from_stack(idx: c_int, lua: &RawLua) -> Result<Self> {
732 Self::from_lua(lua.stack_value(idx, None), lua.lua())
733 }
734
735 #[doc(hidden)]
737 #[inline]
738 unsafe fn from_stack_arg(idx: c_int, i: usize, to: Option<&str>, lua: &RawLua) -> Result<Self> {
739 Self::from_stack(idx, lua).map_err(|err| Error::BadArgument {
740 to: to.map(|s| s.to_string()),
741 pos: i,
742 name: None,
743 cause: Arc::new(err),
744 })
745 }
746}
747
748#[derive(Default, Debug, Clone)]
750pub struct MultiValue(VecDeque<Value>);
751
752impl Deref for MultiValue {
753 type Target = VecDeque<Value>;
754
755 #[inline]
756 fn deref(&self) -> &Self::Target {
757 &self.0
758 }
759}
760
761impl DerefMut for MultiValue {
762 #[inline]
763 fn deref_mut(&mut self) -> &mut Self::Target {
764 &mut self.0
765 }
766}
767
768impl MultiValue {
769 #[inline]
771 pub const fn new() -> MultiValue {
772 MultiValue(VecDeque::new())
773 }
774
775 pub fn with_capacity(capacity: usize) -> MultiValue {
777 MultiValue(VecDeque::with_capacity(capacity))
778 }
779
780 #[inline]
781 pub(crate) fn from_lua_iter<T: IntoLua>(lua: &Lua, iter: impl IntoIterator<Item = T>) -> Result<Self> {
782 let iter = iter.into_iter();
783 let mut multi_value = MultiValue::with_capacity(iter.size_hint().0);
784 for value in iter {
785 multi_value.push_back(value.into_lua(lua)?);
786 }
787 Ok(multi_value)
788 }
789}
790
791impl FromIterator<Value> for MultiValue {
792 #[inline]
793 fn from_iter<I: IntoIterator<Item = Value>>(iter: I) -> Self {
794 let mut multi_value = MultiValue::new();
795 multi_value.extend(iter);
796 multi_value
797 }
798}
799
800impl IntoIterator for MultiValue {
801 type Item = Value;
802 type IntoIter = vec_deque::IntoIter<Value>;
803
804 #[inline]
805 fn into_iter(mut self) -> Self::IntoIter {
806 let deque = mem::take(&mut self.0);
807 mem::forget(self);
808 deque.into_iter()
809 }
810}
811
812impl<'a> IntoIterator for &'a MultiValue {
813 type Item = &'a Value;
814 type IntoIter = vec_deque::Iter<'a, Value>;
815
816 #[inline]
817 fn into_iter(self) -> Self::IntoIter {
818 self.0.iter()
819 }
820}
821
822pub trait IntoLuaMulti: Sized {
827 fn into_lua_multi(self, lua: &Lua) -> Result<MultiValue>;
829
830 #[doc(hidden)]
834 #[inline]
835 unsafe fn push_into_stack_multi(self, lua: &RawLua) -> Result<c_int> {
836 let values = self.into_lua_multi(lua.lua())?;
837 let len: c_int = values.len().try_into().unwrap();
838 unsafe {
839 check_stack(lua.state(), len + 1)?;
840 for val in &values {
841 lua.push_value(val)?;
842 }
843 }
844 Ok(len)
845 }
846}
847
848pub trait FromLuaMulti: Sized {
853 fn from_lua_multi(values: MultiValue, lua: &Lua) -> Result<Self>;
860
861 #[doc(hidden)]
866 #[inline]
867 fn from_lua_args(args: MultiValue, i: usize, to: Option<&str>, lua: &Lua) -> Result<Self> {
868 let _ = (i, to);
869 Self::from_lua_multi(args, lua)
870 }
871
872 #[doc(hidden)]
874 #[inline]
875 unsafe fn from_stack_multi(nvals: c_int, lua: &RawLua) -> Result<Self> {
876 let mut values = MultiValue::with_capacity(nvals as usize);
877 for idx in 0..nvals {
878 values.push_back(lua.stack_value(-nvals + idx, None));
879 }
880 if nvals > 0 {
881 ffi::lua_pop(lua.state(), nvals);
883 }
884 Self::from_lua_multi(values, lua.lua())
885 }
886
887 #[doc(hidden)]
889 #[inline]
890 unsafe fn from_stack_args(nargs: c_int, i: usize, to: Option<&str>, lua: &RawLua) -> Result<Self> {
891 let _ = (i, to);
892 Self::from_stack_multi(nargs, lua)
893 }
894}
895
896#[cfg(test)]
897mod assertions {
898 use super::*;
899
900 #[cfg(not(feature = "send"))]
901 static_assertions::assert_not_impl_any!(Value: Send);
902 #[cfg(not(feature = "send"))]
903 static_assertions::assert_not_impl_any!(MultiValue: Send);
904
905 #[cfg(feature = "send")]
906 static_assertions::assert_impl_all!(Value: Send, Sync);
907 #[cfg(feature = "send")]
908 static_assertions::assert_impl_all!(MultiValue: Send, Sync);
909}