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 Success:true,
48 Payload,
49 Error:None,
50 Metadata:HashMap::new(),
51 GeneratedAt:SystemTimestampGenerator::Now(),
52 }
53 }
54
55 pub fn Failure(CorrelationIdentifier:CorrelationId, Error:ResponseError, Payload:Option<Vec<u8>>) -> Self {
57 Self {
58 CorrelationIdentifier,
59 Success:false,
60 Payload:Payload.unwrap_or_default(),
61 Error:Some(Error),
62 Metadata:HashMap::new(),
63 GeneratedAt:SystemTimestampGenerator::Now(),
64 }
65 }
66
67 pub fn FromTransportError(
69 CorrelationIdentifier:CorrelationId,
70 TransportError:&super::TransportError::TransportError,
71 ) -> Self {
72 Self::Failure(
73 CorrelationIdentifier,
74 ResponseError {
75 Code:TransportError.Code,
76 Message:TransportError.Message.clone(),
77 Details:TransportError.Context.clone(),
78 },
79 None,
80 )
81 }
82
83 pub fn WithMetadata(mut self, Key:impl Into<String>, Value:impl Into<String>) -> Self {
85 self.Metadata.insert(Key.into(), Value.into());
86 self
87 }
88
89 pub fn WithMetadataMap(mut self, Metadata:HashMap<String, String>) -> Self {
91 self.Metadata = Metadata;
92 self
93 }
94
95 pub fn ErrorCode(&self) -> Option<TransportErrorCode> { self.Error.as_ref().map(|ErrorInfo| ErrorInfo.Code) }
97
98 pub fn IsSuccess(&self) -> bool { self.Success }
100
101 pub fn IsError(&self) -> bool { !self.Success }
103
104 pub fn Validate(&self) -> Result<(), String> {
106 if self.CorrelationIdentifier.is_empty() {
107 return Err("correlation_id cannot be empty".to_string());
108 }
109
110 if self.Success && self.Error.is_some() {
111 return Err("success response must not have error".to_string());
112 }
113
114 if !self.Success && self.Error.is_none() {
115 return Err("error response must have error field".to_string());
116 }
117
118 Ok(())
119 }
120}
121
122#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
124pub struct ResponseError {
125 pub Code:TransportErrorCode,
127
128 pub Message:String,
130
131 #[serde(skip_serializing_if = "HashMap::is_empty")]
133 pub Details:HashMap<String, String>,
134}
135
136impl ResponseError {
137 pub fn New(Code:TransportErrorCode, Message:impl Into<String>) -> Self {
139 Self { Code, Message:Message.into(), Details:HashMap::new() }
140 }
141
142 pub fn WithDetail(mut self, Key:impl Into<String>, Value:impl Into<String>) -> Self {
144 self.Details.insert(Key.into(), Value.into());
145 self
146 }
147}
148
149impl std::fmt::Display for ResponseError {
150 fn fmt(&self, Formatter:&mut std::fmt::Formatter<'_>) -> std::fmt::Result {
151 write!(Formatter, "{} (code: {:?})", self.Message, self.Code)?;
152 if !self.Details.is_empty() {
153 let DetailsString:Vec<String> =
154 self.Details.iter().map(|(Key, Value)| format!("{}={}", Key, Value)).collect();
155 write!(Formatter, " [{}]", DetailsString.join(", "))?;
156 }
157 Ok(())
158 }
159}
160
161impl std::error::Error for ResponseError {}
162
163#[cfg(test)]
164mod tests {
165 use TransportErrorCode::ConnectionFailed;
166
167 use super::*;
168 use crate::Transport::TransportStrategy::TransportErrorCode;
169
170 #[test]
171 fn TestUnifiedResponseSuccess() {
172 let Response = UnifiedResponse::Success("req-123".to_string(), b"result".to_vec());
173 assert!(Response.Success);
174 assert_eq!(Response.CorrelationIdentifier, "req-123");
175 assert_eq!(Response.Payload, b"result");
176 assert!(Response.Error.is_none());
177 }
178
179 #[test]
180 fn TestUnifiedResponseError() {
181 let Error = ResponseError::New(ConnectionFailed, "Connection timeout");
182 let Response = UnifiedResponse::Failure("req-456".to_string(), Error, None);
183
184 assert!(!Response.Success);
185 assert_eq!(Response.CorrelationIdentifier, "req-456");
186 assert!(Response.Error.is_some());
187 assert_eq!(Response.Error.as_ref().unwrap().Code, ConnectionFailed);
188 }
189
190 #[test]
191 fn TestUnifiedResponseFromTransportError() {
192 let TransportErrorValue = super::super::TransportError::TransportError::New(ConnectionFailed, "Conn failed")
193 .WithMethod("test.method");
194 let Response = UnifiedResponse::FromTransportError("req-789".to_string(), &TransportErrorValue);
195
196 assert!(!Response.Success);
197 assert_eq!(Response.Error.as_ref().unwrap().Code, ConnectionFailed);
198 assert!(Response.Error.as_ref().unwrap().Message.contains("Conn failed"));
199 }
200
201 #[test]
202 fn TestResponseValidation() {
203 let Response = UnifiedResponse::Success("abc".to_string(), Vec::new());
204 assert!(Response.Validate().is_ok());
205
206 let mut Invalid = Response.clone();
207 Invalid.CorrelationIdentifier = String::new();
208 assert!(Invalid.Validate().is_err());
209
210 let mut Invalid2 = Response.clone();
211 Invalid2.Success = false;
212 Invalid2.Error = None;
213 assert!(Invalid2.Validate().is_err());
214 }
215}