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#[derive(Clone, Default)]
32pub enum Value {
33 #[default]
35 Nil,
36 Boolean(bool),
38 LightUserData(LightUserData),
40 Integer(Integer),
44 Number(Number),
46 #[cfg(any(feature = "luau", doc))]
48 #[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
49 Vector(crate::Vector),
50 String(String),
54 Table(Table),
56 Function(Function),
58 Thread(Thread),
60 UserData(AnyUserData),
64 #[cfg(any(feature = "luau", doc))]
66 #[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
67 Buffer(crate::Buffer),
68 Error(Box<Error>),
70 Other(#[doc(hidden)] ValueRef),
72}
73
74pub use self::Value::Nil;
75
76impl Value {
77 pub const NULL: Value = Value::LightUserData(LightUserData(ptr::null_mut()));
81
82 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 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 #[inline]
130 pub fn to_pointer(&self) -> *const c_void {
131 match self {
132 Value::String(String(vref)) => {
133 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 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 #[inline]
191 pub fn is_nil(&self) -> bool {
192 self == &Nil
193 }
194
195 #[inline]
199 pub fn is_null(&self) -> bool {
200 self == &Self::NULL
201 }
202
203 #[inline]
205 pub fn is_boolean(&self) -> bool {
206 self.as_boolean().is_some()
207 }
208
209 #[inline]
213 pub fn as_boolean(&self) -> Option<bool> {
214 match *self {
215 Value::Boolean(b) => Some(b),
216 _ => None,
217 }
218 }
219
220 #[inline]
222 pub fn is_light_userdata(&self) -> bool {
223 self.as_light_userdata().is_some()
224 }
225
226 #[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 #[inline]
239 pub fn is_integer(&self) -> bool {
240 self.as_integer().is_some()
241 }
242
243 #[inline]
247 pub fn as_integer(&self) -> Option<Integer> {
248 match *self {
249 Value::Integer(i) => Some(i),
250 _ => None,
251 }
252 }
253
254 #[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 #[inline]
267 pub fn as_u32(&self) -> Option<u32> {
268 self.as_integer().and_then(|i| u32::try_from(i).ok())
269 }
270
271 #[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 #[inline]
286 pub fn as_u64(&self) -> Option<u64> {
287 self.as_integer().and_then(|i| u64::try_from(i).ok())
288 }
289
290 #[inline]
294 pub fn as_isize(&self) -> Option<isize> {
295 self.as_integer().and_then(|i| isize::try_from(i).ok())
296 }
297
298 #[inline]
302 pub fn as_usize(&self) -> Option<usize> {
303 self.as_integer().and_then(|i| usize::try_from(i).ok())
304 }
305
306 #[inline]
308 pub fn is_number(&self) -> bool {
309 self.as_number().is_some()
310 }
311
312 #[inline]
316 pub fn as_number(&self) -> Option<Number> {
317 match *self {
318 Value::Number(n) => Some(n),
319 _ => None,
320 }
321 }
322
323 #[inline]
327 pub fn as_f32(&self) -> Option<f32> {
328 self.as_number().and_then(f32::from_f64)
329 }
330
331 #[inline]
335 pub fn as_f64(&self) -> Option<f64> {
336 self.as_number()
337 }
338
339 #[inline]
341 pub fn is_string(&self) -> bool {
342 self.as_string().is_some()
343 }
344
345 #[inline]
349 pub fn as_string(&self) -> Option<&String> {
350 match self {
351 Value::String(s) => Some(s),
352 _ => None,
353 }
354 }
355
356 #[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 #[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 #[inline]
383 pub fn is_table(&self) -> bool {
384 self.as_table().is_some()
385 }
386
387 #[inline]
391 pub fn as_table(&self) -> Option<&Table> {
392 match self {
393 Value::Table(t) => Some(t),
394 _ => None,
395 }
396 }
397
398 #[inline]
400 pub fn is_thread(&self) -> bool {
401 self.as_thread().is_some()
402 }
403
404 #[inline]
408 pub fn as_thread(&self) -> Option<&Thread> {
409 match self {
410 Value::Thread(t) => Some(t),
411 _ => None,
412 }
413 }
414
415 #[inline]
417 pub fn is_function(&self) -> bool {
418 self.as_function().is_some()
419 }
420
421 #[inline]
425 pub fn as_function(&self) -> Option<&Function> {
426 match self {
427 Value::Function(f) => Some(f),
428 _ => None,
429 }
430 }
431
432 #[inline]
434 pub fn is_userdata(&self) -> bool {
435 self.as_userdata().is_some()
436 }
437
438 #[inline]
442 pub fn as_userdata(&self) -> Option<&AnyUserData> {
443 match self {
444 Value::UserData(ud) => Some(ud),
445 _ => None,
446 }
447 }
448
449 #[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 #[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 #[inline]
476 pub fn is_error(&self) -> bool {
477 self.as_error().is_some()
478 }
479
480 pub fn as_error(&self) -> Option<&Error> {
484 match self {
485 Value::Error(e) => Some(e),
486 _ => None,
487 }
488 }
489
490 #[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 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 (Value::Nil, Value::Nil) => Ordering::Equal,
513 (Value::Nil, _) => Ordering::Less,
514 (_, Value::Nil) => Ordering::Greater,
515 (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 (Value::Boolean(a), Value::Boolean(b)) => a.cmp(b),
521 (Value::Boolean(_), _) => Ordering::Less,
522 (_, Value::Boolean(_)) => Ordering::Greater,
523 (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 #[cfg(feature = "luau")]
532 (Value::Vector(a), Value::Vector(b)) => a.partial_cmp(b).unwrap_or(Ordering::Equal),
533 (Value::String(a), Value::String(b)) => a.as_bytes().cmp(&b.as_bytes()),
535 (Value::String(_), _) => Ordering::Less,
536 (_, Value::String(_)) => Ordering::Greater,
537 (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 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#[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 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 visited: visited.cloned().or_else(|| Some(Default::default())),
666 };
667 }
668 Self {
669 value,
670 options,
671 visited: None,
672 }
673 }
674
675 #[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 #[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 #[must_use]
701 pub fn sort_keys(mut self, enabled: bool) -> Self {
702 self.options.sort_keys = enabled;
703 self
704 }
705
706 #[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 #[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}