mlua/
value.rs

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