wezterm_dynamic/
fromdynamic.rs

1use crate::error::Error;
2use crate::value::Value;
3use ordered_float::OrderedFloat;
4use std::collections::HashMap;
5use std::convert::TryInto;
6use std::hash::Hash;
7
8/// Specify how FromDynamic will treat unknown fields
9/// when converting from Value to a given target type
10#[derive(Copy, Clone, Debug, PartialEq, Eq, Default)]
11pub enum UnknownFieldAction {
12    /// Don't check, don't warn, don't raise an error
13    Ignore,
14    /// Emit a log::warn log
15    #[default]
16    Warn,
17    /// Return an Error
18    Deny,
19}
20
21/// Specify various options for FromDynamic::from_dynamic
22#[derive(Copy, Clone, Debug, Default)]
23pub struct FromDynamicOptions {
24    pub unknown_fields: UnknownFieldAction,
25    pub deprecated_fields: UnknownFieldAction,
26}
27
28impl FromDynamicOptions {
29    pub fn flatten(self) -> Self {
30        Self {
31            unknown_fields: UnknownFieldAction::Ignore,
32            ..self
33        }
34    }
35}
36
37/// The FromDynamic trait allows a type to construct itself from a Value.
38/// This trait can be derived.
39pub trait FromDynamic {
40    fn from_dynamic(value: &Value, options: FromDynamicOptions) -> Result<Self, Error>
41    where
42        Self: Sized;
43}
44
45impl FromDynamic for Value {
46    fn from_dynamic(value: &Value, _options: FromDynamicOptions) -> Result<Self, Error> {
47        Ok(value.clone())
48    }
49}
50
51impl FromDynamic for ordered_float::NotNan<f64> {
52    fn from_dynamic(value: &Value, options: FromDynamicOptions) -> Result<Self, Error> {
53        let f = f64::from_dynamic(value, options)?;
54        Ok(ordered_float::NotNan::new(f).map_err(|e| Error::Message(e.to_string()))?)
55    }
56}
57
58impl FromDynamic for std::time::Duration {
59    fn from_dynamic(value: &Value, options: FromDynamicOptions) -> Result<Self, Error> {
60        let f = f64::from_dynamic(value, options)?;
61        Ok(std::time::Duration::from_secs_f64(f))
62    }
63}
64
65impl<T: FromDynamic> FromDynamic for Box<T> {
66    fn from_dynamic(value: &Value, options: FromDynamicOptions) -> Result<Self, Error> {
67        let value = T::from_dynamic(value, options)?;
68        Ok(Box::new(value))
69    }
70}
71
72impl<T: FromDynamic> FromDynamic for std::sync::Arc<T> {
73    fn from_dynamic(value: &Value, options: FromDynamicOptions) -> Result<Self, Error> {
74        let value = T::from_dynamic(value, options)?;
75        Ok(std::sync::Arc::new(value))
76    }
77}
78
79impl<T: FromDynamic> FromDynamic for Option<T> {
80    fn from_dynamic(value: &Value, options: FromDynamicOptions) -> Result<Self, Error> {
81        match value {
82            Value::Null => Ok(None),
83            value => Ok(Some(T::from_dynamic(value, options)?)),
84        }
85    }
86}
87
88impl<T: FromDynamic, const N: usize> FromDynamic for [T; N] {
89    fn from_dynamic(value: &Value, options: FromDynamicOptions) -> Result<Self, Error> {
90        match value {
91            Value::Array(arr) => {
92                let v = arr
93                    .iter()
94                    .map(|v| T::from_dynamic(v, options))
95                    .collect::<Result<Vec<T>, Error>>()?;
96                v.try_into().map_err(|v: Vec<T>| Error::ArraySizeMismatch {
97                    vec_size: v.len(),
98                    array_size: N,
99                })
100            }
101            other => Err(Error::NoConversion {
102                source_type: other.variant_name().to_string(),
103                dest_type: "array",
104            }),
105        }
106    }
107}
108
109impl<K: FromDynamic + Eq + Hash, T: FromDynamic> FromDynamic for HashMap<K, T> {
110    fn from_dynamic(value: &Value, options: FromDynamicOptions) -> Result<Self, Error> {
111        match value {
112            Value::Object(obj) => {
113                let mut map = HashMap::with_capacity(obj.len());
114                for (k, v) in obj.iter() {
115                    map.insert(K::from_dynamic(k, options)?, T::from_dynamic(v, options)?);
116                }
117                Ok(map)
118            }
119            other => Err(Error::NoConversion {
120                source_type: other.variant_name().to_string(),
121                dest_type: "HashMap",
122            }),
123        }
124    }
125}
126
127impl<T: FromDynamic> FromDynamic for Vec<T> {
128    fn from_dynamic(value: &Value, options: FromDynamicOptions) -> Result<Self, Error> {
129        match value {
130            Value::Array(arr) => Ok(arr
131                .iter()
132                .map(|v| T::from_dynamic(v, options))
133                .collect::<Result<Vec<T>, Error>>()?),
134            // lua uses tables for everything; we can end up here if we got an empty
135            // table and treated it as an object. Allow that to stand-in for an empty
136            // array instead.
137            Value::Object(obj) if obj.is_empty() => Ok(vec![]),
138            other => Err(Error::NoConversion {
139                source_type: other.variant_name().to_string(),
140                dest_type: "Vec",
141            }),
142        }
143    }
144}
145
146impl FromDynamic for () {
147    fn from_dynamic(value: &Value, _options: FromDynamicOptions) -> Result<Self, Error> {
148        match value {
149            Value::Null => Ok(()),
150            other => Err(Error::NoConversion {
151                source_type: other.variant_name().to_string(),
152                dest_type: "()",
153            }),
154        }
155    }
156}
157
158impl FromDynamic for bool {
159    fn from_dynamic(value: &Value, _options: FromDynamicOptions) -> Result<Self, Error> {
160        match value {
161            Value::Bool(b) => Ok(*b),
162            other => Err(Error::NoConversion {
163                source_type: other.variant_name().to_string(),
164                dest_type: "bool",
165            }),
166        }
167    }
168}
169
170impl FromDynamic for std::path::PathBuf {
171    fn from_dynamic(value: &Value, _options: FromDynamicOptions) -> Result<Self, Error> {
172        match value {
173            Value::String(s) => Ok(s.into()),
174            other => Err(Error::NoConversion {
175                source_type: other.variant_name().to_string(),
176                dest_type: "PathBuf",
177            }),
178        }
179    }
180}
181
182impl FromDynamic for char {
183    fn from_dynamic(value: &Value, _options: FromDynamicOptions) -> Result<Self, Error> {
184        match value {
185            Value::String(s) => {
186                let mut iter = s.chars();
187                let c = iter.next().ok_or(Error::CharFromWrongSizedString)?;
188                if iter.next().is_some() {
189                    Err(Error::CharFromWrongSizedString)
190                } else {
191                    Ok(c)
192                }
193            }
194            other => Err(Error::NoConversion {
195                source_type: other.variant_name().to_string(),
196                dest_type: "char",
197            }),
198        }
199    }
200}
201
202impl FromDynamic for String {
203    fn from_dynamic(value: &Value, _options: FromDynamicOptions) -> Result<Self, Error> {
204        match value {
205            Value::String(s) => Ok(s.to_string()),
206            other => Err(Error::NoConversion {
207                source_type: other.variant_name().to_string(),
208                dest_type: "String",
209            }),
210        }
211    }
212}
213
214macro_rules! int {
215    ($($ty:ty),* $(,)?) => {
216        $(
217impl FromDynamic for $ty {
218    fn from_dynamic(value: &Value, _options: FromDynamicOptions) -> Result<Self, Error> {
219        match value {
220            Value::I64(n) => match (*n).try_into() {
221                Ok(n) => Ok(n),
222                Err(err) => Err(Error::Message(err.to_string())),
223            },
224            Value::U64(n) => match (*n).try_into() {
225                Ok(n) => Ok(n),
226                Err(err) => Err(Error::Message(err.to_string())),
227            },
228            other => Err(Error::NoConversion{
229                source_type:other.variant_name().to_string(),
230                dest_type: stringify!($ty),
231            })
232        }
233    }
234}
235        )*
236    }
237}
238
239int!(i8, i16, i32, i64, isize, u8, u16, u32, u64, usize);
240
241impl FromDynamic for f32 {
242    fn from_dynamic(value: &Value, _options: FromDynamicOptions) -> Result<Self, Error> {
243        match value {
244            Value::F64(OrderedFloat(n)) => Ok((*n) as f32),
245            Value::I64(n) => Ok((*n) as f32),
246            Value::U64(n) => Ok((*n) as f32),
247            other => Err(Error::NoConversion {
248                source_type: other.variant_name().to_string(),
249                dest_type: "f32",
250            }),
251        }
252    }
253}
254
255impl FromDynamic for f64 {
256    fn from_dynamic(value: &Value, _options: FromDynamicOptions) -> Result<Self, Error> {
257        match value {
258            Value::F64(OrderedFloat(n)) => Ok(*n),
259            Value::I64(n) => Ok((*n) as f64),
260            Value::U64(n) => Ok((*n) as f64),
261            other => Err(Error::NoConversion {
262                source_type: other.variant_name().to_string(),
263                dest_type: "f64",
264            }),
265        }
266    }
267}