1use crate::definition::{SerializedFormat, StringFormat};
2use crate::json_reading::{
3 extract_array, extract_bool, extract_f64, extract_i64, extract_object, extract_str,
4 FromJsonError,
5};
6use crate::{SerializedFormatKind, StringFormatKind};
7use serde_json::{Number, Value};
8use std::collections::{BTreeMap, BTreeSet};
9use std::convert::TryInto;
10use std::error::Error;
11use std::fmt::Debug;
12use std::fmt::Write;
13use std::str::FromStr;
14#[cfg(feature = "uuid")]
15use uuid::Uuid;
16
17pub trait FeattleValue: Debug + Sized {
25 fn as_json(&self) -> Value;
27
28 fn overview(&self) -> String;
31
32 fn try_from_json(value: &Value) -> Result<Self, FromJsonError>;
34
35 fn serialized_format() -> SerializedFormat;
38}
39
40pub trait FeattleStringValue: FromStr + ToString + Debug
51where
52 <Self as FromStr>::Err: Error + Send + Sync + 'static,
53{
54 fn serialized_string_format() -> StringFormat;
57}
58
59impl<T> FeattleValue for T
60where
61 T: FeattleStringValue,
62 <T as FromStr>::Err: Error + Send + Sync + 'static,
63{
64 fn as_json(&self) -> Value {
65 Value::String(self.to_string())
66 }
67 fn overview(&self) -> String {
68 self.to_string()
69 }
70 fn try_from_json(value: &Value) -> Result<Self, FromJsonError> {
71 extract_str(value)?.parse().map_err(FromJsonError::parsing)
72 }
73 fn serialized_format() -> SerializedFormat {
74 let f = Self::serialized_string_format();
75 SerializedFormat {
76 kind: SerializedFormatKind::String(f.kind),
77 tag: f.tag,
78 }
79 }
80}
81
82impl FeattleValue for bool {
83 fn as_json(&self) -> Value {
84 Value::Bool(*self)
85 }
86 fn overview(&self) -> String {
87 self.to_string()
88 }
89 fn try_from_json(value: &Value) -> Result<Self, FromJsonError> {
90 extract_bool(value)
91 }
92 fn serialized_format() -> SerializedFormat {
93 SerializedFormat {
94 kind: SerializedFormatKind::Bool,
95 tag: "bool".to_owned(),
96 }
97 }
98}
99
100macro_rules! impl_try_from_value_i64 {
101 ($kind:ty) => {
102 impl FeattleValue for $kind {
103 fn as_json(&self) -> Value {
104 serde_json::to_value(*self).unwrap()
105 }
106 fn overview(&self) -> String {
107 self.to_string()
108 }
109 fn try_from_json(value: &Value) -> Result<Self, FromJsonError> {
110 extract_i64(value)?
111 .try_into()
112 .map_err(FromJsonError::parsing)
113 }
114 fn serialized_format() -> SerializedFormat {
115 SerializedFormat {
116 kind: SerializedFormatKind::Integer,
117 tag: stringify!($kind).to_owned(),
118 }
119 }
120 }
121 };
122}
123
124impl_try_from_value_i64! {u8}
125impl_try_from_value_i64! {i8}
126impl_try_from_value_i64! {u16}
127impl_try_from_value_i64! {i16}
128impl_try_from_value_i64! {u32}
129impl_try_from_value_i64! {i32}
130impl_try_from_value_i64! {u64}
131impl_try_from_value_i64! {i64}
132impl_try_from_value_i64! {usize}
133impl_try_from_value_i64! {isize}
134
135impl FeattleValue for f32 {
136 fn as_json(&self) -> Value {
137 Value::Number(Number::from_f64(*self as f64).unwrap())
138 }
139 fn overview(&self) -> String {
140 self.to_string()
141 }
142 fn try_from_json(value: &Value) -> Result<Self, FromJsonError> {
143 let n_64 = extract_f64(value)?;
144 let n_32 = n_64 as f32;
145 if (n_64 - n_32 as f64).abs() > 1e-6 {
146 Err(FromJsonError::WrongKind {
147 actual: "Number::f64",
148 expected: "Number::f32",
149 })
150 } else {
151 Ok(n_32)
152 }
153 }
154 fn serialized_format() -> SerializedFormat {
155 SerializedFormat {
156 kind: SerializedFormatKind::Float,
157 tag: "f32".to_owned(),
158 }
159 }
160}
161
162impl FeattleValue for f64 {
163 fn as_json(&self) -> Value {
164 Value::Number(Number::from_f64(*self).unwrap())
165 }
166 fn overview(&self) -> String {
167 self.to_string()
168 }
169 fn try_from_json(value: &Value) -> Result<Self, FromJsonError> {
170 extract_f64(value)
171 }
172 fn serialized_format() -> SerializedFormat {
173 SerializedFormat {
174 kind: SerializedFormatKind::Float,
175 tag: "f64".to_owned(),
176 }
177 }
178}
179
180#[cfg(feature = "uuid")]
181impl FeattleStringValue for Uuid {
182 fn serialized_string_format() -> StringFormat {
183 StringFormat {
184 kind: StringFormatKind::Pattern(
185 "[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{12}",
186 ),
187 tag: "Uuid".to_owned(),
188 }
189 }
190}
191
192impl FeattleStringValue for String {
193 fn serialized_string_format() -> StringFormat {
194 StringFormat {
195 kind: StringFormatKind::Any,
196 tag: "String".to_owned(),
197 }
198 }
199}
200
201impl<T: FeattleValue> FeattleValue for Vec<T> {
202 fn as_json(&self) -> Value {
203 Value::Array(self.iter().map(|item| item.as_json()).collect())
204 }
205 fn overview(&self) -> String {
206 format!("[{}]", iter_overview(self.iter()))
207 }
208 fn try_from_json(value: &Value) -> Result<Self, FromJsonError> {
209 let mut list = Vec::new();
210 for item in extract_array(value)? {
211 list.push(T::try_from_json(item)?);
212 }
213 Ok(list)
214 }
215 fn serialized_format() -> SerializedFormat {
216 let f = T::serialized_format();
217 SerializedFormat {
218 kind: SerializedFormatKind::List(Box::new(f.kind)),
219 tag: format!("Vec<{}>", f.tag),
220 }
221 }
222}
223
224impl<T: FeattleValue + Ord> FeattleValue for BTreeSet<T> {
225 fn as_json(&self) -> Value {
226 Value::Array(self.iter().map(|item| item.as_json()).collect())
227 }
228 fn overview(&self) -> String {
229 format!("[{}]", iter_overview(self.iter()))
230 }
231 fn try_from_json(value: &Value) -> Result<Self, FromJsonError> {
232 let mut set = BTreeSet::new();
233 for item in extract_array(value)? {
234 set.insert(T::try_from_json(item)?);
235 }
236 Ok(set)
237 }
238 fn serialized_format() -> SerializedFormat {
239 let f = T::serialized_format();
240 SerializedFormat {
241 kind: SerializedFormatKind::Set(Box::new(f.kind)),
242 tag: format!("Set<{}>", f.tag),
243 }
244 }
245}
246
247impl<K: FeattleStringValue + Ord, V: FeattleValue> FeattleValue for BTreeMap<K, V>
248where
249 <K as FromStr>::Err: Error + Send + Sync + 'static,
250{
251 fn as_json(&self) -> Value {
252 Value::Object(
253 self.iter()
254 .map(|(item_key, item_value)| (item_key.to_string(), item_value.as_json()))
255 .collect(),
256 )
257 }
258 fn overview(&self) -> String {
259 let mut keys_by_value: BTreeMap<_, Vec<_>> = BTreeMap::new();
261 for (key, value) in self {
262 keys_by_value.entry(value.overview()).or_default().push(key);
263 }
264
265 let overview_by_value: Vec<_> = keys_by_value
266 .into_iter()
267 .map(|(value, keys)| format!("{}: {}", iter_overview(keys.into_iter()), value))
268 .collect();
269
270 format!("{{{}}}", iter_overview(overview_by_value.iter()))
271 }
272 fn try_from_json(value: &Value) -> Result<Self, FromJsonError> {
273 let mut map = BTreeMap::new();
274 for (item_key, item_value) in extract_object(value)? {
275 map.insert(
276 item_key.parse().map_err(FromJsonError::parsing)?,
277 V::try_from_json(item_value)?,
278 );
279 }
280 Ok(map)
281 }
282 fn serialized_format() -> SerializedFormat {
283 let fk = K::serialized_string_format();
284 let fv = V::serialized_format();
285 SerializedFormat {
286 kind: SerializedFormatKind::Map(fk.kind, Box::new(fv.kind)),
287 tag: format!("Map<{}, {}>", fk.tag, fv.tag),
288 }
289 }
290}
291
292impl<T: FeattleValue> FeattleValue for Option<T> {
293 fn as_json(&self) -> Value {
294 match self {
295 None => Value::Null,
296 Some(inner) => inner.as_json(),
297 }
298 }
299 fn overview(&self) -> String {
300 match self {
301 None => "None".to_owned(),
302 Some(s) => format!("Some({})", s.overview()),
303 }
304 }
305 fn try_from_json(value: &Value) -> Result<Self, FromJsonError> {
306 match value {
307 Value::Null => Ok(None),
308 other => T::try_from_json(other).map(Some),
309 }
310 }
311 fn serialized_format() -> SerializedFormat {
312 let f = T::serialized_format();
313 SerializedFormat {
314 kind: SerializedFormatKind::Optional(Box::new(f.kind)),
315 tag: format!("Option<{}>", f.tag),
316 }
317 }
318}
319
320fn iter_overview<'a, T: FeattleValue + 'a>(iter: impl Iterator<Item = &'a T>) -> String {
321 const MAX_ITEMS: usize = 3;
322 let mut overview = String::new();
323 let mut iter = iter.enumerate();
324
325 while let Some((i, value)) = iter.next() {
326 if i == MAX_ITEMS {
327 write!(overview, ", ... {} more", iter.count() + 1).unwrap();
328 break;
329 } else if i > 0 {
330 overview += ", ";
331 }
332 overview += &value.overview();
333 }
334
335 overview
336}
337
338#[cfg(test)]
339mod tests {
340 use super::*;
341 use serde_json::json;
342
343 fn converts<T: FeattleValue + PartialEq>(value: Value, parsed: T, overview: &str) {
344 converts2(value.clone(), parsed, overview, value);
345 }
346
347 fn converts2<T: FeattleValue + PartialEq>(
348 value: Value,
349 parsed: T,
350 overview: &str,
351 converted: Value,
352 ) {
353 assert_eq!(parsed.as_json(), converted);
354 assert_eq!(parsed.overview(), overview);
355 assert_eq!(T::try_from_json(&value).ok(), Some(parsed));
356 }
357
358 fn fails<T: FeattleValue + PartialEq>(value: Value) {
359 assert_eq!(T::try_from_json(&value).ok(), None);
360 }
361
362 #[test]
363 fn bool() {
364 converts(json!(true), true, "true");
365 converts(json!(false), false, "false");
366
367 fails::<bool>(json!(0));
368 fails::<bool>(json!(null));
369
370 assert_eq!(bool::serialized_format().kind, SerializedFormatKind::Bool);
371 }
372
373 #[test]
374 fn int() {
375 fn basic<T: FeattleValue + PartialEq>(parsed: T) {
376 converts(json!(17), parsed, "17");
377 fails::<T>(json!(17.5));
378 fails::<T>(json!(null));
379 assert_eq!(T::serialized_format().kind, SerializedFormatKind::Integer);
380 }
381
382 basic(17u8);
383 basic(17i8);
384 basic(17u16);
385 basic(17i16);
386 basic(17u32);
387 basic(17i32);
388 basic(17u64);
389 basic(17i64);
390 basic(17usize);
391 basic(17isize);
392
393 fails::<u8>(json!(-17));
394 converts(json!(-17), -17i8, "-17");
395 fails::<u16>(json!(-17));
396 converts(json!(-17), -17i16, "-17");
397 fails::<u32>(json!(-17));
398 converts(json!(-17), -17i32, "-17");
399 fails::<u64>(json!(-17));
400 converts(json!(-17), -17i64, "-17");
401 fails::<usize>(json!(-17));
402 converts(json!(-17), -17isize, "-17");
403
404 let overview = u32::MAX.to_string();
405 fails::<u8>(json!(u32::MAX));
406 fails::<i8>(json!(u32::MAX));
407 fails::<u16>(json!(u32::MAX));
408 fails::<i16>(json!(u32::MAX));
409 converts(json!(u32::MAX), u32::MAX, &overview);
410 fails::<i32>(json!(u32::MAX));
411 converts(json!(u32::MAX), u32::MAX as u64, &overview);
412 converts(json!(u32::MAX), u32::MAX as i64, &overview);
413 converts(json!(u32::MAX), u32::MAX as usize, &overview);
414 converts(json!(u32::MAX), u32::MAX as isize, &overview);
415 }
416
417 #[test]
418 fn float() {
419 converts2(json!(17), 17f32, "17", json!(17.0));
420 converts2(json!(17), 17f64, "17", json!(17.0));
421 converts(json!(17.5), 17.5f32, "17.5");
422 converts(json!(17.5), 17.5f64, "17.5");
423
424 fails::<bool>(json!(null));
425
426 assert_eq!(f32::serialized_format().kind, SerializedFormatKind::Float);
427 assert_eq!(f64::serialized_format().kind, SerializedFormatKind::Float);
428 }
429
430 #[test]
431 #[cfg(feature = "uuid")]
432 fn uuid() {
433 converts(
434 json!("8886fc87-93e1-4d08-9722-9fc1411b6b96"),
435 Uuid::parse_str("8886fc87-93e1-4d08-9722-9fc1411b6b96").unwrap(),
436 "8886fc87-93e1-4d08-9722-9fc1411b6b96",
437 );
438
439 fails::<Uuid>(json!("yadayada"));
440 let kind = Uuid::serialized_format().kind;
441 match kind {
442 SerializedFormatKind::String(StringFormatKind::Pattern(_)) => {}
443 _ => panic!("invalid serialized format kind: {:?}", kind),
444 }
445 }
446
447 #[test]
448 fn string() {
449 converts(json!("17"), "17".to_owned(), "17");
450 converts(json!(""), "".to_owned(), "");
451 fails::<String>(json!(17));
452 fails::<String>(json!(null));
453 assert_eq!(
454 String::serialized_format().kind,
455 SerializedFormatKind::String(StringFormatKind::Any)
456 );
457 }
458
459 #[test]
460 fn vec() {
461 converts(json!([3, 14, 15]), vec![3i32, 14, 15], "[3, 14, 15]");
462 converts(
463 json!([3, 14, 15, 92]),
464 vec![3i32, 14, 15, 92],
465 "[3, 14, 15, ... 1 more]",
466 );
467 converts(
468 json!([3, 14, 15, 92, 65, 35]),
469 vec![3i32, 14, 15, 92, 65, 35],
470 "[3, 14, 15, ... 3 more]",
471 );
472 fails::<Vec<i32>>(json!([3, 14, "15", 92]));
473 assert_eq!(
474 Vec::<i32>::serialized_format().kind,
475 SerializedFormatKind::List(Box::new(SerializedFormatKind::Integer))
476 )
477 }
478
479 #[test]
480 fn set() {
481 converts(
482 json!([3, 14, 15]),
483 vec![3, 14, 15].into_iter().collect::<BTreeSet<i32>>(),
484 "[3, 14, 15]",
485 );
486 converts2(
487 json!([1, 2, 4, 4, 3]),
488 vec![1, 2, 3, 4].into_iter().collect::<BTreeSet<i32>>(),
489 "[1, 2, 3, ... 1 more]",
490 json!([1, 2, 3, 4]),
491 );
492 fails::<BTreeSet<i32>>(json!([3, 14, "15", 92]));
493 assert_eq!(
494 BTreeSet::<i32>::serialized_format().kind,
495 SerializedFormatKind::Set(Box::new(SerializedFormatKind::Integer))
496 )
497 }
498
499 #[test]
500 fn map() {
501 converts(
502 json!({
503 "a": 1,
504 "b": 2,
505 "x": 1,
506 }),
507 vec![
508 ("a".to_owned(), 1),
509 ("b".to_owned(), 2),
510 ("x".to_owned(), 1),
511 ]
512 .into_iter()
513 .collect::<BTreeMap<_, _>>(),
514 "{a, x: 1, b: 2}",
515 );
516 fails::<BTreeMap<String, String>>(json!({
517 "a": "1",
518 "b": 2,
519 "x": 1,
520 }));
521 assert_eq!(
522 BTreeMap::<String, i32>::serialized_format().kind,
523 SerializedFormatKind::Map(
524 StringFormatKind::Any,
525 Box::new(SerializedFormatKind::Integer)
526 )
527 )
528 }
529
530 #[test]
531 fn option() {
532 converts(json!(17), Some(17), "Some(17)");
533 converts(json!(null), None::<i32>, "None");
534 fails::<Option<i32>>(json!(17.5));
535 assert_eq!(
536 Option::<i32>::serialized_format().kind,
537 SerializedFormatKind::Optional(Box::new(SerializedFormatKind::Integer))
538 )
539 }
540
541 #[test]
542 fn choices() {
543 use crate::feattle_enum;
544 feattle_enum! {enum Choices { Red, Green, Blue }};
545
546 converts(json!("Red"), Choices::Red, "Red");
547 fails::<Choices>(json!("Black"));
548 assert_eq!(
549 Choices::serialized_format().kind,
550 SerializedFormatKind::String(StringFormatKind::Choices(&["Red", "Green", "Blue"]))
551 )
552 }
553}