nvim_oxi_types/
conversion.rs

1//! Traits for converting between Neovim [`Object`]s and Rust types.
2
3use std::collections::HashMap;
4
5use thiserror::Error as ThisError;
6
7use crate::array::ArrayFromTupleError;
8use crate::{
9    Array,
10    Boolean,
11    Dictionary,
12    Float,
13    Function,
14    Integer,
15    Object,
16    ObjectKind,
17};
18
19#[derive(Clone, Debug, Eq, PartialEq, ThisError)]
20pub enum Error {
21    #[error("Was expecting a \"{expected}\" but received a \"{actual}\"")]
22    FromWrongType { expected: &'static str, actual: &'static str },
23
24    #[error(transparent)]
25    FromInt(#[from] std::num::TryFromIntError),
26
27    #[error(transparent)]
28    FromUtf8(#[from] std::string::FromUtf8Error),
29
30    #[cfg(feature = "serde")]
31    #[error(transparent)]
32    Deserialize(#[from] crate::serde::DeserializeError),
33
34    #[cfg(feature = "serde")]
35    #[error(transparent)]
36    Serialize(#[from] crate::serde::SerializeError),
37
38    #[doc(hidden)]
39    #[error("{0}")]
40    Other(String),
41}
42
43/// Trait implemented for types can be obtained from an [`Object`].
44pub trait FromObject: Sized {
45    fn from_object(object: Object) -> Result<Self, Error>;
46}
47
48/// Trait implemented for types can be converted into an [`Object`].
49pub trait ToObject {
50    fn to_object(self) -> Result<Object, Error>;
51}
52
53impl FromObject for Object {
54    fn from_object(obj: Object) -> Result<Self, Error> {
55        Ok(obj)
56    }
57}
58
59impl FromObject for () {
60    fn from_object(obj: Object) -> Result<Self, Error> {
61        match obj.kind() {
62            ObjectKind::Nil => Ok(()),
63
64            other => Err(Error::FromWrongType {
65                expected: "nil",
66                actual: other.as_static(),
67            }),
68        }
69    }
70}
71
72impl FromObject for Boolean {
73    fn from_object(obj: Object) -> Result<Self, Error> {
74        match obj.kind() {
75            ObjectKind::Boolean => Ok(unsafe { obj.as_boolean_unchecked() }),
76
77            other => Err(Error::FromWrongType {
78                expected: "bool",
79                actual: other.as_static(),
80            }),
81        }
82    }
83}
84
85impl TryFrom<Object> for Integer {
86    type Error = Error;
87
88    fn try_from(obj: Object) -> Result<Self, Self::Error> {
89        match obj.kind() {
90            ObjectKind::Integer
91            | ObjectKind::Buffer
92            | ObjectKind::Window
93            | ObjectKind::TabPage => Ok(unsafe { obj.as_integer_unchecked() }),
94
95            other => Err(Error::FromWrongType {
96                expected: "integer",
97                actual: other.as_static(),
98            }),
99        }
100    }
101}
102
103impl FromObject for Float {
104    fn from_object(obj: Object) -> Result<Self, Error> {
105        match obj.kind() {
106            ObjectKind::Float => Ok(unsafe { obj.as_float_unchecked() }),
107
108            other => Err(Error::FromWrongType {
109                expected: "float",
110                actual: other.as_static(),
111            }),
112        }
113    }
114}
115
116impl FromObject for Array {
117    fn from_object(obj: Object) -> Result<Self, Error> {
118        match obj.kind() {
119            ObjectKind::Array => Ok(unsafe { obj.into_array_unchecked() }),
120
121            other => Err(Error::FromWrongType {
122                expected: "string",
123                actual: other.as_static(),
124            }),
125        }
126    }
127}
128
129impl<A, R> FromObject for Function<A, R> {
130    fn from_object(obj: Object) -> Result<Self, Error> {
131        match obj.kind() {
132            ObjectKind::LuaRef => {
133                Ok(Self::from_ref(unsafe { obj.as_luaref_unchecked() }))
134            },
135
136            other => Err(Error::FromWrongType {
137                expected: "function",
138                actual: other.as_static(),
139            }),
140        }
141    }
142}
143
144impl<T: TryFrom<Object, Error = Error>> FromObject for T {
145    #[inline]
146    fn from_object(obj: Object) -> Result<Self, Error> {
147        T::try_from(obj)
148    }
149}
150
151/// Implements `FromObject` for a type that implements `From<Integer>`.
152macro_rules! from_int {
153    ($integer:ty) => {
154        impl FromObject for $integer {
155            fn from_object(obj: Object) -> Result<Self, Error> {
156                Integer::from_object(obj).map(Into::into)
157            }
158        }
159    };
160}
161
162from_int!(i128);
163
164/// Implements `TryFrom<Object>` for a type that implements `TryFrom<Integer>`.
165macro_rules! int_try_from_obj {
166    ($integer:ty) => {
167        impl TryFrom<Object> for $integer {
168            type Error = Error;
169
170            fn try_from(obj: Object) -> Result<Self, Self::Error> {
171                Integer::try_from(obj)
172                    .and_then(|n| n.try_into().map_err(Into::into))
173            }
174        }
175    };
176}
177
178int_try_from_obj!(i8);
179int_try_from_obj!(u8);
180int_try_from_obj!(i16);
181int_try_from_obj!(u16);
182int_try_from_obj!(i32);
183int_try_from_obj!(u32);
184int_try_from_obj!(u64);
185int_try_from_obj!(u128);
186int_try_from_obj!(isize);
187int_try_from_obj!(usize);
188
189impl FromObject for f32 {
190    fn from_object(obj: Object) -> Result<Self, Error> {
191        Ok(Float::from_object(obj)? as _)
192    }
193}
194
195impl FromObject for String {
196    fn from_object(obj: Object) -> Result<Self, Error> {
197        crate::String::from_object(obj)
198            .map(|nvim_str| nvim_str.to_string_lossy().into())
199    }
200}
201
202impl<T> FromObject for Option<T>
203where
204    T: FromObject,
205{
206    fn from_object(obj: Object) -> Result<Self, Error> {
207        (!obj.is_nil()).then(|| T::from_object(obj)).transpose()
208    }
209}
210
211impl<T> FromObject for Vec<T>
212where
213    T: FromObject,
214{
215    fn from_object(obj: Object) -> Result<Self, Error> {
216        Array::from_object(obj)?
217            .into_iter()
218            .map(FromObject::from_object)
219            .collect()
220    }
221}
222
223/// Implements `FromObject` for tuples `(A, B, C, ..)` where all the
224/// elements in the tuple are `TryFrom<Object>` with the same error.
225macro_rules! tuple_from_object {
226    ($($ty:ident)*) => {
227        impl<Err, $($ty,)*> FromObject for ($($ty,)*)
228        where
229            $($ty: TryFrom<Object, Error = Err>,)*
230            Err: Into<self::Error> + core::error::Error,
231        {
232            #[inline]
233            #[allow(non_snake_case)]
234            fn from_object(obj: Object) -> Result<Self, Error> {
235                Array::from_object(obj)?
236                    .try_into()
237                    .map_err(|err: ArrayFromTupleError<Err>| match err {
238                        ArrayFromTupleError::ElementFromObject { error, .. } => error.into(),
239                        err @ ArrayFromTupleError::NotEnoughElements { .. } => Error::Other(err.to_string()),
240                    })
241            }
242        }
243    };
244}
245
246tuple_from_object!(A);
247tuple_from_object!(A B);
248tuple_from_object!(A B C);
249tuple_from_object!(A B C D);
250tuple_from_object!(A B C D E);
251tuple_from_object!(A B C D E F);
252tuple_from_object!(A B C D E F G);
253tuple_from_object!(A B C D E F G H);
254tuple_from_object!(A B C D E F G H I);
255tuple_from_object!(A B C D E F G H I J);
256tuple_from_object!(A B C D E F G H I J K);
257tuple_from_object!(A B C D E F G H I J K L);
258tuple_from_object!(A B C D E F G H I J K L M);
259tuple_from_object!(A B C D E F G H I J K L M N);
260tuple_from_object!(A B C D E F G H I J K L M N O);
261tuple_from_object!(A B C D E F G H I J K L M N O P);
262
263impl<T> ToObject for T
264where
265    T: Into<Object>,
266{
267    fn to_object(self) -> Result<Object, Error> {
268        Ok(self.into())
269    }
270}
271
272/// Implements `ToObject` for "big integer" types.
273macro_rules! bigint_to_obj {
274    ($type:ty) => {
275        impl ToObject for $type {
276            fn to_object(self) -> Result<Object, Error> {
277                Ok(i64::try_from(self)?.into())
278            }
279        }
280    };
281}
282
283bigint_to_obj!(u64);
284bigint_to_obj!(isize);
285bigint_to_obj!(usize);
286bigint_to_obj!(i128);
287bigint_to_obj!(u128);
288
289impl<T> ToObject for Vec<T>
290where
291    T: ToObject,
292{
293    fn to_object(self) -> Result<Object, Error> {
294        Ok(self
295            .into_iter()
296            .map(ToObject::to_object)
297            .collect::<Result<Array, Error>>()?
298            .into())
299    }
300}
301
302impl<K, V> ToObject for HashMap<K, V>
303where
304    K: Into<crate::String>,
305    V: ToObject,
306{
307    fn to_object(self) -> Result<Object, Error> {
308        self.into_iter()
309            .map(|(k, v)| Ok((k, v.to_object()?)))
310            .collect::<Result<Dictionary, Error>>()
311            .map(Into::into)
312    }
313}