sui_graphql_rpc/
connection.rs

1// Copyright (c) Mysten Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4use std::borrow::Cow;
5use std::marker::PhantomData;
6
7use async_graphql::connection::{
8    ConnectionNameType, CursorType, DefaultConnectionName, DefaultEdgeName, Edge, EdgeNameType,
9    EmptyFields, EnableNodesField, NodesFieldSwitcherSealed, PageInfo,
10};
11use async_graphql::{Object, ObjectType, OutputType, TypeName};
12
13/// Mirrors the `Connection` type from async-graphql, with the exception that if `start_cursor` and/
14/// or `end_cursor` is set on the struct, then when `page_info` is called, it will use those values
15/// before deferring to `edges`. The default implementation derives these cursors from the first and
16/// last element of `edges`, so if `edges` is empty, both are set to null. This is undesirable for
17/// queries that make use of `scan_limit`; when the scan limit is reached, a caller can continue to
18/// paginate forwards or backwards until all candidates in the scanning range have been visited,
19/// even if the current page yields no results.
20pub(crate) struct ScanConnection<
21    Cursor,
22    Node,
23    EdgeFields = EmptyFields,
24    Name = DefaultConnectionName,
25    EdgeName = DefaultEdgeName,
26    NodesField = EnableNodesField,
27> where
28    Cursor: CursorType + Send + Sync,
29    Node: OutputType,
30    EdgeFields: ObjectType,
31    Name: ConnectionNameType,
32    EdgeName: EdgeNameType,
33    NodesField: NodesFieldSwitcherSealed,
34{
35    _mark1: PhantomData<Name>,
36    _mark2: PhantomData<EdgeName>,
37    _mark3: PhantomData<NodesField>,
38    pub edges: Vec<Edge<Cursor, Node, EdgeFields, EdgeName>>,
39    pub has_previous_page: bool,
40    pub has_next_page: bool,
41    pub start_cursor: Option<String>,
42    pub end_cursor: Option<String>,
43}
44
45#[Object(name_type)]
46impl<Cursor, Node, EdgeFields, Name, EdgeName>
47    ScanConnection<Cursor, Node, EdgeFields, Name, EdgeName, EnableNodesField>
48where
49    Cursor: CursorType + Send + Sync,
50    Node: OutputType,
51    EdgeFields: ObjectType,
52    Name: ConnectionNameType,
53    EdgeName: EdgeNameType,
54{
55    /// Information to aid in pagination.
56    async fn page_info(&self) -> PageInfo {
57        // Unlike the default implementation, this Connection will use `start_cursor` and
58        // `end_cursor` if they are `Some`.
59        PageInfo {
60            has_previous_page: self.has_previous_page,
61            has_next_page: self.has_next_page,
62            start_cursor: self
63                .start_cursor
64                .clone()
65                .or_else(|| self.edges.first().map(|edge| edge.cursor.encode_cursor())),
66            end_cursor: self
67                .end_cursor
68                .clone()
69                .or_else(|| self.edges.last().map(|edge| edge.cursor.encode_cursor())),
70        }
71    }
72
73    /// A list of edges.
74    #[inline]
75    async fn edges(&self) -> &[Edge<Cursor, Node, EdgeFields, EdgeName>] {
76        &self.edges
77    }
78
79    /// A list of nodes.
80    async fn nodes(&self) -> Vec<&Node> {
81        self.edges.iter().map(|e| &e.node).collect()
82    }
83}
84
85impl<Cursor, Node, NodesField, EdgeFields, Name, EdgeName>
86    ScanConnection<Cursor, Node, EdgeFields, Name, EdgeName, NodesField>
87where
88    Cursor: CursorType + Send + Sync,
89    Node: OutputType,
90    EdgeFields: ObjectType,
91    Name: ConnectionNameType,
92    EdgeName: EdgeNameType,
93    NodesField: NodesFieldSwitcherSealed,
94{
95    /// Create a new connection.
96    #[inline]
97    pub fn new(has_previous_page: bool, has_next_page: bool) -> Self {
98        ScanConnection {
99            _mark1: PhantomData,
100            _mark2: PhantomData,
101            _mark3: PhantomData,
102            edges: Vec::new(),
103            has_previous_page,
104            has_next_page,
105            start_cursor: None,
106            end_cursor: None,
107        }
108    }
109}
110
111impl<Cursor, Node, EdgeFields, Name, EdgeName, NodesField> TypeName
112    for ScanConnection<Cursor, Node, EdgeFields, Name, EdgeName, NodesField>
113where
114    Cursor: CursorType + Send + Sync,
115    Node: OutputType,
116    EdgeFields: ObjectType,
117    Name: ConnectionNameType,
118    EdgeName: EdgeNameType,
119    NodesField: NodesFieldSwitcherSealed,
120{
121    #[inline]
122    fn type_name() -> Cow<'static, str> {
123        Name::type_name::<Node>().into()
124    }
125}