Add Cursor scalar (#48)

* Add Cursor scalar
This commit is contained in:
Blaine Bublitz 2020-05-04 22:21:45 -07:00 committed by GitHub
parent a5b2272b59
commit eaa50a52e0
6 changed files with 79 additions and 9 deletions

View File

@ -1,3 +1,4 @@
use crate::types::connection::cursor::Cursor;
use crate::types::connection::edge::Edge;
use crate::types::connection::page_info::PageInfo;
use crate::{
@ -18,7 +19,7 @@ use std::collections::HashMap;
pub struct Connection<T, E: ObjectType + Sync + Send = EmptyEdgeFields> {
total_count: Option<usize>,
page_info: PageInfo,
nodes: Vec<(String, E, T)>,
nodes: Vec<(Cursor, E, T)>,
}
impl<T, E: ObjectType + Sync + Send> Connection<T, E> {
@ -27,7 +28,7 @@ impl<T, E: ObjectType + Sync + Send> Connection<T, E> {
total_count: Option<usize>,
has_previous_page: bool,
has_next_page: bool,
nodes: Vec<(String, E, T)>,
nodes: Vec<(Cursor, E, T)>,
) -> Self {
Connection {
total_count,

View File

@ -0,0 +1,66 @@
use crate::{Result, ScalarType, Value};
use async_graphql_derive::Scalar;
use std::ops::{Deref, DerefMut};
/// Cursor scalar
///
/// A custom scalar that serializes as a string.
/// https://relay.dev/graphql/connections.htm#sec-Cursor
#[derive(Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug)]
pub struct Cursor(String);
impl std::fmt::Display for Cursor {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
impl Deref for Cursor {
type Target = String;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl DerefMut for Cursor {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl From<String> for Cursor {
fn from(value: String) -> Self {
Cursor(value)
}
}
impl<'a> From<&'a str> for Cursor {
fn from(value: &'a str) -> Self {
Cursor(value.to_string())
}
}
impl From<usize> for Cursor {
fn from(value: usize) -> Self {
Cursor(value.to_string())
}
}
#[Scalar(internal)]
impl ScalarType for Cursor {
fn type_name() -> &'static str {
"Cursor"
}
fn parse(value: &Value) -> Option<Self> {
match value {
Value::String(s) => Some(Cursor(s.into())),
_ => None,
}
}
fn to_json(&self) -> Result<serde_json::Value> {
Ok(self.0.to_string().into())
}
}

View File

@ -1,4 +1,5 @@
mod connection_type;
mod cursor;
mod edge;
mod page_info;
mod slice;
@ -6,6 +7,7 @@ mod slice;
use crate::{Context, FieldResult, ObjectType};
pub use connection_type::Connection;
pub use cursor::Cursor;
/// Connection query operation
pub enum QueryOperation<'a> {
@ -77,7 +79,7 @@ pub struct EmptyEdgeFields;
/// }
/// };
///
/// let nodes = (start..end).into_iter().map(|n| (base64::encode(n.to_be_bytes()), DiffFields {diff: n - 1000}, n)).collect();
/// let nodes = (start..end).into_iter().map(|n| (base64::encode(n.to_be_bytes()).into(), DiffFields {diff: n - 1000}, n)).collect();
/// Ok(Connection::new(None, true, true, nodes))
/// }
/// }

View File

@ -1,10 +1,11 @@
use crate::types::connection::cursor::Cursor;
use async_graphql_derive::Object;
pub struct PageInfo {
pub has_previous_page: bool,
pub has_next_page: bool,
pub start_cursor: Option<String>,
pub end_cursor: Option<String>,
pub start_cursor: Option<Cursor>,
pub end_cursor: Option<Cursor>,
}
#[Object(internal)]
@ -20,12 +21,12 @@ impl PageInfo {
}
#[field(desc = "When paginating backwards, the cursor to continue.")]
async fn start_cursor(&self) -> &Option<String> {
async fn start_cursor(&self) -> &Option<Cursor> {
&self.start_cursor
}
#[field(desc = "When paginating forwards, the cursor to continue.")]
async fn end_cursor(&self) -> &Option<String> {
async fn end_cursor(&self) -> &Option<Cursor> {
&self.end_cursor
}
}

View File

@ -35,7 +35,7 @@ impl<'a, T: Sync> DataSource for &'a [T] {
let mut nodes = Vec::with_capacity(end - start);
for (idx, item) in self[start..end].iter().enumerate() {
nodes.push((
base64::encode((idx as u32).to_be_bytes()),
base64::encode((idx as u32).to_be_bytes()).into(),
EmptyEdgeFields,
item,
));

View File

@ -7,7 +7,7 @@ mod optional;
mod query_root;
mod upload;
pub use connection::{Connection, DataSource, EmptyEdgeFields, QueryOperation};
pub use connection::{Connection, Cursor, DataSource, EmptyEdgeFields, QueryOperation};
pub use empty_mutation::EmptyMutation;
pub use empty_subscription::EmptySubscription;
pub use query_root::QueryRoot;