graphql_starter/pagination/
cursor.rs

1use 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/// Opaque cursor
11#[derive(Debug, Clone, PartialEq, Eq)]
12pub struct OpaqueCursor(Vec<u8>);
13impl OpaqueCursor {
14    /// Decodes the given base64 string into an [OpaqueCursor]
15    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    /// Encodes this [OpaqueCursor] into a base64 string
25    pub fn encode(&self) -> String {
26        BASE64_URL_SAFE_NO_PAD.encode(&self.0)
27    }
28
29    /// Serializes any data into an [OpaqueCursor]
30    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    /// Deserializes the [OpaqueCursor] into the given data type
40    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}