xand_api_client/
errors.rs1use serde::Serialize;
2use snafu::Snafu;
3use std::sync::Arc;
4use tonic::{transport::Error, Code, Status};
5use xand_api_proto::error::XandApiProtoErrs;
6use xand_api_proto::proto_models::TransactionIdError;
7#[derive(Debug, Snafu, Clone, Serialize)]
9#[snafu(visibility(pub))]
10pub enum XandApiClientError {
11 TransportError {
12 #[snafu(source(from(tonic::transport::Error, Arc::new)))]
13 #[serde(serialize_with = "xand_utils::snafu_extensions::debug_serialize")]
14 source: Arc<tonic::transport::Error>,
15 },
16 #[snafu(display("Requested entity not found: {}", message))]
17 NotFound {
18 message: String,
19 },
20 #[snafu(display("Bad request: {}", message))]
21 BadRequest {
22 message: String,
23 },
24 OtherGrpcError {
26 #[snafu(source(from(tonic::Status, Arc::new)))]
27 #[serde(serialize_with = "xand_utils::snafu_extensions::debug_serialize")]
28 source: Arc<tonic::Status>,
29 },
30 ProtoError {
31 source: XandApiProtoErrs,
32 },
33 #[snafu(display("Transaction ID returned by xand-api is invalid: {}", source))]
34 #[snafu(context(false))]
35 InvalidIdInTransaction {
36 source: TransactionIdError,
37 },
38 #[snafu(display("xand-api reply was missing required data: {}", message))]
39 BadReplyError {
40 message: String,
41 },
42 #[snafu(display("Transaction state is unknown"))]
43 UnknownTransactionStatus,
44 #[snafu(display("Invalid address {}", address))]
45 InvalidAddress {
46 address: String,
47 },
48 #[snafu(display("Invalid CIDR block {}", cidr_block))]
49 InvalidCidrBlock {
50 cidr_block: String,
51 },
52 #[snafu(display("The operation timed out"))]
53 Timeout,
54
55 #[snafu(display(
56 "Your request was not authorized. Please check your credentials and try again: {}",
57 message
58 ))]
59 Unauthorized {
60 message: String,
61 },
62}
63
64impl From<tonic::transport::Error> for XandApiClientError {
65 fn from(e: Error) -> Self {
66 XandApiClientError::TransportError {
67 source: Arc::new(e),
68 }
69 }
70}
71
72impl From<tonic::Status> for XandApiClientError {
73 fn from(e: Status) -> Self {
74 match e.code() {
75 Code::NotFound => XandApiClientError::NotFound {
76 message: e.message().to_owned(),
77 },
78 Code::InvalidArgument => XandApiClientError::BadRequest {
79 message: e.message().to_owned(),
80 },
81 Code::Unauthenticated => XandApiClientError::Unauthorized {
82 message: e.message().to_owned(),
83 },
84 _ => XandApiClientError::OtherGrpcError { source: e.into() },
85 }
86 }
87}
88
89impl From<XandApiProtoErrs> for XandApiClientError {
90 fn from(e: XandApiProtoErrs) -> Self {
91 XandApiClientError::ProtoError { source: e }
92 }
93}
94
95#[cfg(test)]
96mod test {
97 use super::*;
98
99 #[allow(unconditional_recursion, dead_code)]
102 fn send_test<T: Send>(_: T) {
103 send_test(XandApiClientError::BadRequest {
104 message: "hi".to_string(),
105 });
106 }
107
108 #[test]
109 fn unauthorized() {
110 let status = Status::new(Code::Unauthenticated, "some arbitrary message".to_string());
111 let error: XandApiClientError = status.into();
112
113 assert!(matches!(
114 error,
115 XandApiClientError::Unauthorized {
116 message
117 } if message == "some arbitrary message"
118 ));
119 }
120}