graphql_starter/pagination/
cursor.rs1use base64::{prelude::BASE64_URL_SAFE_NO_PAD, Engine};
2use serde::{
3 de::{DeserializeOwned, Error as SerdeError},
4 Deserialize, Deserializer, Serialize, Serializer,
5};
6
7use super::PaginationErrorCode;
8use crate::error::{MapToErr, Result};
9
10#[derive(Debug, Clone, PartialEq, Eq)]
12pub struct OpaqueCursor(Vec<u8>);
13impl OpaqueCursor {
14 pub fn decode(cursor: impl AsRef<str>) -> Result<Self> {
16 let data = BASE64_URL_SAFE_NO_PAD.decode(cursor.as_ref()).map_to_err_with(
17 PaginationErrorCode::PageInvalidCursor,
18 "Couldn't decode the cursor as base64",
19 )?;
20
21 Ok(Self(data))
22 }
23
24 pub fn encode(&self) -> String {
26 BASE64_URL_SAFE_NO_PAD.encode(&self.0)
27 }
28
29 pub fn new<T>(data: &T) -> Result<Self>
31 where
32 T: Serialize,
33 {
34 Ok(Self(
35 serde_json::to_vec(data).map_to_internal_err("Couldn't serialize a cursor")?,
36 ))
37 }
38
39 pub fn as_data<T>(&self) -> Result<T>
41 where
42 T: DeserializeOwned,
43 {
44 serde_json::from_slice(&self.0).map_to_err_with(
45 PaginationErrorCode::PageInvalidCursor,
46 "Couldn't deserialize the cursor into the expected type",
47 )
48 }
49}
50impl Serialize for OpaqueCursor {
51 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
52 where
53 S: Serializer,
54 {
55 serializer.serialize_str(&self.encode())
56 }
57}
58impl<'de> Deserialize<'de> for OpaqueCursor {
59 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
60 where
61 D: Deserializer<'de>,
62 {
63 let cursor: String = serde::Deserialize::deserialize(deserializer)?;
64 Self::decode(cursor).map_err(|err| D::Error::custom(err.info().message()))
65 }
66}