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
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
use ;
use DeserializeOwned;
use crateFieldValue;
/// Deserialize Trustfall query results or edge parameters into a Rust struct.
///
/// # Use with query results
///
/// Running a Trustfall query produces an iterator of `BTreeMap<Arc<str>, FieldValue>` outputs
/// representing the query results. These maps all have a common "shape" — the same keys and
/// the same value types — as determined by the query and schema.
///
/// This trait allows deserializing those query result maps into a dedicated struct,
/// to get you easy access to strongly-typed data instead of [`FieldValue`] enums.
///
/// ## Example
///
/// Say we ran a query like:
/// ```graphql
/// query {
/// Order {
/// item_name @output
/// quantity @output
/// }
/// }
/// ```
///
/// Each of this query's outputs contain a string named `item_name` and an integer named `quantity`.
/// This trait allows us to define an output struct type:
/// ```rust
/// #[derive(Debug, PartialEq, Eq, serde::Deserialize)]
/// struct Output {
/// item_name: String,
/// quantity: i64,
/// }
/// ```
///
/// We can then unpack the query results into an iterator of such structs:
/// ```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("item_name") => FieldValue::String("widget".into()),
/// # Arc::from("quantity") => FieldValue::Int64(42),
/// # }
/// # ].into_iter()))
/// # }
/// #
/// # #[derive(Debug, PartialEq, Eq, serde::Deserialize)]
/// # struct Output {
/// # item_name: String,
/// # quantity: i64,
/// # }
///
/// use trustfall_core::TryIntoStruct;
///
/// 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 {
/// item_name: "widget".to_string(),
/// quantity: 42,
/// },
/// ],
/// results,
/// );
/// ```
///
/// # Use with edge parameters
///
/// Edges defined in Trustfall schemas may take parameters, for example:
/// ```graphql
/// type NewsWebsite {
/// latest_stories(count: Int!): [Story!]!
/// }
/// ```
///
/// This trait can be used to deserialize [`&EdgeParameters`](crate::ir::EdgeParameters)
/// into a struct specific to the parameters of that edge:
/// ```rust
/// #[derive(Debug, PartialEq, Eq, serde::Deserialize)]
/// struct LatestStoriesParameters {
/// count: usize
/// }
/// ```
///
/// For example:
/// ```rust
/// # use trustfall_core::{ir::EdgeParameters, interpreter::ContextIterator};
/// #
/// # #[derive(Debug, Clone)]
/// # struct Vertex;
/// #
/// # #[derive(Debug, PartialEq, Eq, serde::Deserialize)]
/// # struct LatestStoriesParameters {
/// # count: usize
/// # }
///
/// use trustfall_core::TryIntoStruct;
///
/// fn resolve_latest_stories(contexts: ContextIterator<Vertex>, parameters: &EdgeParameters) {
/// let parameters: LatestStoriesParameters = parameters
/// .try_into_struct()
/// .expect("edge parameters did not match struct definition");
/// let count = parameters.count;
///
/// // then resolve the edge with the given count
/// }
/// ```