nvim_oxi_types/
object.rs

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// https://github.com/neovim/neovim/blob/v0.9.0/src/nvim/api/private/defs.h#L109-L120
20//
21/// Binding to a Neovim object.
22///
23/// Represents any valid Neovim type.
24#[repr(C)]
25pub struct Object {
26    ty: ObjectKind,
27    data: ObjectData,
28}
29
30// https://github.com/neovim/neovim/blob/v0.9.0/src/nvim/api/private/defs.h#L94-L107
31//
32/// Specifies the kind of a Neovim [`Object`].
33#[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// https://github.com/neovim/neovim/blob/v0.9.0/src/nvim/api/private/defs.h#L111-L119
68#[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    /// Returns a new nil object.
118    #[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    /// Make a non-owning version of this `Object`.
144    #[inline]
145    #[doc(hidden)]
146    pub fn non_owning(&self) -> NonOwning<'_, Self> {
147        // Using ptr::read, because can't copy the union.
148        unsafe { NonOwning::new(std::ptr::read(self)) }
149    }
150
151    /// # Safety
152    ///
153    /// TODO
154    #[inline(always)]
155    pub unsafe fn as_boolean_unchecked(&self) -> bool {
156        self.data.boolean
157    }
158
159    /// # Safety
160    ///
161    /// TODO
162    #[inline(always)]
163    pub unsafe fn as_integer_unchecked(&self) -> Integer {
164        self.data.integer
165    }
166
167    /// # Safety
168    ///
169    /// TODO
170    #[inline(always)]
171    pub unsafe fn as_float_unchecked(&self) -> Float {
172        self.data.float
173    }
174
175    /// # Safety
176    ///
177    /// TODO
178    #[inline(always)]
179    pub unsafe fn as_luaref_unchecked(&self) -> LuaRef {
180        self.data.luaref
181    }
182
183    /// # Safety
184    ///
185    /// TODO
186    ///
187    /// Extracts the contained [`String`](crate::String) value without checking
188    /// that the object actually contains a [`String`](crate::String).
189    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    /// # Safety
197    ///
198    /// TODO
199    ///
200    /// Extracts the contained [`Array`] value without checking that the object
201    /// actually contains an [`Array`].
202    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    /// # Safety
210    ///
211    /// TODO
212    ///
213    /// Extracts the contained [`Dictionary`] value without checking that the
214    /// object actually contains a [`Dictionary`].
215    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
318// Implements `From<..>` for primitive `Copy` types.
319macro_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
334/// Implements `From<..>` for primitive `ManuallyDrop` types.
335macro_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
359/// Implements `From<..>` for integer types convertible to `Integer`.
360macro_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                // This checks that the number is in the range (i32::MIN,
500                // i32::MAX) andd that it has no fractional component.
501                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}