1include!(concat!(env!("OUT_DIR"), "/codegen.rs"));
26
27use alien_error::{AlienError, GenericError, HumanLayerPresentation};
28
29pub trait SdkResultExt<T> {
31 fn into_sdk_error(self) -> Result<T, AlienError<GenericError>>;
33}
34
35impl<T> SdkResultExt<ResponseValue<T>> for Result<ResponseValue<T>, Error<()>> {
36 fn into_sdk_error(self) -> Result<ResponseValue<T>, AlienError<GenericError>> {
37 self.map_err(convert_sdk_error)
38 }
39}
40
41pub fn convert_sdk_error(err: Error<()>) -> AlienError<GenericError> {
43 match err {
44 Error::ErrorResponse(response) => {
45 let status = response.status().as_u16();
46 AlienError {
47 code: "UNEXPECTED_RESPONSE".to_string(),
48 message: format!(
49 "Unexpected response: {} {}",
50 status,
51 response.status().canonical_reason().unwrap_or("Unknown")
52 ),
53 context: Some(serde_json::json!({
54 "status": status,
55 })),
56 hint: None,
57 retryable: status >= 500,
58 internal: false,
59 http_status_code: Some(status),
60 source: None,
61 human_layer_presentation: HumanLayerPresentation::Normal,
62 error: Some(GenericError {
63 message: format!("Unexpected response status: {}", status),
64 }),
65 }
66 }
67 Error::CommunicationError(reqwest_err) => {
68 let retryable =
69 reqwest_err.is_connect() || reqwest_err.is_timeout() || reqwest_err.is_request();
70
71 AlienError {
72 code: "COMMUNICATION_ERROR".to_string(),
73 message: format!("Communication Error: {}", reqwest_err),
74 context: None,
75 hint: None,
76 retryable,
77 internal: false,
78 http_status_code: reqwest_err.status().map(|s| s.as_u16()),
79 source: build_reqwest_source(&reqwest_err),
80 human_layer_presentation: HumanLayerPresentation::Normal,
81 error: Some(GenericError {
82 message: format!("Communication Error: {}", reqwest_err),
83 }),
84 }
85 }
86 Error::InvalidRequest(msg) => AlienError {
87 code: "INVALID_REQUEST".to_string(),
88 message: format!("Invalid Request: {}", msg),
89 context: None,
90 hint: None,
91 retryable: false,
92 internal: false,
93 http_status_code: Some(400),
94 source: None,
95 human_layer_presentation: HumanLayerPresentation::Normal,
96 error: Some(GenericError {
97 message: format!("Invalid Request: {}", msg),
98 }),
99 },
100 Error::ResponseBodyError(reqwest_err) => AlienError {
101 code: "RESPONSE_BODY_ERROR".to_string(),
102 message: format!("Error reading response body: {}", reqwest_err),
103 context: None,
104 hint: None,
105 retryable: true,
106 internal: false,
107 http_status_code: reqwest_err.status().map(|s| s.as_u16()),
108 source: build_reqwest_source(&reqwest_err),
109 human_layer_presentation: HumanLayerPresentation::Normal,
110 error: Some(GenericError {
111 message: format!("Error reading response body: {}", reqwest_err),
112 }),
113 },
114 Error::InvalidResponsePayload(bytes, json_err) => {
115 let raw_body = String::from_utf8_lossy(&bytes);
116 let truncated = if raw_body.len() > 1000 {
117 format!(
118 "{}...(truncated {} bytes)",
119 &raw_body[..1000],
120 raw_body.len() - 1000
121 )
122 } else {
123 raw_body.to_string()
124 };
125
126 AlienError {
127 code: "INVALID_RESPONSE_PAYLOAD".to_string(),
128 message: format!("Failed to parse response: {}", json_err),
129 context: Some(serde_json::json!({
130 "parseError": json_err.to_string(),
131 "responseBody": truncated,
132 })),
133 hint: None,
134 retryable: false,
135 internal: false,
136 http_status_code: None,
137 source: Some(Box::new(AlienError::new(GenericError {
138 message: json_err.to_string(),
139 }))),
140 human_layer_presentation: HumanLayerPresentation::Normal,
141 error: Some(GenericError {
142 message: format!("Failed to parse response: {}", json_err),
143 }),
144 }
145 }
146 Error::InvalidUpgrade(reqwest_err) => AlienError {
147 code: "INVALID_UPGRADE".to_string(),
148 message: format!("Connection upgrade failed: {}", reqwest_err),
149 context: None,
150 hint: None,
151 retryable: false,
152 internal: false,
153 http_status_code: reqwest_err.status().map(|s| s.as_u16()),
154 source: build_reqwest_source(&reqwest_err),
155 human_layer_presentation: HumanLayerPresentation::Normal,
156 error: Some(GenericError {
157 message: format!("Connection upgrade failed: {}", reqwest_err),
158 }),
159 },
160 Error::UnexpectedResponse(response) => {
161 let status = response.status().as_u16();
162 AlienError {
163 code: "UNEXPECTED_RESPONSE".to_string(),
164 message: format!(
165 "Unexpected response: {} {}",
166 status,
167 response.status().canonical_reason().unwrap_or("Unknown")
168 ),
169 context: Some(serde_json::json!({
170 "status": status,
171 "url": response.url().to_string(),
172 })),
173 hint: None,
174 retryable: status >= 500,
175 internal: false,
176 http_status_code: Some(status),
177 source: None,
178 human_layer_presentation: HumanLayerPresentation::Normal,
179 error: Some(GenericError {
180 message: format!("Unexpected response status: {}", status),
181 }),
182 }
183 }
184 Error::Custom(msg) => AlienError {
185 code: "SDK_HOOK_ERROR".to_string(),
186 message: msg.clone(),
187 context: None,
188 hint: None,
189 retryable: false,
190 internal: false,
191 http_status_code: None,
192 source: None,
193 human_layer_presentation: HumanLayerPresentation::Normal,
194 error: Some(GenericError { message: msg }),
195 },
196 }
197}
198
199fn build_reqwest_source(reqwest_err: &reqwest::Error) -> Option<Box<AlienError<GenericError>>> {
200 use std::error::Error as _;
201
202 reqwest_err.source().map(|source| {
203 Box::new(AlienError {
204 code: "GENERIC_ERROR".to_string(),
205 message: source.to_string(),
206 context: None,
207 hint: None,
208 retryable: false,
209 internal: false,
210 http_status_code: None,
211 source: None,
212 human_layer_presentation: HumanLayerPresentation::Transparent,
213 error: Some(GenericError {
214 message: source.to_string(),
215 }),
216 })
217 })
218}