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