async_graphql/types/connection/
cursor.rs

1use std::{
2    char::ParseCharError,
3    convert::Infallible,
4    fmt::Display,
5    num::{ParseFloatError, ParseIntError},
6    ops::{Deref, DerefMut},
7    str::ParseBoolError,
8};
9
10use serde::{Serialize, de::DeserializeOwned};
11
12use crate::ID;
13
14/// Cursor type
15///
16/// A custom scalar that serializes as a string.
17/// <https://relay.dev/graphql/connections.htm#sec-Cursor>
18pub trait CursorType: Sized {
19    /// Error type for `decode_cursor`.
20    type Error: Display;
21
22    /// Decode cursor from string.
23    fn decode_cursor(s: &str) -> Result<Self, Self::Error>;
24
25    /// Encode cursor to string.
26    fn encode_cursor(&self) -> String;
27}
28
29macro_rules! cursor_type_int_impl {
30    ($($t:ty)*) => {$(
31        impl CursorType for $t {
32            type Error = ParseIntError;
33
34            fn decode_cursor(s: &str) -> Result<Self, Self::Error> {
35                s.parse()
36            }
37
38            fn encode_cursor(&self) -> String {
39                self.to_string()
40            }
41        }
42    )*}
43}
44
45cursor_type_int_impl! { isize i8 i16 i32 i64 i128 usize u8 u16 u32 u64 u128 }
46
47impl CursorType for f32 {
48    type Error = ParseFloatError;
49
50    fn decode_cursor(s: &str) -> Result<Self, Self::Error> {
51        s.parse()
52    }
53
54    fn encode_cursor(&self) -> String {
55        self.to_string()
56    }
57}
58
59impl CursorType for f64 {
60    type Error = ParseFloatError;
61
62    fn decode_cursor(s: &str) -> Result<Self, Self::Error> {
63        s.parse()
64    }
65
66    fn encode_cursor(&self) -> String {
67        self.to_string()
68    }
69}
70
71impl CursorType for char {
72    type Error = ParseCharError;
73
74    fn decode_cursor(s: &str) -> Result<Self, Self::Error> {
75        s.parse()
76    }
77
78    fn encode_cursor(&self) -> String {
79        self.to_string()
80    }
81}
82
83impl CursorType for bool {
84    type Error = ParseBoolError;
85
86    fn decode_cursor(s: &str) -> Result<Self, Self::Error> {
87        s.parse()
88    }
89
90    fn encode_cursor(&self) -> String {
91        self.to_string()
92    }
93}
94
95impl CursorType for String {
96    type Error = Infallible;
97
98    fn decode_cursor(s: &str) -> Result<Self, Self::Error> {
99        Ok(s.to_string())
100    }
101
102    fn encode_cursor(&self) -> String {
103        self.clone()
104    }
105}
106
107impl CursorType for ID {
108    type Error = Infallible;
109
110    fn decode_cursor(s: &str) -> Result<Self, Self::Error> {
111        Ok(s.to_string().into())
112    }
113
114    fn encode_cursor(&self) -> String {
115        self.to_string()
116    }
117}
118
119#[cfg(feature = "chrono")]
120impl CursorType for chrono::DateTime<chrono::Utc> {
121    type Error = chrono::ParseError;
122
123    fn decode_cursor(s: &str) -> Result<Self, Self::Error> {
124        Ok(chrono::DateTime::parse_from_rfc3339(s)?.with_timezone::<chrono::Utc>(&chrono::Utc {}))
125    }
126
127    fn encode_cursor(&self) -> String {
128        self.to_rfc3339_opts(chrono::SecondsFormat::Micros, true)
129    }
130}
131
132#[cfg(feature = "uuid")]
133impl CursorType for uuid::Uuid {
134    type Error = uuid::Error;
135
136    fn decode_cursor(s: &str) -> Result<Self, Self::Error> {
137        s.parse()
138    }
139
140    fn encode_cursor(&self) -> String {
141        self.to_string()
142    }
143}
144
145/// A opaque cursor that encode/decode the value to base64
146pub struct OpaqueCursor<T>(pub T);
147
148impl<T> Deref for OpaqueCursor<T> {
149    type Target = T;
150
151    #[inline]
152    fn deref(&self) -> &Self::Target {
153        &self.0
154    }
155}
156
157impl<T> DerefMut for OpaqueCursor<T> {
158    #[inline]
159    fn deref_mut(&mut self) -> &mut Self::Target {
160        &mut self.0
161    }
162}
163
164impl<T> CursorType for OpaqueCursor<T>
165where
166    T: Serialize + DeserializeOwned,
167{
168    type Error = Box<dyn std::error::Error + Send + Sync>;
169
170    fn decode_cursor(s: &str) -> Result<Self, Self::Error> {
171        use base64::Engine;
172
173        let data = base64::engine::general_purpose::URL_SAFE_NO_PAD.decode(s)?;
174        Ok(Self(serde_json::from_slice(&data)?))
175    }
176
177    fn encode_cursor(&self) -> String {
178        use base64::Engine;
179
180        let value = serde_json::to_vec(&self.0).unwrap_or_default();
181        base64::engine::general_purpose::URL_SAFE_NO_PAD.encode(value)
182    }
183}