1#![allow(non_snake_case, non_camel_case_types, non_upper_case_globals)]
2use std::collections::HashMap;
7
8use serde::{Deserialize, Serialize};
9
10use super::{
11 Common::{CorrelationId, SystemTimestampGenerator, Timestamp, TimestampGenerator},
12 TransportStrategy::TransportErrorCode,
13};
14
15#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
17pub struct UnifiedResponse {
18 pub CorrelationIdentifier:CorrelationId,
20
21 pub Success:bool,
23
24 #[serde(skip_serializing_if = "Vec::is_empty")]
26 pub Payload:Vec<u8>,
27
28 #[serde(skip_serializing_if = "Option::is_none")]
30 pub Error:Option<ResponseError>,
31
32 #[serde(skip_serializing_if = "HashMap::is_empty")]
34 pub Metadata:HashMap<String, String>,
35
36 pub GeneratedAt:Timestamp,
39}
40
41impl UnifiedResponse {
42 pub fn Success(CorrelationIdentifier:CorrelationId, Payload:Vec<u8>) -> Self {
45 Self {
46 CorrelationIdentifier,
47
48 Success:true,
49
50 Payload,
51
52 Error:None,
53
54 Metadata:HashMap::new(),
55
56 GeneratedAt:SystemTimestampGenerator::Now(),
57 }
58 }
59
60 pub fn Failure(CorrelationIdentifier:CorrelationId, Error:ResponseError, Payload:Option<Vec<u8>>) -> Self {
62 Self {
63 CorrelationIdentifier,
64
65 Success:false,
66
67 Payload:Payload.unwrap_or_default(),
68
69 Error:Some(Error),
70
71 Metadata:HashMap::new(),
72
73 GeneratedAt:SystemTimestampGenerator::Now(),
74 }
75 }
76
77 pub fn FromTransportError(
79 CorrelationIdentifier:CorrelationId,
80
81 TransportError:&super::TransportError::TransportError,
82 ) -> Self {
83 Self::Failure(
84 CorrelationIdentifier,
85 ResponseError {
86 Code:TransportError.Code,
87 Message:TransportError.Message.clone(),
88 Details:TransportError.Context.clone(),
89 },
90 None,
91 )
92 }
93
94 pub fn WithMetadata(mut self, Key:impl Into<String>, Value:impl Into<String>) -> Self {
96 self.Metadata.insert(Key.into(), Value.into());
97
98 self
99 }
100
101 pub fn WithMetadataMap(mut self, Metadata:HashMap<String, String>) -> Self {
103 self.Metadata = Metadata;
104
105 self
106 }
107
108 pub fn ErrorCode(&self) -> Option<TransportErrorCode> { self.Error.as_ref().map(|ErrorInfo| ErrorInfo.Code) }
110
111 pub fn IsSuccess(&self) -> bool { self.Success }
113
114 pub fn IsError(&self) -> bool { !self.Success }
116
117 pub fn Validate(&self) -> Result<(), String> {
119 if self.CorrelationIdentifier.is_empty() {
120 return Err("correlation_id cannot be empty".to_string());
121 }
122
123 if self.Success && self.Error.is_some() {
124 return Err("success response must not have error".to_string());
125 }
126
127 if !self.Success && self.Error.is_none() {
128 return Err("error response must have error field".to_string());
129 }
130
131 Ok(())
132 }
133}
134
135#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
137pub struct ResponseError {
138 pub Code:TransportErrorCode,
140
141 pub Message:String,
143
144 #[serde(skip_serializing_if = "HashMap::is_empty")]
146 pub Details:HashMap<String, String>,
147}
148
149impl ResponseError {
150 pub fn New(Code:TransportErrorCode, Message:impl Into<String>) -> Self {
152 Self { Code, Message:Message.into(), Details:HashMap::new() }
153 }
154
155 pub fn WithDetail(mut self, Key:impl Into<String>, Value:impl Into<String>) -> Self {
157 self.Details.insert(Key.into(), Value.into());
158
159 self
160 }
161}
162
163impl std::fmt::Display for ResponseError {
164 fn fmt(&self, Formatter:&mut std::fmt::Formatter<'_>) -> std::fmt::Result {
165 write!(Formatter, "{} (code: {:?})", self.Message, self.Code)?;
166
167 if !self.Details.is_empty() {
168 let DetailsString:Vec<String> =
169 self.Details.iter().map(|(Key, Value)| format!("{}={}", Key, Value)).collect();
170
171 write!(Formatter, " [{}]", DetailsString.join(", "))?;
172 }
173
174 Ok(())
175 }
176}
177
178impl std::error::Error for ResponseError {}
179
180#[cfg(test)]
181mod tests {
182
183 use TransportErrorCode::ConnectionFailed;
184
185 use super::*;
186 use crate::Transport::TransportStrategy::TransportErrorCode;
187
188 #[test]
189 fn TestUnifiedResponseSuccess() {
190 let Response = UnifiedResponse::Success("req-123".to_string(), b"result".to_vec());
191
192 assert!(Response.Success);
193
194 assert_eq!(Response.CorrelationIdentifier, "req-123");
195
196 assert_eq!(Response.Payload, b"result");
197
198 assert!(Response.Error.is_none());
199 }
200
201 #[test]
202 fn TestUnifiedResponseError() {
203 let Error = ResponseError::New(ConnectionFailed, "Connection timeout");
204
205 let Response = UnifiedResponse::Failure("req-456".to_string(), Error, None);
206
207 assert!(!Response.Success);
208
209 assert_eq!(Response.CorrelationIdentifier, "req-456");
210
211 assert!(Response.Error.is_some());
212
213 assert_eq!(Response.Error.as_ref().unwrap().Code, ConnectionFailed);
214 }
215
216 #[test]
217 fn TestUnifiedResponseFromTransportError() {
218 let TransportErrorValue = super::super::TransportError::TransportError::New(ConnectionFailed, "Conn failed")
219 .WithMethod("test.method");
220
221 let Response = UnifiedResponse::FromTransportError("req-789".to_string(), &TransportErrorValue);
222
223 assert!(!Response.Success);
224
225 assert_eq!(Response.Error.as_ref().unwrap().Code, ConnectionFailed);
226
227 assert!(Response.Error.as_ref().unwrap().Message.contains("Conn failed"));
228 }
229
230 #[test]
231 fn TestResponseValidation() {
232 let Response = UnifiedResponse::Success("abc".to_string(), Vec::new());
233
234 assert!(Response.Validate().is_ok());
235
236 let mut Invalid = Response.clone();
237
238 Invalid.CorrelationIdentifier = String::new();
239
240 assert!(Invalid.Validate().is_err());
241
242 let mut Invalid2 = Response.clone();
243
244 Invalid2.Success = false;
245
246 Invalid2.Error = None;
247
248 assert!(Invalid2.Validate().is_err());
249 }
250}