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#[derive(Copy, Clone, Debug, PartialEq, Eq, Default)]
11pub enum UnknownFieldAction {
12 Ignore,
14 #[default]
16 Warn,
17 Deny,
19}
20
21#[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
37pub 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 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}