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#[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::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 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 #[inline]
185 pub fn is_nil(&self) -> bool {
186 self == &Nil
187 }
188
189 #[inline]
193 pub fn is_null(&self) -> bool {
194 self == &Self::NULL
195 }
196
197 #[inline]
199 pub fn is_boolean(&self) -> bool {
200 self.as_boolean().is_some()
201 }
202
203 #[inline]
207 pub fn as_boolean(&self) -> Option<bool> {
208 match *self {
209 Value::Boolean(b) => Some(b),
210 _ => None,
211 }
212 }
213
214 #[inline]
216 pub fn is_light_userdata(&self) -> bool {
217 self.as_light_userdata().is_some()
218 }
219
220 #[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 #[inline]
233 pub fn is_integer(&self) -> bool {
234 self.as_integer().is_some()
235 }
236
237 #[inline]
241 pub fn as_integer(&self) -> Option<Integer> {
242 match *self {
243 Value::Integer(i) => Some(i),
244 _ => None,
245 }
246 }
247
248 #[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 #[inline]
261 pub fn as_u32(&self) -> Option<u32> {
262 self.as_integer().and_then(|i| u32::try_from(i).ok())
263 }
264
265 #[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 #[inline]
280 pub fn as_u64(&self) -> Option<u64> {
281 self.as_integer().and_then(|i| u64::try_from(i).ok())
282 }
283
284 #[inline]
288 pub fn as_isize(&self) -> Option<isize> {
289 self.as_integer().and_then(|i| isize::try_from(i).ok())
290 }
291
292 #[inline]
296 pub fn as_usize(&self) -> Option<usize> {
297 self.as_integer().and_then(|i| usize::try_from(i).ok())
298 }
299
300 #[inline]
302 pub fn is_number(&self) -> bool {
303 self.as_number().is_some()
304 }
305
306 #[inline]
310 pub fn as_number(&self) -> Option<Number> {
311 match *self {
312 Value::Number(n) => Some(n),
313 _ => None,
314 }
315 }
316
317 #[inline]
321 pub fn as_f32(&self) -> Option<f32> {
322 self.as_number().and_then(f32::from_f64)
323 }
324
325 #[inline]
329 pub fn as_f64(&self) -> Option<f64> {
330 self.as_number()
331 }
332
333 #[inline]
335 pub fn is_string(&self) -> bool {
336 self.as_string().is_some()
337 }
338
339 #[inline]
343 pub fn as_string(&self) -> Option<&LuaString> {
344 match self {
345 Value::String(s) => Some(s),
346 _ => None,
347 }
348 }
349
350 #[inline]
352 pub fn is_table(&self) -> bool {
353 self.as_table().is_some()
354 }
355
356 #[inline]
360 pub fn as_table(&self) -> Option<&Table> {
361 match self {
362 Value::Table(t) => Some(t),
363 _ => None,
364 }
365 }
366
367 #[inline]
369 pub fn is_thread(&self) -> bool {
370 self.as_thread().is_some()
371 }
372
373 #[inline]
377 pub fn as_thread(&self) -> Option<&Thread> {
378 match self {
379 Value::Thread(t) => Some(t),
380 _ => None,
381 }
382 }
383
384 #[inline]
386 pub fn is_function(&self) -> bool {
387 self.as_function().is_some()
388 }
389
390 #[inline]
394 pub fn as_function(&self) -> Option<&Function> {
395 match self {
396 Value::Function(f) => Some(f),
397 _ => None,
398 }
399 }
400
401 #[inline]
403 pub fn is_userdata(&self) -> bool {
404 self.as_userdata().is_some()
405 }
406
407 #[inline]
411 pub fn as_userdata(&self) -> Option<&AnyUserData> {
412 match self {
413 Value::UserData(ud) => Some(ud),
414 _ => None,
415 }
416 }
417
418 #[cfg(any(feature = "luau", doc))]
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 #[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 #[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 #[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 #[inline]
470 pub fn is_error(&self) -> bool {
471 self.as_error().is_some()
472 }
473
474 pub fn as_error(&self) -> Option<&Error> {
478 match self {
479 Value::Error(e) => Some(e),
480 _ => None,
481 }
482 }
483
484 #[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 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 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 (Value::Nil, Value::Nil) => Ordering::Equal,
507 (Value::Nil, _) => Ordering::Less,
508 (_, Value::Nil) => Ordering::Greater,
509 (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 (Value::Boolean(a), Value::Boolean(b)) => a.cmp(b),
515 (Value::Boolean(_), _) => Ordering::Less,
516 (_, Value::Boolean(_)) => Ordering::Greater,
517 (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 #[cfg(feature = "luau")]
526 (Value::Vector(a), Value::Vector(b)) => a.partial_cmp(b).unwrap_or(Ordering::Equal),
527 (Value::String(a), Value::String(b)) => a.as_bytes().cmp(&b.as_bytes()),
529 (Value::String(_), _) => Ordering::Less,
530 (_, Value::String(_)) => Ordering::Greater,
531 (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#[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 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 visited: visited.cloned().or_else(|| Some(Default::default())),
653 };
654 }
655 Self {
656 value,
657 options,
658 visited: None,
659 }
660 }
661
662 #[must_use]
666 pub fn recursion_limit(mut self, limit: usize) -> Self {
667 self.options.recursion_limit = limit;
668 self
669 }
670
671 #[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 #[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 #[must_use]
697 pub fn sort_keys(mut self, enabled: bool) -> Self {
698 self.options.sort_keys = enabled;
699 self
700 }
701
702 #[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 #[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}