1use std::borrow::Cow;
2use std::ffi::c_int;
3use std::mem::ManuallyDrop;
4
5use lua::{ffi::*, Poppable, Pushable};
6use luajit as lua;
7
8use crate::{
9 Array,
10 Boolean,
11 Dictionary,
12 Float,
13 Function,
14 Integer,
15 LuaRef,
16 NonOwning,
17};
18
19#[repr(C)]
25pub struct Object {
26 ty: ObjectKind,
27 data: ObjectData,
28}
29
30#[derive(Copy, Clone, Debug, Eq, PartialEq)]
34#[repr(C)]
35pub enum ObjectKind {
36 Nil = 0,
37 Boolean,
38 Integer,
39 Float,
40 String,
41 Array,
42 Dictionary,
43 LuaRef,
44 Buffer,
45 Window,
46 TabPage,
47}
48
49impl ObjectKind {
50 pub fn as_static(&self) -> &'static str {
51 match self {
52 Self::Nil => "nil",
53 Self::Boolean => "boolean",
54 Self::Integer => "integer",
55 Self::Float => "float",
56 Self::String => "string",
57 Self::Array => "array",
58 Self::Dictionary => "dictionary",
59 Self::LuaRef => "luaref",
60 Self::Buffer => "buffer",
61 Self::Window => "window",
62 Self::TabPage => "tabpage",
63 }
64 }
65}
66
67#[repr(C)]
69union ObjectData {
70 boolean: Boolean,
71 integer: Integer,
72 float: Float,
73 string: ManuallyDrop<crate::String>,
74 array: ManuallyDrop<Array>,
75 dictionary: ManuallyDrop<Dictionary>,
76 luaref: LuaRef,
77}
78
79impl Default for Object {
80 fn default() -> Object {
81 Object::nil()
82 }
83}
84
85impl core::fmt::Debug for Object {
86 #[inline]
87 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
88 let field: &dyn core::fmt::Debug = match self.ty {
89 ObjectKind::Nil => &"()",
90
91 ObjectKind::Boolean => unsafe { &self.data.boolean },
92
93 ObjectKind::Integer
94 | ObjectKind::Buffer
95 | ObjectKind::Window
96 | ObjectKind::TabPage => unsafe { &self.data.integer },
97
98 ObjectKind::Float => unsafe { &self.data.float },
99
100 ObjectKind::String => unsafe { &*self.data.string },
101
102 ObjectKind::Array => unsafe { &*self.data.array },
103
104 ObjectKind::Dictionary => unsafe { &*self.data.dictionary },
105
106 ObjectKind::LuaRef => {
107 let luaref = unsafe { self.data.luaref };
108 return write!(f, "Object(LuaRef({}))", luaref);
109 },
110 };
111
112 field.fmt(f)
113 }
114}
115
116impl Object {
117 #[inline]
119 pub fn nil() -> Self {
120 Self { ty: ObjectKind::Nil, data: ObjectData { integer: 0 } }
121 }
122
123 #[inline]
124 pub fn is_nil(&self) -> bool {
125 matches!(self.ty, ObjectKind::Nil)
126 }
127
128 #[inline]
129 pub fn is_some(&self) -> bool {
130 !self.is_nil()
131 }
132
133 #[inline(always)]
134 pub fn from_luaref(luaref: LuaRef) -> Self {
135 Self { ty: ObjectKind::LuaRef, data: ObjectData { luaref } }
136 }
137
138 #[inline]
139 pub fn kind(&self) -> ObjectKind {
140 self.ty
141 }
142
143 #[inline]
145 #[doc(hidden)]
146 pub fn non_owning(&self) -> NonOwning<'_, Self> {
147 unsafe { NonOwning::new(std::ptr::read(self)) }
149 }
150
151 #[inline(always)]
155 pub unsafe fn as_boolean_unchecked(&self) -> bool {
156 self.data.boolean
157 }
158
159 #[inline(always)]
163 pub unsafe fn as_integer_unchecked(&self) -> Integer {
164 self.data.integer
165 }
166
167 #[inline(always)]
171 pub unsafe fn as_float_unchecked(&self) -> Float {
172 self.data.float
173 }
174
175 #[inline(always)]
179 pub unsafe fn as_luaref_unchecked(&self) -> LuaRef {
180 self.data.luaref
181 }
182
183 pub unsafe fn into_string_unchecked(self) -> crate::String {
190 #[allow(clippy::unnecessary_struct_initialization)]
191 let s = crate::String { ..*self.data.string };
192 core::mem::forget(self);
193 s
194 }
195
196 pub unsafe fn into_array_unchecked(self) -> Array {
203 #[allow(clippy::unnecessary_struct_initialization)]
204 let array = Array(crate::kvec::KVec { ..self.data.array.0 });
205 core::mem::forget(self);
206 array
207 }
208
209 pub unsafe fn into_dict_unchecked(self) -> Dictionary {
216 #[allow(clippy::unnecessary_struct_initialization)]
217 let dict = Dictionary(crate::kvec::KVec { ..self.data.dictionary.0 });
218 core::mem::forget(self);
219 dict
220 }
221}
222
223macro_rules! clone_copy {
224 ($self:expr, $field:ident) => {{
225 Self {
226 ty: $self.ty,
227 data: ObjectData { $field: unsafe { $self.data.$field } },
228 }
229 }};
230}
231
232macro_rules! clone_drop {
233 ($self:expr, $field:ident, $as_type:ty) => {{
234 Self {
235 ty: $self.ty,
236 data: ObjectData {
237 $field: ManuallyDrop::new(
238 unsafe { &$self.data.$field as &$as_type }.clone(),
239 ),
240 },
241 }
242 }};
243}
244
245impl Clone for Object {
246 fn clone(&self) -> Self {
247 match self.ty {
248 ObjectKind::Nil => Self::nil(),
249 ObjectKind::Boolean => clone_copy!(self, boolean),
250 ObjectKind::Integer
251 | ObjectKind::Buffer
252 | ObjectKind::Window
253 | ObjectKind::TabPage => clone_copy!(self, integer),
254 ObjectKind::Float => clone_copy!(self, float),
255 ObjectKind::String => clone_drop!(self, string, crate::String),
256 ObjectKind::Array => clone_drop!(self, array, Array),
257 ObjectKind::Dictionary => {
258 clone_drop!(self, dictionary, Dictionary)
259 },
260 ObjectKind::LuaRef => clone_copy!(self, luaref),
261 }
262 }
263}
264
265impl Drop for Object {
266 fn drop(&mut self) {
267 match self.ty {
268 ObjectKind::String => unsafe {
269 ManuallyDrop::drop(&mut self.data.string)
270 },
271
272 ObjectKind::Array => unsafe {
273 ManuallyDrop::drop(&mut self.data.array)
274 },
275
276 ObjectKind::Dictionary => unsafe {
277 ManuallyDrop::drop(&mut self.data.dictionary)
278 },
279
280 _ => {},
281 }
282 }
283}
284
285impl PartialEq<Self> for Object {
286 #[inline]
287 fn eq(&self, other: &Self) -> bool {
288 if self.ty != other.ty {
289 return false;
290 };
291
292 let (lhs, rhs) = (&self.data, &other.data);
293
294 unsafe {
295 use ObjectKind::*;
296 match self.ty {
297 Nil => true,
298 Boolean => lhs.boolean == rhs.boolean,
299 Integer | Buffer | Window | TabPage => {
300 lhs.integer == rhs.integer
301 },
302 Float => lhs.float == rhs.float,
303 String => lhs.string == rhs.string,
304 Array => lhs.array == rhs.array,
305 Dictionary => lhs.dictionary == rhs.dictionary,
306 LuaRef => lhs.luaref == rhs.luaref,
307 }
308 }
309 }
310}
311
312impl From<()> for Object {
313 fn from(_: ()) -> Self {
314 Self::nil()
315 }
316}
317
318macro_rules! from_copy {
320 ($type:ident, $variant:ident, $data:ident) => {
321 impl From<$type> for Object {
322 #[inline(always)]
323 fn from($data: $type) -> Self {
324 Object { ty: ObjectKind::$variant, data: ObjectData { $data } }
325 }
326 }
327 };
328}
329
330from_copy!(Boolean, Boolean, boolean);
331from_copy!(Integer, Integer, integer);
332from_copy!(Float, Float, float);
333
334macro_rules! from_man_drop {
336 ($type:ty, $variant:ident, $data:ident) => {
337 impl From<$type> for Object {
338 #[inline(always)]
339 fn from($data: $type) -> Self {
340 Object {
341 ty: ObjectKind::$variant,
342 data: ObjectData { $data: ManuallyDrop::new($data) },
343 }
344 }
345 }
346 };
347}
348
349from_man_drop!(crate::String, String, string);
350from_man_drop!(Array, Array, array);
351from_man_drop!(Dictionary, Dictionary, dictionary);
352
353impl<A, R> From<Function<A, R>> for Object {
354 fn from(fun: Function<A, R>) -> Self {
355 Self::from_luaref(fun.lua_ref)
356 }
357}
358
359macro_rules! from_int {
361 ($type:ident) => {
362 impl From<$type> for Object {
363 #[inline(always)]
364 fn from(i: $type) -> Self {
365 Integer::from(i).into()
366 }
367 }
368 };
369}
370
371from_int!(i8);
372from_int!(u8);
373from_int!(i16);
374from_int!(u16);
375from_int!(i32);
376from_int!(u32);
377
378impl From<f32> for Object {
379 #[inline(always)]
380 fn from(n: f32) -> Self {
381 Float::from(n).into()
382 }
383}
384
385impl From<String> for Object {
386 #[inline(always)]
387 fn from(s: String) -> Self {
388 crate::String::from(s.as_str()).into()
389 }
390}
391
392impl From<&str> for Object {
393 #[inline(always)]
394 fn from(s: &str) -> Self {
395 crate::String::from(s).into()
396 }
397}
398
399impl From<char> for Object {
400 #[inline(always)]
401 fn from(ch: char) -> Self {
402 crate::String::from(ch).into()
403 }
404}
405
406impl<T> From<Option<T>> for Object
407where
408 Object: From<T>,
409{
410 #[inline(always)]
411 fn from(maybe: Option<T>) -> Self {
412 maybe.map(Into::into).unwrap_or_default()
413 }
414}
415
416impl<T> From<Box<T>> for Object
417where
418 Object: From<T>,
419{
420 #[inline(always)]
421 fn from(boxed: Box<T>) -> Self {
422 (*boxed).into()
423 }
424}
425
426impl<T> From<Cow<'_, T>> for Object
427where
428 T: Clone,
429 Object: From<T>,
430{
431 #[inline(always)]
432 fn from(moo: Cow<'_, T>) -> Self {
433 moo.into_owned().into()
434 }
435}
436
437impl From<Cow<'_, str>> for Object {
438 fn from(moo: Cow<'_, str>) -> Self {
439 crate::String::from(moo.as_ref()).into()
440 }
441}
442
443impl<T> FromIterator<T> for Object
444where
445 T: Into<Object>,
446{
447 #[inline(always)]
448 fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
449 Array::from_iter(iter).into()
450 }
451}
452
453impl<K, V> FromIterator<(K, V)> for Object
454where
455 crate::String: From<K>,
456 Object: From<V>,
457{
458 #[inline(always)]
459 fn from_iter<I: IntoIterator<Item = (K, V)>>(iter: I) -> Self {
460 Dictionary::from_iter(iter).into()
461 }
462}
463
464impl Pushable for Object {
465 unsafe fn push(self, lstate: *mut lua_State) -> Result<c_int, lua::Error> {
466 match self.kind() {
467 ObjectKind::Nil => ().push(lstate),
468 ObjectKind::Boolean => self.as_boolean_unchecked().push(lstate),
469 ObjectKind::Integer
470 | ObjectKind::Buffer
471 | ObjectKind::Window
472 | ObjectKind::TabPage => self.as_integer_unchecked().push(lstate),
473 ObjectKind::Float => self.as_float_unchecked().push(lstate),
474 ObjectKind::String => self.into_string_unchecked().push(lstate),
475 ObjectKind::Array => self.into_array_unchecked().push(lstate),
476 ObjectKind::Dictionary => self.into_dict_unchecked().push(lstate),
477 ObjectKind::LuaRef => {
478 Function::<(), ()>::from_ref(self.as_luaref_unchecked())
479 .push(lstate)
480 },
481 }
482 }
483}
484
485impl Poppable for Object {
486 unsafe fn pop(lstate: *mut lua_State) -> Result<Self, lua::Error> {
487 if lua_gettop(lstate) == 0 {
488 return Ok(Self::nil());
489 }
490
491 match lua_type(lstate, -1) {
492 LUA_TNIL => <()>::pop(lstate).map(Into::into),
493
494 LUA_TBOOLEAN => bool::pop(lstate).map(Into::into),
495
496 LUA_TNUMBER => {
497 let n = lua_Number::pop(lstate)?;
498
499 if n == (n as c_int) as lua_Number {
502 Ok(Object::from(n as c_int))
503 } else {
504 Ok(Object::from(n))
505 }
506 },
507
508 LUA_TSTRING => crate::String::pop(lstate).map(Into::into),
509
510 LUA_TTABLE => {
511 if lua::utils::is_table_array(lstate, -1) {
512 Array::pop(lstate).map(Into::into)
513 } else {
514 Dictionary::pop(lstate).map(Into::into)
515 }
516 },
517
518 LUA_TFUNCTION => Function::<(), ()>::pop(lstate).map(Into::into),
519
520 LUA_TNONE => Err(lua::Error::PopEmptyStack),
521
522 LUA_TLIGHTUSERDATA | LUA_TUSERDATA | LUA_TTHREAD => {
523 let typename = lua::utils::debug_type(lstate, -1);
524 lua_pop(lstate, 1);
525
526 Err(lua::Error::pop_error(
527 "Object",
528 format!("unexpected value of type {}", typename),
529 ))
530 },
531
532 _ => unreachable!(),
533 }
534 }
535}
536
537#[cfg(feature = "serde")]
538mod serde {
539 use std::fmt;
540
541 use serde::de::{self, Deserialize};
542
543 use super::Object;
544 use crate::{Array, Dictionary, Integer, LuaRef};
545
546 impl<'de> Deserialize<'de> for Object {
547 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
548 where
549 D: de::Deserializer<'de>,
550 {
551 struct ObjectVisitor;
552
553 macro_rules! visit_into {
554 ($fn_name:ident, $ty:ty) => {
555 fn $fn_name<E>(self, value: $ty) -> Result<Self::Value, E>
556 where
557 E: de::Error,
558 {
559 Ok(Object::from(value))
560 }
561 };
562 }
563
564 impl<'de> de::Visitor<'de> for ObjectVisitor {
565 type Value = Object;
566
567 fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
568 f.write_str("either a string of a byte vector")
569 }
570
571 fn visit_unit<E>(self) -> Result<Self::Value, E>
572 where
573 E: de::Error,
574 {
575 Ok(Object::nil())
576 }
577
578 fn visit_bytes<E>(self, b: &[u8]) -> Result<Self::Value, E>
579 where
580 E: de::Error,
581 {
582 Ok(crate::String::from_bytes(b).into())
583 }
584
585 fn visit_u64<E>(self, n: u64) -> Result<Self::Value, E>
586 where
587 E: de::Error,
588 {
589 Integer::try_from(n).map(Object::from).map_err(E::custom)
590 }
591
592 fn visit_f32<E>(self, f: f32) -> Result<Self::Value, E>
593 where
594 E: de::Error,
595 {
596 Ok(Object::from_luaref(f as LuaRef))
597 }
598
599 fn visit_seq<A>(
600 self,
601 mut seq: A,
602 ) -> Result<Self::Value, A::Error>
603 where
604 A: de::SeqAccess<'de>,
605 {
606 let mut vec = Vec::<Object>::with_capacity(
607 seq.size_hint().unwrap_or_default(),
608 );
609
610 while let Some(obj) = seq.next_element::<Object>()? {
611 vec.push(obj);
612 }
613
614 Ok(vec.into_iter().collect::<Array>().into())
615 }
616
617 fn visit_map<A>(
618 self,
619 mut map: A,
620 ) -> Result<Self::Value, A::Error>
621 where
622 A: de::MapAccess<'de>,
623 {
624 let mut vec =
625 Vec::<(crate::String, Object)>::with_capacity(
626 map.size_hint().unwrap_or_default(),
627 );
628
629 while let Some(pair) =
630 map.next_entry::<crate::String, Object>()?
631 {
632 vec.push(pair);
633 }
634
635 Ok(vec.into_iter().collect::<Dictionary>().into())
636 }
637
638 visit_into!(visit_bool, bool);
639 visit_into!(visit_i8, i8);
640 visit_into!(visit_u8, u8);
641 visit_into!(visit_i16, i16);
642 visit_into!(visit_u16, u16);
643 visit_into!(visit_i32, i32);
644 visit_into!(visit_u32, u32);
645 visit_into!(visit_i64, i64);
646 visit_into!(visit_f64, f64);
647 visit_into!(visit_str, &str);
648 }
649
650 deserializer.deserialize_any(ObjectVisitor)
651 }
652 }
653}
654
655#[cfg(test)]
656mod tests {
657 use super::*;
658 use crate::conversion::FromObject;
659
660 #[test]
661 fn std_string_to_obj_and_back() {
662 let str = String::from("foo");
663 let obj = Object::from(str.clone());
664 let str_again = String::from_object(obj);
665 assert!(str_again.is_ok());
666 assert_eq!(str, str_again.unwrap());
667 }
668}