nvim_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::{
8    Array,
9    Boolean,
10    Dictionary,
11    Float,
12    Function,
13    Integer,
14    Object,
15    ObjectKind,
16};
17
18#[derive(Clone, Debug, Eq, PartialEq, ThisError)]
19pub enum Error {
20    #[error("Was expecting a \"{expected}\" but received a \"{actual}\"")]
21    FromWrongType { expected: &'static str, actual: &'static str },
22
23    #[error(transparent)]
24    FromInt(#[from] std::num::TryFromIntError),
25
26    #[error(transparent)]
27    FromUtf8(#[from] std::string::FromUtf8Error),
28
29    #[cfg(feature = "serde")]
30    #[error(transparent)]
31    Serde(#[from] crate::serde::Error),
32}
33
34/// Trait implemented for types can be obtained from an [`Object`].
35pub trait FromObject: Sized {
36    fn from_object(object: Object) -> Result<Self, Error>;
37}
38
39/// Trait implemented for types can be converted into an [`Object`].
40pub trait ToObject {
41    fn to_object(self) -> Result<Object, Error>;
42}
43
44impl FromObject for () {
45    fn from_object(obj: Object) -> Result<Self, Error> {
46        match obj.kind() {
47            ObjectKind::Nil => Ok(()),
48
49            other => Err(Error::FromWrongType {
50                expected: "nil",
51                actual: other.as_static(),
52            }),
53        }
54    }
55}
56
57impl FromObject for Boolean {
58    fn from_object(obj: Object) -> Result<Self, Error> {
59        match obj.kind() {
60            ObjectKind::Boolean => Ok(unsafe { obj.as_boolean_unchecked() }),
61
62            other => Err(Error::FromWrongType {
63                expected: "bool",
64                actual: other.as_static(),
65            }),
66        }
67    }
68}
69
70impl FromObject for Integer {
71    fn from_object(obj: Object) -> Result<Self, Error> {
72        match obj.kind() {
73            ObjectKind::Integer => Ok(unsafe { obj.as_integer_unchecked() }),
74
75            other => Err(Error::FromWrongType {
76                expected: "integer",
77                actual: other.as_static(),
78            }),
79        }
80    }
81}
82
83impl FromObject for Float {
84    fn from_object(obj: Object) -> Result<Self, Error> {
85        match obj.kind() {
86            ObjectKind::Float => Ok(unsafe { obj.as_float_unchecked() }),
87
88            other => Err(Error::FromWrongType {
89                expected: "float",
90                actual: other.as_static(),
91            }),
92        }
93    }
94}
95
96impl FromObject for crate::String {
97    fn from_object(obj: Object) -> Result<Self, Error> {
98        match obj.kind() {
99            ObjectKind::String => Ok(unsafe { obj.into_string_unchecked() }),
100
101            other => Err(Error::FromWrongType {
102                expected: "string",
103                actual: other.as_static(),
104            }),
105        }
106    }
107}
108
109impl FromObject for Array {
110    fn from_object(obj: Object) -> Result<Self, Error> {
111        match obj.kind() {
112            ObjectKind::Array => Ok(unsafe { obj.into_array_unchecked() }),
113
114            other => Err(Error::FromWrongType {
115                expected: "string",
116                actual: other.as_static(),
117            }),
118        }
119    }
120}
121
122impl FromObject for Dictionary {
123    fn from_object(obj: Object) -> Result<Self, Error> {
124        match obj.kind() {
125            ObjectKind::Dictionary => Ok(unsafe { obj.into_dict_unchecked() }),
126
127            other => Err(Error::FromWrongType {
128                expected: "string",
129                actual: other.as_static(),
130            }),
131        }
132    }
133}
134
135impl<A, R> FromObject for Function<A, R> {
136    fn from_object(obj: Object) -> Result<Self, Error> {
137        match obj.kind() {
138            ObjectKind::LuaRef => {
139                Ok(Self::from_ref(unsafe { obj.as_luaref_unchecked() }))
140            },
141
142            other => Err(Error::FromWrongType {
143                expected: "function",
144                actual: other.as_static(),
145            }),
146        }
147    }
148}
149
150/// Implements `FromObject` for a type that implements `From<Integer>`.
151macro_rules! from_int {
152    ($integer:ty) => {
153        impl FromObject for $integer {
154            fn from_object(obj: Object) -> Result<Self, Error> {
155                Integer::from_object(obj).map(Into::into)
156            }
157        }
158    };
159}
160
161from_int!(i128);
162
163/// Implements `FromObject` for a type that implements `TryFrom<Integer>`.
164macro_rules! try_from_int {
165    ($integer:ty) => {
166        impl FromObject for $integer {
167            fn from_object(obj: Object) -> Result<Self, Error> {
168                Integer::from_object(obj).and_then(|n| Ok(n.try_into()?))
169            }
170        }
171    };
172}
173
174try_from_int!(i8);
175try_from_int!(u8);
176try_from_int!(i16);
177try_from_int!(u16);
178try_from_int!(i32);
179try_from_int!(u32);
180try_from_int!(u64);
181try_from_int!(u128);
182try_from_int!(isize);
183try_from_int!(usize);
184
185impl FromObject for f32 {
186    fn from_object(obj: Object) -> Result<Self, Error> {
187        Ok(Float::from_object(obj)? as _)
188    }
189}
190
191impl FromObject for String {
192    fn from_object(obj: Object) -> Result<Self, Error> {
193        crate::String::from_object(obj)
194            .and_then(|nvim_str| Ok(nvim_str.into_string()?))
195    }
196}
197
198impl<T> FromObject for Option<T>
199where
200    T: FromObject,
201{
202    fn from_object(obj: Object) -> Result<Self, Error> {
203        (!obj.is_nil()).then(|| T::from_object(obj)).transpose()
204    }
205}
206
207impl<T> FromObject for Vec<T>
208where
209    T: FromObject,
210{
211    fn from_object(obj: Object) -> Result<Self, Error> {
212        Array::from_object(obj)?
213            .into_iter()
214            .map(FromObject::from_object)
215            .collect()
216    }
217}
218
219impl<T> ToObject for T
220where
221    T: Into<Object>,
222{
223    fn to_object(self) -> Result<Object, Error> {
224        Ok(self.into())
225    }
226}
227
228/// Implements `ToObject` for "big integer" types.
229macro_rules! bigint_to_obj {
230    ($type:ty) => {
231        impl ToObject for $type {
232            fn to_object(self) -> Result<Object, Error> {
233                Ok(i64::try_from(self)?.into())
234            }
235        }
236    };
237}
238
239bigint_to_obj!(u64);
240bigint_to_obj!(isize);
241bigint_to_obj!(usize);
242bigint_to_obj!(i128);
243bigint_to_obj!(u128);
244
245impl<T> ToObject for Vec<T>
246where
247    T: ToObject,
248{
249    fn to_object(self) -> Result<Object, Error> {
250        Ok(self
251            .into_iter()
252            .map(ToObject::to_object)
253            .collect::<Result<Array, Error>>()?
254            .into())
255    }
256}
257
258impl<K, V> ToObject for HashMap<K, V>
259where
260    K: Into<crate::String>,
261    V: ToObject,
262{
263    fn to_object(self) -> Result<Object, Error> {
264        self.into_iter()
265            .map(|(k, v)| Ok((k, v.to_object()?)))
266            .collect::<Result<Dictionary, Error>>()
267            .map(Into::into)
268    }
269}