1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
use crate::types::{ErrorCode, ErrorResponse};
use prost::bytes::Bytes;
use prost::{DecodeError, Message};
use reqwest::StatusCode;
use std::error::Error;
use std::fmt::{Display, Formatter};

/// When there is an error while writing to VSS storage, the response contains a relevant error code.
/// A mapping from a VSS server error codes. Refer to [`ErrorResponse`] docs for more
/// information regarding each error code and corresponding use-cases.
#[derive(Debug)]
pub enum VssError {
	/// Please refer to [`ErrorCode::NoSuchKeyException`].
	NoSuchKeyError(String),

	/// Please refer to [`ErrorCode::InvalidRequestException`].
	InvalidRequestError(String),

	/// Please refer to [`ErrorCode::ConflictException`].
	ConflictError(String),

	/// Please refer to [`ErrorCode::InternalServerException`].
	InternalServerError(String),

	/// There is an unknown error, it could be a client-side bug, unrecognized error-code, network error
	/// or something else.
	InternalError(String),
}

impl VssError {
	/// Create new instance of `VssError`
	pub fn new(status: StatusCode, payload: Bytes) -> VssError {
		match ErrorResponse::decode(&payload[..]) {
			Ok(error_response) => VssError::from(error_response),
			Err(e) => {
				let message =
					format!("Unable to decode ErrorResponse from server, HttpStatusCode: {}, DecodeErr: {}", status, e);
				VssError::InternalError(message)
			}
		}
	}
}

impl Display for VssError {
	fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
		match self {
			VssError::NoSuchKeyError(message) => {
				write!(f, "Requested key does not exist: {}", message)
			}
			VssError::InvalidRequestError(message) => {
				write!(f, "Request sent to VSS Storage was invalid: {}", message)
			}
			VssError::ConflictError(message) => {
				write!(f, "Potential version conflict in write operation: {}", message)
			}
			VssError::InternalServerError(message) => {
				write!(f, "InternalServerError: {}", message)
			}
			VssError::InternalError(message) => {
				write!(f, "InternalError: {}", message)
			}
		}
	}
}

impl Error for VssError {}

impl From<ErrorResponse> for VssError {
	fn from(error_response: ErrorResponse) -> Self {
		match error_response.error_code() {
			ErrorCode::NoSuchKeyException => VssError::NoSuchKeyError(error_response.message),
			ErrorCode::InvalidRequestException => VssError::InvalidRequestError(error_response.message),
			ErrorCode::ConflictException => VssError::ConflictError(error_response.message),
			ErrorCode::InternalServerException => VssError::InternalServerError(error_response.message),
			_ => VssError::InternalError(format!(
				"VSS responded with an unknown error code: {}, message: {}",
				error_response.error_code, error_response.message
			)),
		}
	}
}

impl From<DecodeError> for VssError {
	fn from(err: DecodeError) -> Self {
		VssError::InternalError(err.to_string())
	}
}

impl From<reqwest::Error> for VssError {
	fn from(err: reqwest::Error) -> Self {
		VssError::InternalError(err.to_string())
	}
}