grafbase_sdk/types/elements/
response.rs

1use crate::{SdkError, cbor, sealed::Sealed, types::authorization::private::QueryElementOrResponseItem, wit};
2use serde::Deserialize;
3
4use super::QueryElementId;
5
6/// A list of elements present in the query on which one of the extension's directive was applied on their definition.
7#[derive(Clone, Copy)]
8pub struct ResponseElements<'a>(&'a wit::ResponseElements);
9
10impl<'a> From<&'a wit::ResponseElements> for ResponseElements<'a> {
11    fn from(value: &'a wit::ResponseElements) -> Self {
12        Self(value)
13    }
14}
15
16// is never empty, otherwise we wouldn't call the extension at all
17#[allow(clippy::len_without_is_empty)]
18impl<'a> ResponseElements<'a> {
19    /// Number of elements within the query
20    pub fn len(&self) -> usize {
21        self.0.elements.len()
22    }
23
24    /// Iterate over all elements, regardless of the directive they're associated with. Useful if
25    /// expect only one directive to be used.
26    pub fn iter(&self) -> impl ExactSizeIterator<Item = ResponseElement<'a>> + 'a {
27        (*self).into_iter()
28    }
29
30    /// Iterate over all elements grouped by the directive name.
31    pub fn iter_grouped_by_directive_name(
32        &self,
33    ) -> impl ExactSizeIterator<Item = (&'a str, impl ExactSizeIterator<Item = ResponseElement<'a>> + 'a)> + 'a {
34        let resp = self.0;
35        let items = &resp.items;
36        self.0.directive_names.iter().map(move |(name, start, end)| {
37            let start = *start;
38            (
39                name.as_str(),
40                resp.elements[start as usize..*end as usize]
41                    .iter()
42                    .map(move |inner| ResponseElement {
43                        items,
44                        query_element_id: QueryElementId(inner.query_element_id),
45                        items_range: inner.items_range,
46                    }),
47            )
48        })
49    }
50}
51
52impl<'a> IntoIterator for ResponseElements<'a> {
53    type Item = ResponseElement<'a>;
54    type IntoIter = ResponseElementsIterator<'a>;
55    fn into_iter(self) -> Self::IntoIter {
56        ResponseElementsIterator {
57            items: &self.0.items,
58            iter: self.0.elements.iter(),
59        }
60    }
61}
62
63/// Iterator over the elements of the query on which a directive was applied.
64pub struct ResponseElementsIterator<'a> {
65    items: &'a [Vec<u8>],
66    iter: std::slice::Iter<'a, wit::ResponseElement>,
67}
68
69impl ExactSizeIterator for ResponseElementsIterator<'_> {}
70
71impl<'a> Iterator for ResponseElementsIterator<'a> {
72    type Item = ResponseElement<'a>;
73    fn next(&mut self) -> Option<Self::Item> {
74        let items = self.items;
75        self.iter.next().map(move |inner| ResponseElement {
76            items,
77            query_element_id: QueryElementId(inner.query_element_id),
78            items_range: inner.items_range,
79        })
80    }
81    fn size_hint(&self) -> (usize, Option<usize>) {
82        self.iter.size_hint()
83    }
84}
85
86/// An element of the query on which a directive was applied.
87#[derive(Clone, Copy)]
88pub struct ResponseElement<'a> {
89    items: &'a [Vec<u8>],
90    query_element_id: QueryElementId,
91    items_range: (u32, u32),
92}
93
94impl<'a> ResponseElement<'a> {
95    /// When a directive requires response data, it'll be processed in two stages:
96    /// - authorize_query will first receive the query element and all the arguments that do not
97    ///   depend on response data. This allows fetching any relevant data from an external service.
98    ///   Any data you need for later must be kept in the state vector.
99    /// - authorize_response will receive the response data, but nothing else. Only a
100    ///   `QueryElementId` is provided to allow finding any relevant data in the state vector.
101    pub fn query_element_id(&self) -> QueryElementId {
102        self.query_element_id
103    }
104
105    /// Arguments of the directive with any query data injected. Any argument that depends on
106    /// response data will not be present here and be provided separately.
107    pub fn items(&self) -> impl ExactSizeIterator<Item = ResponseItem<'a>> {
108        let (start, end) = self.items_range;
109        self.items[start as usize..end as usize]
110            .iter()
111            .enumerate()
112            .map(move |(offset, bytes)| ResponseItem {
113                bytes,
114                ix: start + offset as u32,
115            })
116    }
117}
118
119impl Sealed for ResponseItem<'_> {}
120impl QueryElementOrResponseItem for ResponseItem<'_> {
121    fn ix(&self) -> u32 {
122        self.ix
123    }
124}
125
126/// Represents a single item, object or field, that is subject to an authorization rule with the
127/// data requested by the directive.
128pub struct ResponseItem<'a> {
129    bytes: &'a [u8],
130    pub(in crate::types) ix: u32,
131}
132
133impl<'a> ResponseItem<'a> {
134    /// Arguments that depend on response data will be provided here. All other arguments will only
135    /// be provided in the `authorize_query()` step today.
136    pub fn directive_arguments<T>(&self) -> Result<T, SdkError>
137    where
138        T: Deserialize<'a>,
139    {
140        cbor::from_slice(self.bytes).map_err(Into::into)
141    }
142}