Skip to main content

mlua/
value.rs

1use std::cmp::Ordering;
2use std::collections::HashSet;
3use std::os::raw::c_void;
4use std::{fmt, ptr, str};
5
6use num_traits::FromPrimitive;
7
8use crate::error::{Error, Result};
9use crate::function::Function;
10use crate::string::LuaString;
11use crate::table::Table;
12use crate::thread::Thread;
13use crate::types::{Integer, LightUserData, Number, ValueRef};
14use crate::userdata::AnyUserData;
15use crate::util::{StackGuard, check_stack};
16
17#[cfg(feature = "serde")]
18use {
19    crate::table::SerializableTable,
20    rustc_hash::FxHashSet,
21    serde::ser::{self, Serialize, Serializer},
22    std::{cell::RefCell, rc::Rc, result::Result as StdResult},
23};
24
25/// A dynamically typed Lua value.
26///
27/// The non-primitive variants (eg. string/table/function/thread/userdata) contain handle types
28/// into the internal Lua state. It is a logic error to mix handle types between separate
29/// `Lua` instances, and doing so will result in a panic.
30#[derive(Clone, Default)]
31pub enum Value {
32    /// The Lua value `nil`.
33    #[default]
34    Nil,
35    /// The Lua value `true` or `false`.
36    Boolean(bool),
37    /// A "light userdata" object, equivalent to a raw pointer.
38    LightUserData(LightUserData),
39    /// An integer number.
40    ///
41    /// Any Lua number convertible to an `Integer` will be represented as this variant.
42    Integer(Integer),
43    /// A floating point number.
44    Number(Number),
45    /// A Luau vector.
46    #[cfg(any(feature = "luau", doc))]
47    #[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
48    Vector(crate::Vector),
49    /// An interned string, managed by Lua.
50    ///
51    /// Unlike Rust strings, Lua strings may not be valid UTF-8.
52    String(LuaString),
53    /// Reference to a Lua table.
54    Table(Table),
55    /// Reference to a Lua function (or closure).
56    Function(Function),
57    /// Reference to a Lua thread (or coroutine).
58    Thread(Thread),
59    /// Reference to a userdata object that holds a custom type which implements `UserData`.
60    ///
61    /// Special builtin userdata types will be represented as other `Value` variants.
62    UserData(AnyUserData),
63    /// A Luau buffer.
64    #[cfg(any(feature = "luau", doc))]
65    #[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
66    Buffer(crate::Buffer),
67    /// `Error` is a special builtin userdata type. When received from Lua it is implicitly cloned.
68    Error(Box<Error>),
69    /// Any other value not known to mlua (eg. LuaJIT CData).
70    Other(#[doc(hidden)] ValueRef),
71}
72
73pub use self::Value::Nil;
74
75impl Value {
76    /// A special value (lightuserdata) to represent null value.
77    ///
78    /// It can be used in Lua tables without downsides of `nil`.
79    pub const NULL: Value = Value::LightUserData(LightUserData(ptr::null_mut()));
80
81    /// Returns type name of this value.
82    pub fn type_name(&self) -> &'static str {
83        match *self {
84            Value::Nil => "nil",
85            Value::Boolean(_) => "boolean",
86            Value::LightUserData(_) => "lightuserdata",
87            Value::Integer(_) => "integer",
88            Value::Number(_) => "number",
89            #[cfg(feature = "luau")]
90            Value::Vector(_) => "vector",
91            Value::String(_) => "string",
92            Value::Table(_) => "table",
93            Value::Function(_) => "function",
94            Value::Thread(_) => "thread",
95            Value::UserData(_) => "userdata",
96            #[cfg(feature = "luau")]
97            Value::Buffer(_) => "buffer",
98            Value::Error(_) => "error",
99            Value::Other(_) => "other",
100        }
101    }
102
103    /// Compares two values for equality.
104    ///
105    /// Equality comparisons do not convert strings to numbers or vice versa.
106    /// Tables, functions, threads, and userdata are compared by reference:
107    /// two objects are considered equal only if they are the same object.
108    ///
109    /// If table or userdata have `__eq` metamethod then mlua will try to invoke it.
110    /// The first value is checked first. If that value does not define a metamethod
111    /// for `__eq`, then mlua will check the second value.
112    /// Then mlua calls the metamethod with the two values as arguments, if found.
113    pub fn equals(&self, other: &Self) -> Result<bool> {
114        match (self, other) {
115            (Value::Table(a), Value::Table(b)) => a.equals(b),
116            (Value::UserData(a), Value::UserData(b)) => a.equals(b),
117            (a, b) => Ok(a == b),
118        }
119    }
120
121    /// Converts the value to a generic C pointer.
122    ///
123    /// The value can be a userdata, a table, a thread, a string, or a function; otherwise it
124    /// returns NULL. Different objects will give different pointers.
125    /// There is no way to convert the pointer back to its original value.
126    ///
127    /// Typically this function is used only for hashing and debug information.
128    #[inline]
129    pub fn to_pointer(&self) -> *const c_void {
130        match self {
131            Value::LightUserData(ud) => ud.0,
132            Value::Table(Table(vref))
133            | Value::Function(Function(vref))
134            | Value::Thread(Thread(vref, ..))
135            | Value::UserData(AnyUserData(vref))
136            | Value::Other(vref) => vref.to_pointer(),
137            Value::String(s) => s.to_pointer(),
138            #[cfg(feature = "luau")]
139            Value::Buffer(crate::Buffer(vref)) => vref.to_pointer(),
140            _ => ptr::null(),
141        }
142    }
143
144    /// Converts the value to a string.
145    ///
146    /// This might invoke the `__tostring` metamethod for non-primitive types (eg. tables,
147    /// functions).
148    pub fn to_string(&self) -> Result<String> {
149        unsafe fn invoke_tostring(vref: &ValueRef) -> Result<String> {
150            let lua = vref.lua.lock();
151            let state = lua.state();
152            let _guard = StackGuard::new(state);
153            check_stack(state, 3)?;
154
155            lua.push_ref(vref);
156            protect_lua!(state, 1, 1, fn(state) {
157                ffi::luaL_tolstring(state, -1, ptr::null_mut());
158            })?;
159            Ok(LuaString(lua.try_pop_ref()?).to_str()?.to_string())
160        }
161
162        match self {
163            Value::Nil => Ok("nil".to_string()),
164            Value::Boolean(b) => Ok(b.to_string()),
165            Value::LightUserData(ud) if ud.0.is_null() => Ok("null".to_string()),
166            Value::LightUserData(ud) => Ok(format!("lightuserdata: {:p}", ud.0)),
167            Value::Integer(i) => Ok(i.to_string()),
168            Value::Number(n) => Ok(n.to_string()),
169            #[cfg(feature = "luau")]
170            Value::Vector(v) => Ok(v.to_string()),
171            Value::String(s) => Ok(s.to_str()?.to_string()),
172            Value::Table(Table(vref))
173            | Value::Function(Function(vref))
174            | Value::Thread(Thread(vref, ..))
175            | Value::UserData(AnyUserData(vref))
176            | Value::Other(vref) => unsafe { invoke_tostring(vref) },
177            #[cfg(feature = "luau")]
178            Value::Buffer(crate::Buffer(vref)) => unsafe { invoke_tostring(vref) },
179            Value::Error(err) => Ok(err.to_string()),
180        }
181    }
182
183    /// Returns `true` if the value is a [`Nil`].
184    #[inline]
185    pub fn is_nil(&self) -> bool {
186        self == &Nil
187    }
188
189    /// Returns `true` if the value is a [`NULL`].
190    ///
191    /// [`NULL`]: Value::NULL
192    #[inline]
193    pub fn is_null(&self) -> bool {
194        self == &Self::NULL
195    }
196
197    /// Returns `true` if the value is a boolean.
198    #[inline]
199    pub fn is_boolean(&self) -> bool {
200        self.as_boolean().is_some()
201    }
202
203    /// Cast the value to boolean.
204    ///
205    /// If the value is a Boolean, returns it or `None` otherwise.
206    #[inline]
207    pub fn as_boolean(&self) -> Option<bool> {
208        match *self {
209            Value::Boolean(b) => Some(b),
210            _ => None,
211        }
212    }
213
214    /// Returns `true` if the value is a [`LightUserData`].
215    #[inline]
216    pub fn is_light_userdata(&self) -> bool {
217        self.as_light_userdata().is_some()
218    }
219
220    /// Cast the value to [`LightUserData`].
221    ///
222    /// If the value is a [`LightUserData`], returns it or `None` otherwise.
223    #[inline]
224    pub fn as_light_userdata(&self) -> Option<LightUserData> {
225        match *self {
226            Value::LightUserData(l) => Some(l),
227            _ => None,
228        }
229    }
230
231    /// Returns `true` if the value is an [`Integer`].
232    #[inline]
233    pub fn is_integer(&self) -> bool {
234        self.as_integer().is_some()
235    }
236
237    /// Cast the value to [`Integer`].
238    ///
239    /// If the value is a Lua [`Integer`], returns it or `None` otherwise.
240    #[inline]
241    pub fn as_integer(&self) -> Option<Integer> {
242        match *self {
243            Value::Integer(i) => Some(i),
244            _ => None,
245        }
246    }
247
248    /// Cast the value to `i32`.
249    ///
250    /// If the value is a Lua [`Integer`], try to convert it to `i32` or return `None` otherwise.
251    #[inline]
252    pub fn as_i32(&self) -> Option<i32> {
253        #[allow(clippy::useless_conversion)]
254        self.as_integer().and_then(|i| i32::try_from(i).ok())
255    }
256
257    /// Cast the value to `u32`.
258    ///
259    /// If the value is a Lua [`Integer`], try to convert it to `u32` or return `None` otherwise.
260    #[inline]
261    pub fn as_u32(&self) -> Option<u32> {
262        self.as_integer().and_then(|i| u32::try_from(i).ok())
263    }
264
265    /// Cast the value to `i64`.
266    ///
267    /// If the value is a Lua [`Integer`], try to convert it to `i64` or return `None` otherwise.
268    #[inline]
269    pub fn as_i64(&self) -> Option<i64> {
270        #[cfg(target_pointer_width = "64")]
271        return self.as_integer();
272        #[cfg(not(target_pointer_width = "64"))]
273        return self.as_integer().map(i64::from);
274    }
275
276    /// Cast the value to `u64`.
277    ///
278    /// If the value is a Lua [`Integer`], try to convert it to `u64` or return `None` otherwise.
279    #[inline]
280    pub fn as_u64(&self) -> Option<u64> {
281        self.as_integer().and_then(|i| u64::try_from(i).ok())
282    }
283
284    /// Cast the value to `isize`.
285    ///
286    /// If the value is a Lua [`Integer`], try to convert it to `isize` or return `None` otherwise.
287    #[inline]
288    pub fn as_isize(&self) -> Option<isize> {
289        self.as_integer().and_then(|i| isize::try_from(i).ok())
290    }
291
292    /// Cast the value to `usize`.
293    ///
294    /// If the value is a Lua [`Integer`], try to convert it to `usize` or return `None` otherwise.
295    #[inline]
296    pub fn as_usize(&self) -> Option<usize> {
297        self.as_integer().and_then(|i| usize::try_from(i).ok())
298    }
299
300    /// Returns `true` if the value is a Lua [`Number`].
301    #[inline]
302    pub fn is_number(&self) -> bool {
303        self.as_number().is_some()
304    }
305
306    /// Cast the value to [`Number`].
307    ///
308    /// If the value is a Lua [`Number`], returns it or `None` otherwise.
309    #[inline]
310    pub fn as_number(&self) -> Option<Number> {
311        match *self {
312            Value::Number(n) => Some(n),
313            _ => None,
314        }
315    }
316
317    /// Cast the value to `f32`.
318    ///
319    /// If the value is a Lua [`Number`], try to convert it to `f32` or return `None` otherwise.
320    #[inline]
321    pub fn as_f32(&self) -> Option<f32> {
322        self.as_number().and_then(f32::from_f64)
323    }
324
325    /// Cast the value to `f64`.
326    ///
327    /// If the value is a Lua [`Number`], try to convert it to `f64` or return `None` otherwise.
328    #[inline]
329    pub fn as_f64(&self) -> Option<f64> {
330        self.as_number()
331    }
332
333    /// Returns `true` if the value is a [`LuaString`].
334    #[inline]
335    pub fn is_string(&self) -> bool {
336        self.as_string().is_some()
337    }
338
339    /// Cast the value to a [`LuaString`].
340    ///
341    /// If the value is a [`LuaString`], returns it or `None` otherwise.
342    #[inline]
343    pub fn as_string(&self) -> Option<&LuaString> {
344        match self {
345            Value::String(s) => Some(s),
346            _ => None,
347        }
348    }
349
350    /// Returns `true` if the value is a Lua [`Table`].
351    #[inline]
352    pub fn is_table(&self) -> bool {
353        self.as_table().is_some()
354    }
355
356    /// Cast the value to [`Table`].
357    ///
358    /// If the value is a Lua [`Table`], returns it or `None` otherwise.
359    #[inline]
360    pub fn as_table(&self) -> Option<&Table> {
361        match self {
362            Value::Table(t) => Some(t),
363            _ => None,
364        }
365    }
366
367    /// Returns `true` if the value is a Lua [`Thread`].
368    #[inline]
369    pub fn is_thread(&self) -> bool {
370        self.as_thread().is_some()
371    }
372
373    /// Cast the value to [`Thread`].
374    ///
375    /// If the value is a Lua [`Thread`], returns it or `None` otherwise.
376    #[inline]
377    pub fn as_thread(&self) -> Option<&Thread> {
378        match self {
379            Value::Thread(t) => Some(t),
380            _ => None,
381        }
382    }
383
384    /// Returns `true` if the value is a Lua [`Function`].
385    #[inline]
386    pub fn is_function(&self) -> bool {
387        self.as_function().is_some()
388    }
389
390    /// Cast the value to [`Function`].
391    ///
392    /// If the value is a Lua [`Function`], returns it or `None` otherwise.
393    #[inline]
394    pub fn as_function(&self) -> Option<&Function> {
395        match self {
396            Value::Function(f) => Some(f),
397            _ => None,
398        }
399    }
400
401    /// Returns `true` if the value is an [`AnyUserData`].
402    #[inline]
403    pub fn is_userdata(&self) -> bool {
404        self.as_userdata().is_some()
405    }
406
407    /// Cast the value to [`AnyUserData`].
408    ///
409    /// If the value is an [`AnyUserData`], returns it or `None` otherwise.
410    #[inline]
411    pub fn as_userdata(&self) -> Option<&AnyUserData> {
412        match self {
413            Value::UserData(ud) => Some(ud),
414            _ => None,
415        }
416    }
417
418    /// Cast the value to a [`Vector`].
419    ///
420    /// If the value is a [`Vector`], returns it or `None` otherwise.
421    ///
422    /// [`Vector`]: crate::Vector
423    #[cfg(any(feature = "luau", doc))]
424    #[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
425    #[inline]
426    pub fn as_vector(&self) -> Option<crate::Vector> {
427        match self {
428            Value::Vector(v) => Some(*v),
429            _ => None,
430        }
431    }
432
433    /// Returns `true` if the value is a [`Vector`].
434    ///
435    /// [`Vector`]: crate::Vector
436    #[cfg(any(feature = "luau", doc))]
437    #[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
438    #[inline]
439    pub fn is_vector(&self) -> bool {
440        self.as_vector().is_some()
441    }
442
443    /// Cast the value to a [`Buffer`].
444    ///
445    /// If the value is [`Buffer`], returns it or `None` otherwise.
446    ///
447    /// [`Buffer`]: crate::Buffer
448    #[cfg(any(feature = "luau", doc))]
449    #[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
450    #[inline]
451    pub fn as_buffer(&self) -> Option<&crate::Buffer> {
452        match self {
453            Value::Buffer(b) => Some(b),
454            _ => None,
455        }
456    }
457
458    /// Returns `true` if the value is a [`Buffer`].
459    ///
460    /// [`Buffer`]: crate::Buffer
461    #[cfg(any(feature = "luau", doc))]
462    #[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
463    #[inline]
464    pub fn is_buffer(&self) -> bool {
465        self.as_buffer().is_some()
466    }
467
468    /// Returns `true` if the value is an [`Error`].
469    #[inline]
470    pub fn is_error(&self) -> bool {
471        self.as_error().is_some()
472    }
473
474    /// Cast the value to [`Error`].
475    ///
476    /// If the value is an [`Error`], returns it or `None` otherwise.
477    pub fn as_error(&self) -> Option<&Error> {
478        match self {
479            Value::Error(e) => Some(e),
480            _ => None,
481        }
482    }
483
484    /// Wrap reference to this Value into [`SerializableValue`].
485    ///
486    /// This allows customizing serialization behavior using serde.
487    #[cfg(feature = "serde")]
488    #[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
489    pub fn to_serializable(&self) -> SerializableValue<'_> {
490        SerializableValue::new(self, Default::default(), None)
491    }
492
493    // Compares two values.
494    // Used to sort values for Debug printing.
495    pub(crate) fn sort_cmp(&self, other: &Self) -> Ordering {
496        fn cmp_integer_number(a: Integer, b: Number) -> Ordering {
497            match (a as Number).partial_cmp(&b) {
498                // Integer::MAX can round up to a float outside the integer ranges
499                Some(Ordering::Equal) => Integer::from_f64(b).map_or(Ordering::Less, |b| a.cmp(&b)),
500                ordering => ordering.unwrap_or(Ordering::Equal),
501            }
502        }
503
504        match (self, other) {
505            // Nil
506            (Value::Nil, Value::Nil) => Ordering::Equal,
507            (Value::Nil, _) => Ordering::Less,
508            (_, Value::Nil) => Ordering::Greater,
509            // Null (a special case)
510            (Value::LightUserData(ud1), Value::LightUserData(ud2)) if ud1 == ud2 => Ordering::Equal,
511            (Value::LightUserData(ud1), _) if ud1.0.is_null() => Ordering::Less,
512            (_, Value::LightUserData(ud2)) if ud2.0.is_null() => Ordering::Greater,
513            // Boolean
514            (Value::Boolean(a), Value::Boolean(b)) => a.cmp(b),
515            (Value::Boolean(_), _) => Ordering::Less,
516            (_, Value::Boolean(_)) => Ordering::Greater,
517            // Integer && Number
518            (Value::Integer(a), Value::Integer(b)) => a.cmp(b),
519            (Value::Integer(a), Value::Number(b)) => cmp_integer_number(*a, *b),
520            (Value::Number(a), Value::Integer(b)) => cmp_integer_number(*b, *a).reverse(),
521            (Value::Number(a), Value::Number(b)) => a.partial_cmp(b).unwrap_or(Ordering::Equal),
522            (Value::Integer(_) | Value::Number(_), _) => Ordering::Less,
523            (_, Value::Integer(_) | Value::Number(_)) => Ordering::Greater,
524            // Vector (Luau)
525            #[cfg(feature = "luau")]
526            (Value::Vector(a), Value::Vector(b)) => a.partial_cmp(b).unwrap_or(Ordering::Equal),
527            // String
528            (Value::String(a), Value::String(b)) => a.as_bytes().cmp(&b.as_bytes()),
529            (Value::String(_), _) => Ordering::Less,
530            (_, Value::String(_)) => Ordering::Greater,
531            // Other variants can be ordered by their pointer
532            (a, b) => a.to_pointer().cmp(&b.to_pointer()),
533        }
534    }
535
536    pub(crate) fn fmt_pretty(
537        &self,
538        fmt: &mut fmt::Formatter,
539        recursive: bool,
540        ident: usize,
541        visited: &mut HashSet<*const c_void>,
542    ) -> fmt::Result {
543        match self {
544            Value::Nil => write!(fmt, "nil"),
545            Value::Boolean(b) => write!(fmt, "{b}"),
546            Value::LightUserData(ud) if ud.0.is_null() => write!(fmt, "null"),
547            Value::LightUserData(ud) => write!(fmt, "lightuserdata: {:?}", ud.0),
548            Value::Integer(i) => write!(fmt, "{i}"),
549            Value::Number(n) => write!(fmt, "{n}"),
550            #[cfg(feature = "luau")]
551            Value::Vector(v) => write!(fmt, "{v}"),
552            Value::String(s) => write!(fmt, "{s:?}"),
553            Value::Table(t) if recursive && !visited.contains(&t.to_pointer()) => {
554                t.fmt_pretty(fmt, ident, visited)
555            }
556            t @ Value::Table(_) => write!(fmt, "table: {:?}", t.to_pointer()),
557            f @ Value::Function(_) => write!(fmt, "function: {:?}", f.to_pointer()),
558            t @ Value::Thread(_) => write!(fmt, "thread: {:?}", t.to_pointer()),
559            Value::UserData(ud) => ud.fmt_pretty(fmt),
560            #[cfg(feature = "luau")]
561            buf @ Value::Buffer(_) => write!(fmt, "buffer: {:?}", buf.to_pointer()),
562            Value::Error(e) if recursive => write!(fmt, "{e:?}"),
563            Value::Error(_) => write!(fmt, "error"),
564            Value::Other(v) => write!(fmt, "other: {:?}", v.to_pointer()),
565        }
566    }
567}
568
569impl fmt::Debug for Value {
570    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
571        if fmt.alternate() {
572            return self.fmt_pretty(fmt, true, 0, &mut HashSet::new());
573        }
574
575        match self {
576            Value::Nil => write!(fmt, "Nil"),
577            Value::Boolean(b) => write!(fmt, "Boolean({b})"),
578            Value::LightUserData(ud) => write!(fmt, "{ud:?}"),
579            Value::Integer(i) => write!(fmt, "Integer({i})"),
580            Value::Number(n) => write!(fmt, "Number({n})"),
581            #[cfg(feature = "luau")]
582            Value::Vector(v) => write!(fmt, "{v:?}"),
583            Value::String(s) => write!(fmt, "String({s:?})"),
584            Value::Table(t) => write!(fmt, "{t:?}"),
585            Value::Function(f) => write!(fmt, "{f:?}"),
586            Value::Thread(t) => write!(fmt, "{t:?}"),
587            Value::UserData(ud) => write!(fmt, "{ud:?}"),
588            #[cfg(feature = "luau")]
589            Value::Buffer(buf) => write!(fmt, "{buf:?}"),
590            Value::Error(e) => write!(fmt, "Error({e:?})"),
591            Value::Other(v) => write!(fmt, "Other({v:?})"),
592        }
593    }
594}
595
596impl PartialEq for Value {
597    fn eq(&self, other: &Self) -> bool {
598        match (self, other) {
599            (Value::Nil, Value::Nil) => true,
600            (Value::Boolean(a), Value::Boolean(b)) => a == b,
601            (Value::LightUserData(a), Value::LightUserData(b)) => a == b,
602            (Value::Integer(a), Value::Integer(b)) => *a == *b,
603            (Value::Integer(a), Value::Number(b)) | (Value::Number(b), Value::Integer(a)) => {
604                *a as Number == *b && Integer::from_f64(*b) == Some(*a)
605            }
606            (Value::Number(a), Value::Number(b)) => *a == *b,
607            #[cfg(feature = "luau")]
608            (Value::Vector(v1), Value::Vector(v2)) => v1 == v2,
609            (Value::String(a), Value::String(b)) => a == b,
610            (Value::Table(a), Value::Table(b)) => a == b,
611            (Value::Function(a), Value::Function(b)) => a == b,
612            (Value::Thread(a), Value::Thread(b)) => a == b,
613            (Value::UserData(a), Value::UserData(b)) => a == b,
614            #[cfg(feature = "luau")]
615            (Value::Buffer(a), Value::Buffer(b)) => a == b,
616            _ => false,
617        }
618    }
619}
620
621/// A wrapped [`Value`] with customized serialization behavior.
622#[cfg(feature = "serde")]
623#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
624pub struct SerializableValue<'a> {
625    value: &'a Value,
626    options: crate::serde::de::Options,
627    // In many cases we don't need `visited` map, so don't allocate memory by default
628    visited: Option<Rc<RefCell<FxHashSet<*const c_void>>>>,
629}
630
631#[cfg(feature = "serde")]
632impl Serialize for Value {
633    #[inline]
634    fn serialize<S: Serializer>(&self, serializer: S) -> StdResult<S::Ok, S::Error> {
635        SerializableValue::new(self, Default::default(), None).serialize(serializer)
636    }
637}
638
639#[cfg(feature = "serde")]
640impl<'a> SerializableValue<'a> {
641    #[inline]
642    pub(crate) fn new(
643        value: &'a Value,
644        options: crate::serde::de::Options,
645        visited: Option<&Rc<RefCell<FxHashSet<*const c_void>>>>,
646    ) -> Self {
647        if let Value::Table(_) = value {
648            return Self {
649                value,
650                options,
651                // We need to always initialize the `visited` map for Tables
652                visited: visited.cloned().or_else(|| Some(Default::default())),
653            };
654        }
655        Self {
656            value,
657            options,
658            visited: None,
659        }
660    }
661
662    /// Sets the maximum table nesting depth. Default: **128**.
663    ///
664    /// Increasing this limit may require a larger thread stack. Zero rejects all tables.
665    #[must_use]
666    pub fn recursion_limit(mut self, limit: usize) -> Self {
667        self.options.recursion_limit = limit;
668        self
669    }
670
671    /// If true, an attempt to serialize types such as [`Function`], [`Thread`], [`LightUserData`]
672    /// and [`Error`] will cause an error.
673    /// Otherwise these types skipped when iterating or serialized as unit type.
674    ///
675    /// Default: **true**
676    #[must_use]
677    pub fn deny_unsupported_types(mut self, enabled: bool) -> Self {
678        self.options.deny_unsupported_types = enabled;
679        self
680    }
681
682    /// If true, an attempt to serialize a recursive table (table that refers to itself)
683    /// will cause an error.
684    /// Otherwise subsequent attempts to serialize the same table will be ignored.
685    ///
686    /// Default: **true**
687    #[must_use]
688    pub fn deny_recursive_tables(mut self, enabled: bool) -> Self {
689        self.options.deny_recursive_tables = enabled;
690        self
691    }
692
693    /// If true, keys in tables will be iterated (and serialized) in sorted order.
694    ///
695    /// Default: **false**
696    #[must_use]
697    pub fn sort_keys(mut self, enabled: bool) -> Self {
698        self.options.sort_keys = enabled;
699        self
700    }
701
702    /// If true, empty Lua tables will be encoded as array, instead of map.
703    ///
704    /// Default: **false**
705    #[must_use]
706    pub fn encode_empty_tables_as_array(mut self, enabled: bool) -> Self {
707        self.options.encode_empty_tables_as_array = enabled;
708        self
709    }
710
711    /// If true, enable detection of mixed tables.
712    ///
713    /// A mixed table is a table that has both array-like and map-like entries or several borders.
714    ///
715    /// Default: **false**
716    #[must_use]
717    pub fn detect_mixed_tables(mut self, enabled: bool) -> Self {
718        self.options.detect_mixed_tables = enabled;
719        self
720    }
721}
722
723#[cfg(feature = "serde")]
724impl Serialize for SerializableValue<'_> {
725    fn serialize<S>(&self, serializer: S) -> StdResult<S::Ok, S::Error>
726    where
727        S: Serializer,
728    {
729        match self.value {
730            Value::Nil => serializer.serialize_unit(),
731            Value::Boolean(b) => serializer.serialize_bool(*b),
732            #[allow(clippy::useless_conversion)]
733            Value::Integer(i) => serializer.serialize_i64((*i).into()),
734            Value::Number(n) => serializer.serialize_f64(*n),
735            #[cfg(feature = "luau")]
736            Value::Vector(v) => v.serialize(serializer),
737            Value::String(s) => s.serialize(serializer),
738            Value::Table(t) => {
739                let visited = self.visited.as_ref().unwrap().clone();
740                SerializableTable::new(t, self.options, visited).serialize(serializer)
741            }
742            Value::LightUserData(ud) if ud.0.is_null() => serializer.serialize_none(),
743            Value::UserData(ud) if ud.is_serializable() || self.options.deny_unsupported_types => {
744                ud.serialize(serializer)
745            }
746            #[cfg(feature = "luau")]
747            Value::Buffer(buf) => buf.serialize(serializer),
748            Value::Function(_)
749            | Value::Thread(_)
750            | Value::UserData(_)
751            | Value::LightUserData(_)
752            | Value::Error(_)
753            | Value::Other(_) => {
754                if self.options.deny_unsupported_types {
755                    let msg = format!("cannot serialize <{}>", self.value.type_name());
756                    Err(ser::Error::custom(msg))
757                } else {
758                    serializer.serialize_unit()
759                }
760            }
761        }
762    }
763}
764
765#[cfg(test)]
766mod assertions {
767    use super::*;
768
769    #[cfg(not(feature = "send"))]
770    static_assertions::assert_not_impl_any!(Value: Send);
771    #[cfg(feature = "send")]
772    static_assertions::assert_impl_all!(Value: Send, Sync);
773}