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