use std::time::Duration;
use bytes::Bytes;
use http::StatusCode;
use serde::de::DeserializeOwned;
#[derive(Debug, Clone)]
pub struct TypedResponse<T> {
pub data: T,
pub status: StatusCode,
pub latency: Duration,
raw_body: Option<Bytes>,
}
impl<T> TypedResponse<T> {
pub fn new(data: T, status: StatusCode, latency: Duration) -> Self {
Self {
data,
status,
latency,
raw_body: None,
}
}
pub fn with_raw_body(mut self, body: Bytes) -> Self {
self.raw_body = Some(body);
self
}
pub fn raw_body(&self) -> Option<&Bytes> {
self.raw_body.as_ref()
}
pub fn is_success(&self) -> bool {
self.status.is_success()
}
pub fn is_client_error(&self) -> bool {
self.status.is_client_error()
}
pub fn is_server_error(&self) -> bool {
self.status.is_server_error()
}
pub fn map<U, F: FnOnce(T) -> U>(self, f: F) -> TypedResponse<U> {
TypedResponse {
data: f(self.data),
status: self.status,
latency: self.latency,
raw_body: self.raw_body,
}
}
pub fn into_data(self) -> T {
self.data
}
}
pub trait ApiError: DeserializeOwned + std::error::Error + Send + Sync {
fn from_response(status: StatusCode, body: &[u8]) -> Option<Self>;
}
pub type TypedResult<T, E = crate::error::TransportError> = Result<TypedResponse<T>, E>;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_typed_response() {
let response = TypedResponse::new("hello", StatusCode::OK, Duration::from_millis(100));
assert!(response.is_success());
assert_eq!(response.latency.as_millis(), 100);
}
#[test]
fn test_map() {
let response = TypedResponse::new(
"hello".to_string(),
StatusCode::OK,
Duration::from_millis(100),
);
let mapped = response.map(|s| s.len());
assert_eq!(mapped.data, 5);
}
}