1use 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
34pub trait FromObject: Sized {
36 fn from_object(object: Object) -> Result<Self, Error>;
37}
38
39pub 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
159macro_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
172macro_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
237macro_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}