1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
use std::{collections::BTreeMap, sync::Arc};

use serde::de;

use crate::ir::FieldValue;

mod deserializers;

#[cfg(test)]
mod tests;

/// Deserialize Trustfall query results into a Rust struct.
///
/// ```rust
/// # use std::{collections::BTreeMap, sync::Arc};
/// # use maplit::btreemap;
/// # use trustfall_core::ir::FieldValue;
/// #
/// # fn run_query() -> Result<Box<dyn Iterator<Item = BTreeMap<Arc<str>, FieldValue>>>, ()> {
/// #     Ok(Box::new(vec![
/// #        btreemap! {
/// #           Arc::from("number") => FieldValue::Int64(42),
/// #           Arc::from("text") => FieldValue::String("the answer to everything".to_string()),
/// #        }
/// #     ].into_iter()))
/// # }
///
/// use trustfall_core::TryIntoStruct;
///
/// #[derive(Debug, PartialEq, Eq, serde::Deserialize)]
/// struct Output {
///     number: i64,
///     text: String,
/// }
///
/// let results: Vec<_> = run_query()
///     .expect("bad query arguments")
///     .map(|v| v.try_into_struct().expect("struct definition did not match query result shape"))
///     .collect();
///
/// assert_eq!(
///     vec![
///         Output {
///             number: 42,
///             text: "the answer to everything".to_string(),
///         },
///     ],
///     results,
/// );
/// ```
pub trait TryIntoStruct {
    type Error;

    fn try_into_struct<S: for<'de> de::Deserialize<'de>>(self) -> Result<S, Self::Error>;
}

impl TryIntoStruct for BTreeMap<Arc<str>, FieldValue> {
    type Error = deserializers::Error;

    fn try_into_struct<S: for<'de> de::Deserialize<'de>>(self) -> Result<S, deserializers::Error> {
        let deserializer = deserializers::QueryResultDeserializer::new(self);
        S::deserialize(deserializer)
    }
}