concordium_rust_sdk/
endpoints.rs

1//! Wrapper for the node's GRPC API. The return values are parsed and wrapped in
2//! structured values.
3use crate::types;
4use derive_more::From;
5use thiserror::Error;
6use tonic::metadata::errors::InvalidMetadataValue;
7pub use tonic::transport::{Endpoint, Error};
8
9#[derive(Error, Debug)]
10/// Authentication, connection, or response parsing error.
11pub enum RPCError {
12    #[error("Call failed: {0}")]
13    CallError(#[from] tonic::Status),
14    #[error(transparent)]
15    InvalidMetadata(#[from] InvalidMetadataValue),
16    #[error("Error parsing JSON result: {0}")]
17    ParseError(#[from] anyhow::Error),
18}
19
20impl From<serde_json::Error> for RPCError {
21    fn from(x: serde_json::Error) -> Self {
22        Self::ParseError(x.into())
23    }
24}
25
26impl From<semver::Error> for RPCError {
27    fn from(x: semver::Error) -> Self {
28        Self::ParseError(x.into())
29    }
30}
31
32impl RPCError {
33    /// Return whether the error indicates the item being sent is invalid.
34    /// Retrying a request in this case will likely not succeed.
35    ///
36    /// Although some conditions like that are transient.
37    pub fn is_invalid_argument(&self) -> bool {
38        match self {
39            RPCError::CallError(e) => {
40                matches!(e.code(), tonic::Code::InvalidArgument)
41            }
42            RPCError::InvalidMetadata(_) => false,
43            RPCError::ParseError(_) => false,
44        }
45    }
46
47    /// Return whether the object already exists at the node.
48    /// Retrying a request in this case will likely not succeed.
49    pub fn is_duplicate(&self) -> bool {
50        match self {
51            RPCError::CallError(e) => {
52                matches!(e.code(), tonic::Code::AlreadyExists)
53            }
54            RPCError::InvalidMetadata(_) => false,
55            RPCError::ParseError(_) => false,
56        }
57    }
58}
59
60#[derive(Error, Debug)]
61/// Errors that can occur when making queries. This can either be a general
62/// connection/authentication error, or the requested item is not found.
63pub enum QueryError {
64    #[error("RPC error: {0}")]
65    /// A general RPC error occurred.
66    RPCError(#[from] RPCError),
67    #[error("Requested object not found.")]
68    /// The requested item was not found.
69    NotFound,
70}
71
72impl QueryError {
73    /// Whether this error indicates an object was not found.
74    pub fn is_not_found(&self) -> bool {
75        match self {
76            QueryError::RPCError(c) => {
77                if let RPCError::CallError(ce) = c {
78                    ce.code() == tonic::Code::NotFound
79                } else {
80                    false
81                }
82            }
83            QueryError::NotFound => true,
84        }
85    }
86}
87
88impl From<tonic::Status> for QueryError {
89    fn from(s: tonic::Status) -> Self {
90        Self::RPCError(s.into())
91    }
92}
93
94impl From<InvalidMetadataValue> for QueryError {
95    fn from(s: InvalidMetadataValue) -> Self {
96        Self::RPCError(s.into())
97    }
98}
99
100/// Result a GRPC query. This is a simple alias for [std::Result](https://doc.rust-lang.org/std/result/enum.Result.html)
101/// that fixes the error type to be [RPCError].
102pub type RPCResult<A> = Result<A, RPCError>;
103
104/// Result a GRPC query where the item lookup might fail.
105/// This is a simple alias for [std::Result](https://doc.rust-lang.org/std/result/enum.Result.html) that fixes the error type to be [`QueryError`].
106pub type QueryResult<A> = Result<A, QueryError>;
107
108/// Input to the
109/// [`get_blocks_at_height`](crate::v2::Client::get_blocks_at_height) query.
110#[derive(Clone, Copy, Debug, From)]
111pub enum BlocksAtHeightInput {
112    Absolute {
113        /// Height from the beginning of the chain.
114        height: types::AbsoluteBlockHeight,
115    },
116    /// Query relative to an explicit genesis index.
117    Relative {
118        /// Genesis index to start from.
119        genesis_index: types::GenesisIndex,
120        /// Height starting from the genesis block at the genesis index.
121        height: types::BlockHeight,
122        /// Whether to return results only from the specified genesis index
123        /// (`true`), or allow results from more recent genesis indices
124        /// as well (`false`).
125        restrict: bool,
126    },
127}