sui_indexer_alt_jsonrpc/
paginate.rs

1// Copyright (c) Mysten Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4use std::ops::Deref;
5
6use fastcrypto::encoding::Base64;
7use fastcrypto::encoding::Encoding;
8use fastcrypto::error::FastCryptoError;
9use serde::Serialize;
10use serde::de::DeserializeOwned;
11
12use crate::error::RpcError;
13use crate::error::invalid_params;
14
15pub(crate) trait Cursor: Sized {
16    /// Interpret the string as a cursor, Base64-decode it, and then deserialize it from JSON. A
17    /// failure to do so implies the cursor is invalid, which is treated as a user error.
18    fn decode(s: &str) -> Result<Self, Error>;
19
20    /// Represent the cursor in JSON, Base64-encoded. A failure implies the cursor is not properly
21    /// set-up, which is treated as an internal error.
22    fn encode(&self) -> Result<String, Error>;
23}
24
25/// Wraps a value used as a cursor in a paginated request or response. This cursor format
26/// serializes to BCS and then encodes as Base64.
27pub(crate) struct BcsCursor<T>(pub T);
28
29/// Wraps a value used as a cursor in a paginated request or response. This cursor format
30/// serializes to JSON and then encodes as Base64.
31pub(crate) struct JsonCursor<T>(pub T);
32
33/// Description of a page to be fetched.
34pub(crate) struct Page<C: Cursor> {
35    pub cursor: Option<C>,
36    pub limit: i64,
37    pub descending: bool,
38}
39
40#[derive(thiserror::Error, Debug)]
41pub(crate) enum Error {
42    #[error("Failed to decode Base64: {0}")]
43    DecodingBase64(FastCryptoError),
44
45    #[error("Failed to decode BCS: {0}")]
46    DecodingBcs(bcs::Error),
47
48    #[error("Failed to decode JSON: {0}")]
49    DecodingJson(serde_json::error::Error),
50
51    #[error("Failed to encode BCS: {0}")]
52    EncodingBcs(bcs::Error),
53
54    #[error("Failed to encode JSON: {0}")]
55    EncodingJson(serde_json::error::Error),
56
57    #[error("Requested page size {requested} exceeds maximum {max}")]
58    ExceededMaxPageSize { requested: usize, max: usize },
59}
60
61impl<T: Serialize + DeserializeOwned> Cursor for BcsCursor<T> {
62    fn decode(s: &str) -> Result<Self, Error> {
63        let bytes = Base64::decode(s).map_err(Error::DecodingBase64)?;
64        let value = bcs::from_bytes(&bytes).map_err(Error::DecodingBcs)?;
65        Ok(BcsCursor(value))
66    }
67
68    fn encode(&self) -> Result<String, Error> {
69        let bytes = bcs::to_bytes(&self.0).map_err(Error::EncodingBcs)?;
70        Ok(Base64::encode(&bytes))
71    }
72}
73
74impl<T: Serialize + DeserializeOwned> Cursor for JsonCursor<T> {
75    fn decode(s: &str) -> Result<Self, Error> {
76        let bytes = Base64::decode(s).map_err(Error::DecodingBase64)?;
77        let value: T = serde_json::from_slice(&bytes).map_err(Error::DecodingJson)?;
78        Ok(JsonCursor(value))
79    }
80
81    fn encode(&self) -> Result<String, Error> {
82        let bytes = serde_json::to_vec(&self.0).map_err(Error::EncodingJson)?;
83        Ok(Base64::encode(&bytes))
84    }
85}
86
87impl<C: Cursor> Page<C> {
88    /// Interpret RPC method parameters as a description of a page to fetch.
89    ///
90    /// This operation can fail if the Cursor cannot be decoded, or the requested page is too
91    /// large. These are all consider user errors.
92    pub(crate) fn from_params<E: From<Error> + std::error::Error>(
93        default_page_size: usize,
94        max_page_size: usize,
95        cursor: Option<String>,
96        limit: Option<usize>,
97        descending: Option<bool>,
98    ) -> Result<Self, RpcError<E>> {
99        let cursor = cursor
100            .map(|c| C::decode(&c))
101            .transpose()
102            .map_err(|e| invalid_params(E::from(e)))?;
103
104        let limit = limit.unwrap_or(default_page_size);
105        if limit > max_page_size {
106            return Err(invalid_params(E::from(Error::ExceededMaxPageSize {
107                requested: limit,
108                max: max_page_size,
109            })));
110        }
111
112        Ok(Page {
113            cursor,
114            limit: limit as i64,
115            descending: descending.unwrap_or(false),
116        })
117    }
118}
119
120impl<T> Deref for BcsCursor<T> {
121    type Target = T;
122
123    fn deref(&self) -> &T {
124        &self.0
125    }
126}
127
128impl<T> Deref for JsonCursor<T> {
129    type Target = T;
130
131    fn deref(&self) -> &T {
132        &self.0
133    }
134}