1use std::borrow::Cow;
2use std::ffi::c_int;
3use std::mem::ManuallyDrop;
4
5use lua::{Poppable, Pushable, ffi::*};
6use luajit as lua;
7
8use crate::{
9 Array,
10 Boolean,
11 Dictionary,
12 Float,
13 Function,
14 Integer,
15 LuaRef,
16 NonOwning,
17 NvimStr,
18 String as NvimString,
19};
20
21#[repr(C)]
27pub struct Object {
28 ty: ObjectKind,
29 data: ObjectData,
30}
31
32#[derive(Copy, Clone, Debug, Eq, PartialEq)]
36#[repr(C)]
37pub enum ObjectKind {
38 Nil = 0,
39 Boolean,
40 Integer,
41 Float,
42 String,
43 Array,
44 Dictionary,
45 LuaRef,
46 Buffer,
47 Window,
48 TabPage,
49}
50
51impl ObjectKind {
52 pub fn as_static(&self) -> &'static str {
53 match self {
54 Self::Nil => "nil",
55 Self::Boolean => "boolean",
56 Self::Integer => "integer",
57 Self::Float => "float",
58 Self::String => "string",
59 Self::Array => "array",
60 Self::Dictionary => "dictionary",
61 Self::LuaRef => "luaref",
62 Self::Buffer => "buffer",
63 Self::Window => "window",
64 Self::TabPage => "tabpage",
65 }
66 }
67}
68
69#[repr(C)]
71union ObjectData {
72 boolean: Boolean,
73 integer: Integer,
74 float: Float,
75 string: ManuallyDrop<NvimString>,
76 array: ManuallyDrop<Array>,
77 dictionary: ManuallyDrop<Dictionary>,
78 luaref: LuaRef,
79}
80
81impl Default for Object {
82 fn default() -> Object {
83 Object::nil()
84 }
85}
86
87impl core::fmt::Debug for Object {
88 #[inline]
89 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
90 let field: &dyn core::fmt::Debug = match self.ty {
91 ObjectKind::Nil => return f.write_str("nil"),
92
93 ObjectKind::Boolean => unsafe { &self.data.boolean },
94
95 ObjectKind::Integer
96 | ObjectKind::Buffer
97 | ObjectKind::Window
98 | ObjectKind::TabPage => unsafe { &self.data.integer },
99
100 ObjectKind::Float => unsafe { &self.data.float },
101
102 ObjectKind::String => unsafe { &*self.data.string },
103
104 ObjectKind::Array => unsafe { &*self.data.array },
105
106 ObjectKind::Dictionary => unsafe { &*self.data.dictionary },
107
108 ObjectKind::LuaRef => {
109 let luaref = unsafe { self.data.luaref };
110 return write!(f, "Object(LuaRef({luaref}))");
111 },
112 };
113
114 field.fmt(f)
115 }
116}
117
118impl Object {
119 #[inline]
121 pub fn nil() -> Self {
122 Self { ty: ObjectKind::Nil, data: ObjectData { integer: 0 } }
123 }
124
125 #[inline]
126 pub fn is_nil(&self) -> bool {
127 matches!(self.ty, ObjectKind::Nil)
128 }
129
130 #[inline]
131 pub fn is_some(&self) -> bool {
132 !self.is_nil()
133 }
134
135 #[inline(always)]
136 pub fn from_luaref(luaref: LuaRef) -> Self {
137 Self { ty: ObjectKind::LuaRef, data: ObjectData { luaref } }
138 }
139
140 #[inline]
141 pub fn kind(&self) -> ObjectKind {
142 self.ty
143 }
144
145 #[inline]
147 #[doc(hidden)]
148 pub fn non_owning(&self) -> NonOwning<'_, Self> {
149 unsafe { NonOwning::new(std::ptr::read(self)) }
151 }
152
153 #[cfg_attr(debug_assertions, track_caller)]
164 #[inline(always)]
165 pub unsafe fn as_boolean_unchecked(&self) -> bool {
166 debug_assert!(self.ty == ObjectKind::Boolean, "{:?}", self.ty);
167 self.data.boolean
168 }
169
170 #[cfg_attr(debug_assertions, track_caller)]
182 #[inline(always)]
183 pub unsafe fn as_boolean_unchecked_mut(&mut self) -> &mut bool {
184 debug_assert!(self.ty == ObjectKind::Boolean, "{:?}", self.ty);
185 &mut self.data.boolean
186 }
187
188 #[cfg_attr(debug_assertions, track_caller)]
202 #[inline(always)]
203 pub unsafe fn as_integer_unchecked(&self) -> Integer {
204 debug_assert!(
205 matches!(
206 self.ty,
207 ObjectKind::Integer
208 | ObjectKind::Buffer
209 | ObjectKind::Window
210 | ObjectKind::TabPage
211 ),
212 "{:?}",
213 self.ty
214 );
215 self.data.integer
216 }
217
218 #[cfg_attr(debug_assertions, track_caller)]
233 #[inline(always)]
234 pub unsafe fn as_integer_unchecked_mut(&mut self) -> &mut Integer {
235 debug_assert!(
236 matches!(
237 self.ty,
238 ObjectKind::Integer
239 | ObjectKind::Buffer
240 | ObjectKind::Window
241 | ObjectKind::TabPage
242 ),
243 "{:?}",
244 self.ty
245 );
246 &mut self.data.integer
247 }
248
249 #[cfg_attr(debug_assertions, track_caller)]
260 #[inline(always)]
261 pub unsafe fn as_float_unchecked(&self) -> Float {
262 debug_assert!(self.ty == ObjectKind::Float, "{:?}", self.ty);
263 self.data.float
264 }
265
266 #[cfg_attr(debug_assertions, track_caller)]
278 #[inline(always)]
279 pub unsafe fn as_float_unchecked_mut(&mut self) -> &mut Float {
280 debug_assert!(self.ty == ObjectKind::Float, "{:?}", self.ty);
281 &mut self.data.float
282 }
283
284 #[cfg_attr(debug_assertions, track_caller)]
295 #[inline(always)]
296 pub unsafe fn as_luaref_unchecked(&self) -> LuaRef {
297 debug_assert!(self.ty == ObjectKind::LuaRef, "{:?}", self.ty);
298 self.data.luaref
299 }
300
301 #[cfg_attr(debug_assertions, track_caller)]
313 #[inline(always)]
314 pub unsafe fn as_luaref_unchecked_mut(&mut self) -> &mut LuaRef {
315 debug_assert!(self.ty == ObjectKind::LuaRef, "{:?}", self.ty);
316 &mut self.data.luaref
317 }
318
319 #[cfg_attr(debug_assertions, track_caller)]
330 pub unsafe fn as_nvim_str_unchecked(&self) -> NvimStr<'_> {
331 debug_assert!(self.ty == ObjectKind::String, "{:?}", self.ty);
332 self.data.string.as_nvim_str()
333 }
334
335 #[cfg_attr(debug_assertions, track_caller)]
346 pub unsafe fn into_string_unchecked(self) -> NvimString {
347 debug_assert!(self.ty == ObjectKind::String, "{:?}", self.ty);
348 let string = &self.data.string;
349 let string = unsafe {
350 NvimString::from_raw_parts(string.as_ptr(), string.len())
351 };
352 core::mem::forget(self);
353 string
354 }
355
356 #[cfg_attr(debug_assertions, track_caller)]
367 pub unsafe fn as_array_unchecked(&self) -> &Array {
368 debug_assert!(self.ty == ObjectKind::Array, "{:?}", self.ty);
369 &self.data.array
370 }
371
372 #[cfg_attr(debug_assertions, track_caller)]
384 pub unsafe fn as_array_unchecked_mut(&mut self) -> &mut Array {
385 debug_assert!(self.ty == ObjectKind::Array, "{:?}", self.ty);
386 &mut self.data.array
387 }
388
389 #[cfg_attr(debug_assertions, track_caller)]
400 pub unsafe fn into_array_unchecked(self) -> Array {
401 debug_assert!(self.ty == ObjectKind::Array, "{:?}", self.ty);
402 #[allow(clippy::unnecessary_struct_initialization)]
403 let array = Array(crate::kvec::KVec { ..self.data.array.0 });
404 core::mem::forget(self);
405 array
406 }
407
408 #[cfg_attr(debug_assertions, track_caller)]
419 pub unsafe fn as_dictionary_unchecked(&self) -> &Dictionary {
420 debug_assert!(self.ty == ObjectKind::Dictionary, "{:?}", self.ty);
421 &self.data.dictionary
422 }
423
424 #[cfg_attr(debug_assertions, track_caller)]
436 pub unsafe fn as_dictionary_unchecked_mut(&mut self) -> &mut Dictionary {
437 debug_assert!(self.ty == ObjectKind::Dictionary, "{:?}", self.ty);
438 &mut self.data.dictionary
439 }
440
441 #[cfg_attr(debug_assertions, track_caller)]
452 pub unsafe fn into_dictionary_unchecked(self) -> Dictionary {
453 debug_assert!(self.ty == ObjectKind::Dictionary, "{:?}", self.ty);
454 #[allow(clippy::unnecessary_struct_initialization)]
455 let dict = Dictionary(crate::kvec::KVec { ..self.data.dictionary.0 });
456 core::mem::forget(self);
457 dict
458 }
459}
460
461macro_rules! clone_copy {
462 ($self:expr, $field:ident) => {{
463 Self {
464 ty: $self.ty,
465 data: ObjectData { $field: unsafe { $self.data.$field } },
466 }
467 }};
468}
469
470macro_rules! clone_drop {
471 ($self:expr, $field:ident, $as_type:ty) => {{
472 Self {
473 ty: $self.ty,
474 data: ObjectData {
475 $field: ManuallyDrop::new(
476 unsafe { &$self.data.$field as &$as_type }.clone(),
477 ),
478 },
479 }
480 }};
481}
482
483impl Clone for Object {
484 fn clone(&self) -> Self {
485 match self.ty {
486 ObjectKind::Nil => Self::nil(),
487 ObjectKind::Boolean => clone_copy!(self, boolean),
488 ObjectKind::Integer
489 | ObjectKind::Buffer
490 | ObjectKind::Window
491 | ObjectKind::TabPage => clone_copy!(self, integer),
492 ObjectKind::Float => clone_copy!(self, float),
493 ObjectKind::String => clone_drop!(self, string, NvimString),
494 ObjectKind::Array => clone_drop!(self, array, Array),
495 ObjectKind::Dictionary => {
496 clone_drop!(self, dictionary, Dictionary)
497 },
498 ObjectKind::LuaRef => clone_copy!(self, luaref),
499 }
500 }
501}
502
503impl Drop for Object {
504 fn drop(&mut self) {
505 match self.ty {
506 ObjectKind::String => unsafe {
507 ManuallyDrop::drop(&mut self.data.string)
508 },
509
510 ObjectKind::Array => unsafe {
511 ManuallyDrop::drop(&mut self.data.array)
512 },
513
514 ObjectKind::Dictionary => unsafe {
515 ManuallyDrop::drop(&mut self.data.dictionary)
516 },
517
518 _ => {},
519 }
520 }
521}
522
523impl PartialEq<Self> for Object {
524 #[inline]
525 fn eq(&self, other: &Self) -> bool {
526 if self.ty != other.ty {
527 return false;
528 };
529
530 let (lhs, rhs) = (&self.data, &other.data);
531
532 unsafe {
533 use ObjectKind::*;
534 match self.ty {
535 Nil => true,
536 Boolean => lhs.boolean == rhs.boolean,
537 Integer | Buffer | Window | TabPage => {
538 lhs.integer == rhs.integer
539 },
540 Float => lhs.float == rhs.float,
541 String => lhs.string == rhs.string,
542 Array => lhs.array == rhs.array,
543 Dictionary => lhs.dictionary == rhs.dictionary,
544 LuaRef => lhs.luaref == rhs.luaref,
545 }
546 }
547 }
548}
549
550impl From<()> for Object {
551 fn from(_: ()) -> Self {
552 Self::nil()
553 }
554}
555
556macro_rules! from_copy {
558 ($type:ident, $variant:ident, $data:ident) => {
559 impl From<$type> for Object {
560 #[inline(always)]
561 fn from($data: $type) -> Self {
562 Object { ty: ObjectKind::$variant, data: ObjectData { $data } }
563 }
564 }
565 };
566}
567
568from_copy!(Boolean, Boolean, boolean);
569from_copy!(Integer, Integer, integer);
570from_copy!(Float, Float, float);
571
572macro_rules! from_man_drop {
574 ($type:ty, $variant:ident, $data:ident) => {
575 impl From<$type> for Object {
576 #[inline(always)]
577 fn from($data: $type) -> Self {
578 Object {
579 ty: ObjectKind::$variant,
580 data: ObjectData { $data: ManuallyDrop::new($data) },
581 }
582 }
583 }
584 };
585}
586
587from_man_drop!(NvimString, String, string);
588from_man_drop!(Array, Array, array);
589from_man_drop!(Dictionary, Dictionary, dictionary);
590
591impl<A, R> From<Function<A, R>> for Object {
592 fn from(fun: Function<A, R>) -> Self {
593 Self::from_luaref(fun.lua_ref)
594 }
595}
596
597macro_rules! from_int {
599 ($type:ident) => {
600 impl From<$type> for Object {
601 #[inline(always)]
602 fn from(i: $type) -> Self {
603 Integer::from(i).into()
604 }
605 }
606 };
607}
608
609from_int!(i8);
610from_int!(u8);
611from_int!(i16);
612from_int!(u16);
613from_int!(i32);
614from_int!(u32);
615
616impl From<f32> for Object {
617 #[inline(always)]
618 fn from(n: f32) -> Self {
619 Float::from(n).into()
620 }
621}
622
623impl From<String> for Object {
624 #[inline(always)]
625 fn from(s: String) -> Self {
626 NvimString::from(s.as_str()).into()
627 }
628}
629
630impl From<&str> for Object {
631 #[inline(always)]
632 fn from(s: &str) -> Self {
633 NvimString::from(s).into()
634 }
635}
636
637impl From<char> for Object {
638 #[inline(always)]
639 fn from(ch: char) -> Self {
640 NvimString::from(ch).into()
641 }
642}
643
644impl<T> From<Option<T>> for Object
645where
646 Object: From<T>,
647{
648 #[inline(always)]
649 fn from(maybe: Option<T>) -> Self {
650 maybe.map(Into::into).unwrap_or_default()
651 }
652}
653
654impl<T> From<Box<T>> for Object
655where
656 Object: From<T>,
657{
658 #[inline(always)]
659 fn from(boxed: Box<T>) -> Self {
660 (*boxed).into()
661 }
662}
663
664impl<T> From<Cow<'_, T>> for Object
665where
666 T: Clone,
667 Object: From<T>,
668{
669 #[inline(always)]
670 fn from(moo: Cow<'_, T>) -> Self {
671 moo.into_owned().into()
672 }
673}
674
675impl From<Cow<'_, str>> for Object {
676 fn from(moo: Cow<'_, str>) -> Self {
677 NvimString::from(moo.as_ref()).into()
678 }
679}
680
681impl<T> FromIterator<T> for Object
682where
683 T: Into<Object>,
684{
685 #[inline(always)]
686 fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
687 Array::from_iter(iter).into()
688 }
689}
690
691impl<K, V> FromIterator<(K, V)> for Object
692where
693 NvimString: From<K>,
694 Object: From<V>,
695{
696 #[inline(always)]
697 fn from_iter<I: IntoIterator<Item = (K, V)>>(iter: I) -> Self {
698 Dictionary::from_iter(iter).into()
699 }
700}
701
702impl Pushable for Object {
703 unsafe fn push(self, lstate: *mut State) -> Result<c_int, lua::Error> {
704 match self.kind() {
705 ObjectKind::Nil => ().push(lstate),
706 ObjectKind::Boolean => self.as_boolean_unchecked().push(lstate),
707 ObjectKind::Integer
708 | ObjectKind::Buffer
709 | ObjectKind::Window
710 | ObjectKind::TabPage => self.as_integer_unchecked().push(lstate),
711 ObjectKind::Float => self.as_float_unchecked().push(lstate),
712 ObjectKind::String => self.into_string_unchecked().push(lstate),
713 ObjectKind::Array => self.into_array_unchecked().push(lstate),
714 ObjectKind::Dictionary => {
715 self.into_dictionary_unchecked().push(lstate)
716 },
717 ObjectKind::LuaRef => {
718 Function::<(), ()>::from_ref(self.as_luaref_unchecked())
719 .push(lstate)
720 },
721 }
722 }
723}
724
725impl Poppable for Object {
726 unsafe fn pop(lstate: *mut State) -> Result<Self, lua::Error> {
727 if lua_gettop(lstate) == 0 {
728 return Ok(Self::nil());
729 }
730
731 match lua_type(lstate, -1) {
732 LUA_TNIL => <()>::pop(lstate).map(Into::into),
733
734 LUA_TBOOLEAN => bool::pop(lstate).map(Into::into),
735
736 LUA_TNUMBER => {
737 let n = Number::pop(lstate)?;
738
739 if n == (n as c_int) as Number {
742 Ok(Object::from(n as c_int))
743 } else {
744 Ok(Object::from(n))
745 }
746 },
747
748 LUA_TSTRING => NvimString::pop(lstate).map(Into::into),
749
750 LUA_TTABLE => {
751 if lua::utils::is_table_array(lstate, -1) {
752 Array::pop(lstate).map(Into::into)
753 } else {
754 Dictionary::pop(lstate).map(Into::into)
755 }
756 },
757
758 LUA_TFUNCTION => Function::<(), ()>::pop(lstate).map(Into::into),
759
760 LUA_TNONE => Err(lua::Error::PopEmptyStack),
761
762 LUA_TLIGHTUSERDATA | LUA_TUSERDATA | LUA_TTHREAD => {
763 let typename = lua::utils::debug_type(lstate, -1);
764 lua_pop(lstate, 1);
765
766 Err(lua::Error::pop_error(
767 "Object",
768 format!("unexpected value of type {typename}"),
769 ))
770 },
771
772 _ => unreachable!(),
773 }
774 }
775}
776
777#[cfg(feature = "serde")]
778mod serde {
779 use std::fmt;
780
781 use serde::de::{self, Deserialize};
782
783 use crate::{
784 Array,
785 Dictionary,
786 Integer,
787 LuaRef,
788 Object,
789 String as NvimString,
790 };
791
792 impl<'de> Deserialize<'de> for Object {
793 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
794 where
795 D: de::Deserializer<'de>,
796 {
797 struct ObjectVisitor;
798
799 macro_rules! visit_into {
800 ($fn_name:ident, $ty:ty) => {
801 fn $fn_name<E>(self, value: $ty) -> Result<Self::Value, E>
802 where
803 E: de::Error,
804 {
805 Ok(Object::from(value))
806 }
807 };
808 }
809
810 impl<'de> de::Visitor<'de> for ObjectVisitor {
811 type Value = Object;
812
813 fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
814 f.write_str("either a string of a byte vector")
815 }
816
817 fn visit_unit<E>(self) -> Result<Self::Value, E>
818 where
819 E: de::Error,
820 {
821 Ok(Object::nil())
822 }
823
824 fn visit_bytes<E>(self, b: &[u8]) -> Result<Self::Value, E>
825 where
826 E: de::Error,
827 {
828 Ok(NvimString::from_bytes(b).into())
829 }
830
831 fn visit_u64<E>(self, n: u64) -> Result<Self::Value, E>
832 where
833 E: de::Error,
834 {
835 Integer::try_from(n).map(Object::from).map_err(E::custom)
836 }
837
838 fn visit_f32<E>(self, f: f32) -> Result<Self::Value, E>
839 where
840 E: de::Error,
841 {
842 Ok(Object::from_luaref(f as LuaRef))
843 }
844
845 fn visit_seq<A>(
846 self,
847 mut seq: A,
848 ) -> Result<Self::Value, A::Error>
849 where
850 A: de::SeqAccess<'de>,
851 {
852 let mut vec = Vec::<Object>::with_capacity(
853 seq.size_hint().unwrap_or_default(),
854 );
855
856 while let Some(obj) = seq.next_element::<Object>()? {
857 vec.push(obj);
858 }
859
860 Ok(vec.into_iter().collect::<Array>().into())
861 }
862
863 fn visit_map<A>(
864 self,
865 mut map: A,
866 ) -> Result<Self::Value, A::Error>
867 where
868 A: de::MapAccess<'de>,
869 {
870 let mut vec = Vec::<(NvimString, Object)>::with_capacity(
871 map.size_hint().unwrap_or_default(),
872 );
873
874 while let Some(pair) =
875 map.next_entry::<NvimString, Object>()?
876 {
877 vec.push(pair);
878 }
879
880 Ok(vec.into_iter().collect::<Dictionary>().into())
881 }
882
883 visit_into!(visit_bool, bool);
884 visit_into!(visit_i8, i8);
885 visit_into!(visit_u8, u8);
886 visit_into!(visit_i16, i16);
887 visit_into!(visit_u16, u16);
888 visit_into!(visit_i32, i32);
889 visit_into!(visit_u32, u32);
890 visit_into!(visit_i64, i64);
891 visit_into!(visit_f64, f64);
892 visit_into!(visit_str, &str);
893 }
894
895 deserializer.deserialize_any(ObjectVisitor)
896 }
897 }
898}
899
900#[cfg(test)]
901mod tests {
902 use super::*;
903 use crate::conversion::FromObject;
904
905 #[test]
906 fn debug_nil() {
907 assert_eq!(format!("{:?}", Object::nil()), "nil");
908 }
909
910 #[test]
911 fn std_string_to_obj_and_back() {
912 let str = String::from("foo");
913 let obj = Object::from(str.clone());
914 let str_again = String::from_object(obj);
915 assert!(str_again.is_ok());
916 assert_eq!(str, str_again.unwrap());
917 }
918}