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