grafbase_sdk/types/elements/
query.rs

1use std::iter::Enumerate;
2
3use crate::{
4    SdkError,
5    types::{DirectiveSite, authorization::private::QueryElementOrResponseItem},
6    wit,
7};
8use serde::Deserialize;
9
10/// A list of elements present in the query on which one of the extension's directive was applied on their definition.
11#[derive(Clone, Copy)]
12pub struct QueryElements<'a>(&'a wit::QueryElements);
13
14impl<'a> From<&'a wit::QueryElements> for QueryElements<'a> {
15    fn from(value: &'a wit::QueryElements) -> Self {
16        Self(value)
17    }
18}
19
20// is never empty, otherwise we wouldn't call the extension at all
21#[allow(clippy::len_without_is_empty)]
22impl<'a> QueryElements<'a> {
23    /// Number of elements within the query
24    pub fn len(&self) -> usize {
25        self.0.elements.len()
26    }
27
28    /// Iterate over all elements, regardless of the directive they're associated with. Useful if
29    /// expect only one directive to be used.
30    pub fn iter(&self) -> impl ExactSizeIterator<Item = QueryElement<'a>> + 'a {
31        (*self).into_iter()
32    }
33
34    /// Iterate over all elements grouped by the directive name.
35    pub fn iter_grouped_by_directive_name(
36        &self,
37    ) -> impl ExactSizeIterator<Item = (&'a str, impl ExactSizeIterator<Item = QueryElement<'a>> + 'a)> + 'a {
38        let query = self.0;
39        self.0.directive_names.iter().map(|(name, start, end)| {
40            let start = *start;
41            (
42                name.as_str(),
43                query.elements[start as usize..*end as usize]
44                    .iter()
45                    .enumerate()
46                    .map(move |(i, element)| QueryElement {
47                        element,
48                        ix: start + i as u32,
49                    }),
50            )
51        })
52    }
53}
54
55impl<'a> IntoIterator for QueryElements<'a> {
56    type Item = QueryElement<'a>;
57    type IntoIter = QueryElementsIterator<'a>;
58    fn into_iter(self) -> Self::IntoIter {
59        QueryElementsIterator(self.0.elements.iter().enumerate())
60    }
61}
62
63/// Iterator over the elements of the query on which a directive was applied.
64pub struct QueryElementsIterator<'a>(Enumerate<std::slice::Iter<'a, wit::QueryElement>>);
65
66impl ExactSizeIterator for QueryElementsIterator<'_> {}
67
68impl<'a> Iterator for QueryElementsIterator<'a> {
69    type Item = QueryElement<'a>;
70    fn next(&mut self) -> Option<Self::Item> {
71        self.0
72            .next()
73            .map(move |(ix, element)| QueryElement { element, ix: ix as u32 })
74    }
75    fn size_hint(&self) -> (usize, Option<usize>) {
76        self.0.size_hint()
77    }
78}
79
80impl crate::sealed::Sealed for QueryElement<'_> {}
81impl QueryElementOrResponseItem for QueryElement<'_> {
82    fn ix(&self) -> u32 {
83        self.ix
84    }
85}
86
87/// An element of the query on which a directive was applied.
88#[derive(Clone, Copy)]
89pub struct QueryElement<'a> {
90    element: &'a wit::QueryElement,
91    ix: u32,
92}
93
94/// An identifier for a query element. Only relevant for response authorization as data provided in
95/// `authorize_query` won't be re-sent in `authorize_response`. So this ID allows finding the
96/// relevant data in the custom state.
97#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, serde::Serialize, serde::Deserialize)]
98pub struct QueryElementId(pub(super) u32);
99
100impl From<QueryElementId> for u32 {
101    fn from(value: QueryElementId) -> u32 {
102        value.0
103    }
104}
105
106impl std::fmt::Display for QueryElementId {
107    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
108        write!(f, "{}", self.0)
109    }
110}
111
112impl<'a> QueryElement<'a> {
113    /// ID of the query element, only relevant for response authorization.
114    pub fn id(&self) -> QueryElementId {
115        QueryElementId(self.element.id)
116    }
117
118    /// Directive site, where and with which arguments, of the directive associated with this element.
119    /// The provided arguments will exclude anything that depend on response data such as
120    /// `FieldSet`.
121    pub fn directive_site(&self) -> DirectiveSite<'a> {
122        (&self.element.site).into()
123    }
124
125    /// Subgraph name from which this element is requested from. Only present if the authorization extension
126    /// was declared with the `subgraph` grouping:
127    /// ```toml
128    /// # extension.toml
129    /// [authorization]
130    /// group_by = ["subgraph"]
131    /// ```
132    pub fn subgraph_name(&self) -> Option<&'a str> {
133        self.element.subgraph_name.as_deref()
134    }
135
136    /// Arguments of the directive with any query data injected. Any argument that depends on
137    /// response data will not be present here and be provided separately.
138    pub fn directive_arguments<T>(&self) -> Result<T, SdkError>
139    where
140        T: Deserialize<'a>,
141    {
142        minicbor_serde::from_slice(&self.element.arguments).map_err(Into::into)
143    }
144}