1use soap_client::SoapError;
2use thiserror::Error;
3
4#[derive(Debug, Error)]
10pub enum ApiError {
11 #[error("Network error: {0}")]
17 NetworkError(String),
18
19 #[error("Parse error: {0}")]
25 ParseError(String),
26
27 #[error("SOAP fault: error code {0}")]
32 SoapFault(u16),
33
34 #[error("Invalid parameter: {0}")]
39 InvalidParameter(String),
40
41 #[error("Subscription error: {0}")]
46 SubscriptionError(String),
47
48 #[error("Device error: {0}")]
53 DeviceError(String),
54}
55
56impl ApiError {
57 pub fn subscription_expired() -> Self {
59 Self::SubscriptionError("Subscription expired".to_string())
60 }
61}
62
63pub type Result<T> = std::result::Result<T, ApiError>;
65
66impl From<SoapError> for ApiError {
68 fn from(error: SoapError) -> Self {
69 match error {
70 SoapError::Network(msg) => ApiError::NetworkError(msg),
71 SoapError::Parse(msg) => ApiError::ParseError(msg),
72 SoapError::Fault { code, .. } => ApiError::SoapFault(code),
77 }
78 }
79}
80
81impl From<crate::operation::ValidationError> for ApiError {
83 fn from(validation_error: crate::operation::ValidationError) -> Self {
84 match validation_error {
85 crate::operation::ValidationError::InvalidValue {
86 parameter,
87 value,
88 reason,
89 } => ApiError::InvalidParameter(format!(
90 "Invalid value '{value}' for parameter '{parameter}': {reason}"
91 )),
92 crate::operation::ValidationError::RangeError {
93 parameter,
94 value,
95 min,
96 max,
97 } => ApiError::InvalidParameter(format!(
98 "Parameter '{parameter}' value {value} is out of range [{min}, {max}]"
99 )),
100 crate::operation::ValidationError::Custom { parameter, message } => {
101 ApiError::InvalidParameter(format!("Parameter '{parameter}': {message}"))
102 }
103 crate::operation::ValidationError::MissingParameter { parameter } => {
104 ApiError::InvalidParameter(format!("Required parameter '{parameter}' is missing"))
105 }
106 }
107 }
108}
109
110#[cfg(test)]
111mod tests {
112 use super::*;
113
114 #[test]
115 fn test_subscription_expired() {
116 let error = ApiError::subscription_expired();
117 assert!(matches!(error, ApiError::SubscriptionError(_)));
118 let error_str = format!("{error}");
119 assert!(error_str.contains("expired"));
120 }
121
122 #[test]
123 fn test_soap_error_conversion() {
124 let soap_error = SoapError::Network("connection timeout".to_string());
125 let api_error: ApiError = soap_error.into();
126 assert!(matches!(api_error, ApiError::NetworkError(_)));
127
128 let soap_error = SoapError::Parse("invalid XML".to_string());
129 let api_error: ApiError = soap_error.into();
130 assert!(matches!(api_error, ApiError::ParseError(_)));
131
132 let soap_error = SoapError::fault(500);
133 let api_error: ApiError = soap_error.into();
134 assert!(matches!(api_error, ApiError::SoapFault(500)));
135
136 let soap_error = SoapError::Fault {
138 code: 402,
139 description: Some("Invalid Args".to_string()),
140 };
141 let api_error: ApiError = soap_error.into();
142 assert!(matches!(api_error, ApiError::SoapFault(402)));
143 }
144
145 #[test]
146 fn test_error_display() {
147 let network_err = ApiError::NetworkError("connection failed".to_string());
148 assert_eq!(format!("{network_err}"), "Network error: connection failed");
149
150 let parse_err = ApiError::ParseError("invalid XML".to_string());
151 assert_eq!(format!("{parse_err}"), "Parse error: invalid XML");
152
153 let soap_fault = ApiError::SoapFault(500);
154 assert_eq!(format!("{soap_fault}"), "SOAP fault: error code 500");
155 }
156}