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::{BorrowedStr, 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#[derive(Clone, Default)]
31pub enum Value {
32 #[default]
34 Nil,
35 Boolean(bool),
37 LightUserData(LightUserData),
39 Integer(Integer),
43 Number(Number),
45 #[cfg(any(feature = "luau", doc))]
47 #[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
48 Vector(crate::Vector),
49 String(LuaString),
53 Table(Table),
55 Function(Function),
57 Thread(Thread),
59 UserData(AnyUserData),
63 #[cfg(any(feature = "luau", doc))]
65 #[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
66 Buffer(crate::Buffer),
67 Error(Box<Error>),
69 Other(#[doc(hidden)] ValueRef),
71}
72
73pub use self::Value::Nil;
74
75impl Value {
76 pub const NULL: Value = Value::LightUserData(LightUserData(ptr::null_mut()));
80
81 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 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 #[inline]
129 pub fn to_pointer(&self) -> *const c_void {
130 match self {
131 Value::String(LuaString(vref)) => {
132 let lua = vref.lua.lock();
135 unsafe { ffi::lua_tostring(lua.ref_thread(), vref.index) as *const c_void }
136 }
137 Value::LightUserData(ud) => ud.0,
138 Value::Table(Table(vref))
139 | Value::Function(Function(vref))
140 | Value::Thread(Thread(vref, ..))
141 | Value::UserData(AnyUserData(vref))
142 | Value::Other(vref) => vref.to_pointer(),
143 #[cfg(feature = "luau")]
144 Value::Buffer(crate::Buffer(vref)) => vref.to_pointer(),
145 _ => ptr::null(),
146 }
147 }
148
149 pub fn to_string(&self) -> Result<String> {
154 unsafe fn invoke_tostring(vref: &ValueRef) -> Result<String> {
155 let lua = vref.lua.lock();
156 let state = lua.state();
157 let _guard = StackGuard::new(state);
158 check_stack(state, 3)?;
159
160 lua.push_ref(vref);
161 protect_lua!(state, 1, 1, fn(state) {
162 ffi::luaL_tolstring(state, -1, ptr::null_mut());
163 })?;
164 Ok(LuaString(lua.pop_ref()).to_str()?.to_string())
165 }
166
167 match self {
168 Value::Nil => Ok("nil".to_string()),
169 Value::Boolean(b) => Ok(b.to_string()),
170 Value::LightUserData(ud) if ud.0.is_null() => Ok("null".to_string()),
171 Value::LightUserData(ud) => Ok(format!("lightuserdata: {:p}", ud.0)),
172 Value::Integer(i) => Ok(i.to_string()),
173 Value::Number(n) => Ok(n.to_string()),
174 #[cfg(feature = "luau")]
175 Value::Vector(v) => Ok(v.to_string()),
176 Value::String(s) => Ok(s.to_str()?.to_string()),
177 Value::Table(Table(vref))
178 | Value::Function(Function(vref))
179 | Value::Thread(Thread(vref, ..))
180 | Value::UserData(AnyUserData(vref))
181 | Value::Other(vref) => unsafe { invoke_tostring(vref) },
182 #[cfg(feature = "luau")]
183 Value::Buffer(crate::Buffer(vref)) => unsafe { invoke_tostring(vref) },
184 Value::Error(err) => Ok(err.to_string()),
185 }
186 }
187
188 #[inline]
190 pub fn is_nil(&self) -> bool {
191 self == &Nil
192 }
193
194 #[inline]
198 pub fn is_null(&self) -> bool {
199 self == &Self::NULL
200 }
201
202 #[inline]
204 pub fn is_boolean(&self) -> bool {
205 self.as_boolean().is_some()
206 }
207
208 #[inline]
212 pub fn as_boolean(&self) -> Option<bool> {
213 match *self {
214 Value::Boolean(b) => Some(b),
215 _ => None,
216 }
217 }
218
219 #[inline]
221 pub fn is_light_userdata(&self) -> bool {
222 self.as_light_userdata().is_some()
223 }
224
225 #[inline]
229 pub fn as_light_userdata(&self) -> Option<LightUserData> {
230 match *self {
231 Value::LightUserData(l) => Some(l),
232 _ => None,
233 }
234 }
235
236 #[inline]
238 pub fn is_integer(&self) -> bool {
239 self.as_integer().is_some()
240 }
241
242 #[inline]
246 pub fn as_integer(&self) -> Option<Integer> {
247 match *self {
248 Value::Integer(i) => Some(i),
249 _ => None,
250 }
251 }
252
253 #[inline]
257 pub fn as_i32(&self) -> Option<i32> {
258 #[allow(clippy::useless_conversion)]
259 self.as_integer().and_then(|i| i32::try_from(i).ok())
260 }
261
262 #[inline]
266 pub fn as_u32(&self) -> Option<u32> {
267 self.as_integer().and_then(|i| u32::try_from(i).ok())
268 }
269
270 #[inline]
274 pub fn as_i64(&self) -> Option<i64> {
275 #[cfg(target_pointer_width = "64")]
276 return self.as_integer();
277 #[cfg(not(target_pointer_width = "64"))]
278 return self.as_integer().map(i64::from);
279 }
280
281 #[inline]
285 pub fn as_u64(&self) -> Option<u64> {
286 self.as_integer().and_then(|i| u64::try_from(i).ok())
287 }
288
289 #[inline]
293 pub fn as_isize(&self) -> Option<isize> {
294 self.as_integer().and_then(|i| isize::try_from(i).ok())
295 }
296
297 #[inline]
301 pub fn as_usize(&self) -> Option<usize> {
302 self.as_integer().and_then(|i| usize::try_from(i).ok())
303 }
304
305 #[inline]
307 pub fn is_number(&self) -> bool {
308 self.as_number().is_some()
309 }
310
311 #[inline]
315 pub fn as_number(&self) -> Option<Number> {
316 match *self {
317 Value::Number(n) => Some(n),
318 _ => None,
319 }
320 }
321
322 #[inline]
326 pub fn as_f32(&self) -> Option<f32> {
327 self.as_number().and_then(f32::from_f64)
328 }
329
330 #[inline]
334 pub fn as_f64(&self) -> Option<f64> {
335 self.as_number()
336 }
337
338 #[inline]
340 pub fn is_string(&self) -> bool {
341 self.as_string().is_some()
342 }
343
344 #[inline]
348 pub fn as_string(&self) -> Option<&LuaString> {
349 match self {
350 Value::String(s) => Some(s),
351 _ => None,
352 }
353 }
354
355 #[deprecated(
360 since = "0.11.0",
361 note = "This method does not follow Rust naming convention. Use `as_string().and_then(|s| s.to_str().ok())` instead."
362 )]
363 #[inline]
364 pub fn as_str(&self) -> Option<BorrowedStr> {
365 self.as_string().and_then(|s| s.to_str().ok())
366 }
367
368 #[deprecated(
372 since = "0.11.0",
373 note = "This method does not follow Rust naming convention. Use `as_string().map(|s| s.to_string_lossy())` instead."
374 )]
375 #[inline]
376 pub fn as_string_lossy(&self) -> Option<String> {
377 self.as_string().map(|s| s.to_string_lossy())
378 }
379
380 #[inline]
382 pub fn is_table(&self) -> bool {
383 self.as_table().is_some()
384 }
385
386 #[inline]
390 pub fn as_table(&self) -> Option<&Table> {
391 match self {
392 Value::Table(t) => Some(t),
393 _ => None,
394 }
395 }
396
397 #[inline]
399 pub fn is_thread(&self) -> bool {
400 self.as_thread().is_some()
401 }
402
403 #[inline]
407 pub fn as_thread(&self) -> Option<&Thread> {
408 match self {
409 Value::Thread(t) => Some(t),
410 _ => None,
411 }
412 }
413
414 #[inline]
416 pub fn is_function(&self) -> bool {
417 self.as_function().is_some()
418 }
419
420 #[inline]
424 pub fn as_function(&self) -> Option<&Function> {
425 match self {
426 Value::Function(f) => Some(f),
427 _ => None,
428 }
429 }
430
431 #[inline]
433 pub fn is_userdata(&self) -> bool {
434 self.as_userdata().is_some()
435 }
436
437 #[inline]
441 pub fn as_userdata(&self) -> Option<&AnyUserData> {
442 match self {
443 Value::UserData(ud) => Some(ud),
444 _ => None,
445 }
446 }
447
448 #[cfg(any(feature = "luau", doc))]
454 #[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
455 #[inline]
456 pub fn as_buffer(&self) -> Option<&crate::Buffer> {
457 match self {
458 Value::Buffer(b) => Some(b),
459 _ => None,
460 }
461 }
462
463 #[cfg(any(feature = "luau", doc))]
467 #[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
468 #[inline]
469 pub fn is_buffer(&self) -> bool {
470 self.as_buffer().is_some()
471 }
472
473 #[inline]
475 pub fn is_error(&self) -> bool {
476 self.as_error().is_some()
477 }
478
479 pub fn as_error(&self) -> Option<&Error> {
483 match self {
484 Value::Error(e) => Some(e),
485 _ => None,
486 }
487 }
488
489 #[cfg(feature = "serde")]
493 #[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
494 pub fn to_serializable(&self) -> SerializableValue<'_> {
495 SerializableValue::new(self, Default::default(), None)
496 }
497
498 pub(crate) fn sort_cmp(&self, other: &Self) -> Ordering {
501 fn cmp_num(a: Number, b: Number) -> Ordering {
502 match (a, b) {
503 _ if a < b => Ordering::Less,
504 _ if a > b => Ordering::Greater,
505 _ => Ordering::Equal,
506 }
507 }
508
509 match (self, other) {
510 (Value::Nil, Value::Nil) => Ordering::Equal,
512 (Value::Nil, _) => Ordering::Less,
513 (_, Value::Nil) => Ordering::Greater,
514 (Value::LightUserData(ud1), Value::LightUserData(ud2)) if ud1 == ud2 => Ordering::Equal,
516 (Value::LightUserData(ud1), _) if ud1.0.is_null() => Ordering::Less,
517 (_, Value::LightUserData(ud2)) if ud2.0.is_null() => Ordering::Greater,
518 (Value::Boolean(a), Value::Boolean(b)) => a.cmp(b),
520 (Value::Boolean(_), _) => Ordering::Less,
521 (_, Value::Boolean(_)) => Ordering::Greater,
522 (Value::Integer(a), Value::Integer(b)) => a.cmp(b),
524 (Value::Integer(a), Value::Number(b)) => cmp_num(*a as Number, *b),
525 (Value::Number(a), Value::Integer(b)) => cmp_num(*a, *b as Number),
526 (Value::Number(a), Value::Number(b)) => cmp_num(*a, *b),
527 (Value::Integer(_) | Value::Number(_), _) => Ordering::Less,
528 (_, Value::Integer(_) | Value::Number(_)) => Ordering::Greater,
529 #[cfg(feature = "luau")]
531 (Value::Vector(a), Value::Vector(b)) => a.partial_cmp(b).unwrap_or(Ordering::Equal),
532 (Value::String(a), Value::String(b)) => a.as_bytes().cmp(&b.as_bytes()),
534 (Value::String(_), _) => Ordering::Less,
535 (_, Value::String(_)) => Ordering::Greater,
536 (a, b) => a.to_pointer().cmp(&b.to_pointer()),
538 }
539 }
540
541 pub(crate) fn fmt_pretty(
542 &self,
543 fmt: &mut fmt::Formatter,
544 recursive: bool,
545 ident: usize,
546 visited: &mut HashSet<*const c_void>,
547 ) -> fmt::Result {
548 match self {
549 Value::Nil => write!(fmt, "nil"),
550 Value::Boolean(b) => write!(fmt, "{b}"),
551 Value::LightUserData(ud) if ud.0.is_null() => write!(fmt, "null"),
552 Value::LightUserData(ud) => write!(fmt, "lightuserdata: {:?}", ud.0),
553 Value::Integer(i) => write!(fmt, "{i}"),
554 Value::Number(n) => write!(fmt, "{n}"),
555 #[cfg(feature = "luau")]
556 Value::Vector(v) => write!(fmt, "{v}"),
557 Value::String(s) => write!(fmt, "{s:?}"),
558 Value::Table(t) if recursive && !visited.contains(&t.to_pointer()) => {
559 t.fmt_pretty(fmt, ident, visited)
560 }
561 t @ Value::Table(_) => write!(fmt, "table: {:?}", t.to_pointer()),
562 f @ Value::Function(_) => write!(fmt, "function: {:?}", f.to_pointer()),
563 t @ Value::Thread(_) => write!(fmt, "thread: {:?}", t.to_pointer()),
564 Value::UserData(ud) => ud.fmt_pretty(fmt),
565 #[cfg(feature = "luau")]
566 buf @ Value::Buffer(_) => write!(fmt, "buffer: {:?}", buf.to_pointer()),
567 Value::Error(e) if recursive => write!(fmt, "{e:?}"),
568 Value::Error(_) => write!(fmt, "error"),
569 Value::Other(v) => write!(fmt, "other: {:?}", v.to_pointer()),
570 }
571 }
572}
573
574impl fmt::Debug for Value {
575 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
576 if fmt.alternate() {
577 return self.fmt_pretty(fmt, true, 0, &mut HashSet::new());
578 }
579
580 match self {
581 Value::Nil => write!(fmt, "Nil"),
582 Value::Boolean(b) => write!(fmt, "Boolean({b})"),
583 Value::LightUserData(ud) => write!(fmt, "{ud:?}"),
584 Value::Integer(i) => write!(fmt, "Integer({i})"),
585 Value::Number(n) => write!(fmt, "Number({n})"),
586 #[cfg(feature = "luau")]
587 Value::Vector(v) => write!(fmt, "{v:?}"),
588 Value::String(s) => write!(fmt, "String({s:?})"),
589 Value::Table(t) => write!(fmt, "{t:?}"),
590 Value::Function(f) => write!(fmt, "{f:?}"),
591 Value::Thread(t) => write!(fmt, "{t:?}"),
592 Value::UserData(ud) => write!(fmt, "{ud:?}"),
593 #[cfg(feature = "luau")]
594 Value::Buffer(buf) => write!(fmt, "{buf:?}"),
595 Value::Error(e) => write!(fmt, "Error({e:?})"),
596 Value::Other(v) => write!(fmt, "Other({v:?})"),
597 }
598 }
599}
600
601impl PartialEq for Value {
602 fn eq(&self, other: &Self) -> bool {
603 match (self, other) {
604 (Value::Nil, Value::Nil) => true,
605 (Value::Boolean(a), Value::Boolean(b)) => a == b,
606 (Value::LightUserData(a), Value::LightUserData(b)) => a == b,
607 (Value::Integer(a), Value::Integer(b)) => *a == *b,
608 (Value::Integer(a), Value::Number(b)) => *a as Number == *b,
609 (Value::Number(a), Value::Integer(b)) => *a == *b as Number,
610 (Value::Number(a), Value::Number(b)) => *a == *b,
611 #[cfg(feature = "luau")]
612 (Value::Vector(v1), Value::Vector(v2)) => v1 == v2,
613 (Value::String(a), Value::String(b)) => a == b,
614 (Value::Table(a), Value::Table(b)) => a == b,
615 (Value::Function(a), Value::Function(b)) => a == b,
616 (Value::Thread(a), Value::Thread(b)) => a == b,
617 (Value::UserData(a), Value::UserData(b)) => a == b,
618 #[cfg(feature = "luau")]
619 (Value::Buffer(a), Value::Buffer(b)) => a == b,
620 _ => false,
621 }
622 }
623}
624
625#[cfg(feature = "serde")]
627#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
628pub struct SerializableValue<'a> {
629 value: &'a Value,
630 options: crate::serde::de::Options,
631 visited: Option<Rc<RefCell<FxHashSet<*const c_void>>>>,
633}
634
635#[cfg(feature = "serde")]
636impl Serialize for Value {
637 #[inline]
638 fn serialize<S: Serializer>(&self, serializer: S) -> StdResult<S::Ok, S::Error> {
639 SerializableValue::new(self, Default::default(), None).serialize(serializer)
640 }
641}
642
643#[cfg(feature = "serde")]
644impl<'a> SerializableValue<'a> {
645 #[inline]
646 pub(crate) fn new(
647 value: &'a Value,
648 options: crate::serde::de::Options,
649 visited: Option<&Rc<RefCell<FxHashSet<*const c_void>>>>,
650 ) -> Self {
651 if let Value::Table(_) = value {
652 return Self {
653 value,
654 options,
655 visited: visited.cloned().or_else(|| Some(Default::default())),
657 };
658 }
659 Self {
660 value,
661 options,
662 visited: None,
663 }
664 }
665
666 #[must_use]
672 pub fn deny_unsupported_types(mut self, enabled: bool) -> Self {
673 self.options.deny_unsupported_types = enabled;
674 self
675 }
676
677 #[must_use]
683 pub fn deny_recursive_tables(mut self, enabled: bool) -> Self {
684 self.options.deny_recursive_tables = enabled;
685 self
686 }
687
688 #[must_use]
692 pub fn sort_keys(mut self, enabled: bool) -> Self {
693 self.options.sort_keys = enabled;
694 self
695 }
696
697 #[must_use]
701 pub fn encode_empty_tables_as_array(mut self, enabled: bool) -> Self {
702 self.options.encode_empty_tables_as_array = enabled;
703 self
704 }
705
706 #[must_use]
712 pub fn detect_mixed_tables(mut self, enabled: bool) -> Self {
713 self.options.detect_mixed_tables = enabled;
714 self
715 }
716}
717
718#[cfg(feature = "serde")]
719impl Serialize for SerializableValue<'_> {
720 fn serialize<S>(&self, serializer: S) -> StdResult<S::Ok, S::Error>
721 where
722 S: Serializer,
723 {
724 match self.value {
725 Value::Nil => serializer.serialize_unit(),
726 Value::Boolean(b) => serializer.serialize_bool(*b),
727 #[allow(clippy::useless_conversion)]
728 Value::Integer(i) => serializer.serialize_i64((*i).into()),
729 Value::Number(n) => serializer.serialize_f64(*n),
730 #[cfg(feature = "luau")]
731 Value::Vector(v) => v.serialize(serializer),
732 Value::String(s) => s.serialize(serializer),
733 Value::Table(t) => {
734 let visited = self.visited.as_ref().unwrap().clone();
735 SerializableTable::new(t, self.options, visited).serialize(serializer)
736 }
737 Value::LightUserData(ud) if ud.0.is_null() => serializer.serialize_none(),
738 Value::UserData(ud) if ud.is_serializable() || self.options.deny_unsupported_types => {
739 ud.serialize(serializer)
740 }
741 #[cfg(feature = "luau")]
742 Value::Buffer(buf) => buf.serialize(serializer),
743 Value::Function(_)
744 | Value::Thread(_)
745 | Value::UserData(_)
746 | Value::LightUserData(_)
747 | Value::Error(_)
748 | Value::Other(_) => {
749 if self.options.deny_unsupported_types {
750 let msg = format!("cannot serialize <{}>", self.value.type_name());
751 Err(ser::Error::custom(msg))
752 } else {
753 serializer.serialize_unit()
754 }
755 }
756 }
757 }
758}
759
760#[cfg(test)]
761mod assertions {
762 use super::*;
763
764 #[cfg(not(feature = "send"))]
765 static_assertions::assert_not_impl_any!(Value: Send);
766 #[cfg(feature = "send")]
767 static_assertions::assert_impl_all!(Value: Send, Sync);
768}