mirror of https://github.com/telemt/telemt.git
89 lines
2.8 KiB
Rust
89 lines
2.8 KiB
Rust
use http_body_util::{BodyExt, Full};
|
|
use hyper::StatusCode;
|
|
use hyper::body::{Bytes, Incoming};
|
|
use serde::Serialize;
|
|
use serde::de::DeserializeOwned;
|
|
|
|
use super::model::{ApiFailure, ErrorBody, ErrorResponse, SuccessResponse};
|
|
|
|
pub(super) fn success_response<T: Serialize>(
|
|
status: StatusCode,
|
|
data: T,
|
|
revision: String,
|
|
) -> hyper::Response<Full<Bytes>> {
|
|
let payload = SuccessResponse {
|
|
ok: true,
|
|
data,
|
|
revision,
|
|
};
|
|
let body = serde_json::to_vec(&payload).unwrap_or_else(|_| b"{\"ok\":false}".to_vec());
|
|
hyper::Response::builder()
|
|
.status(status)
|
|
.header("content-type", "application/json; charset=utf-8")
|
|
.body(Full::new(Bytes::from(body)))
|
|
.unwrap()
|
|
}
|
|
|
|
pub(super) fn error_response(request_id: u64, failure: ApiFailure) -> hyper::Response<Full<Bytes>> {
|
|
let payload = ErrorResponse {
|
|
ok: false,
|
|
error: ErrorBody {
|
|
code: failure.code,
|
|
message: failure.message,
|
|
},
|
|
request_id,
|
|
};
|
|
let body = serde_json::to_vec(&payload).unwrap_or_else(|_| {
|
|
format!(
|
|
"{{\"ok\":false,\"error\":{{\"code\":\"internal_error\",\"message\":\"serialization failed\"}},\"request_id\":{}}}",
|
|
request_id
|
|
)
|
|
.into_bytes()
|
|
});
|
|
hyper::Response::builder()
|
|
.status(failure.status)
|
|
.header("content-type", "application/json; charset=utf-8")
|
|
.body(Full::new(Bytes::from(body)))
|
|
.unwrap()
|
|
}
|
|
|
|
pub(super) async fn read_json<T: DeserializeOwned>(
|
|
body: Incoming,
|
|
limit: usize,
|
|
) -> Result<T, ApiFailure> {
|
|
let bytes = read_body_with_limit(body, limit).await?;
|
|
serde_json::from_slice(&bytes).map_err(|_| ApiFailure::bad_request("Invalid JSON body"))
|
|
}
|
|
|
|
pub(super) async fn read_optional_json<T: DeserializeOwned>(
|
|
body: Incoming,
|
|
limit: usize,
|
|
) -> Result<Option<T>, ApiFailure> {
|
|
let bytes = read_body_with_limit(body, limit).await?;
|
|
if bytes.is_empty() {
|
|
return Ok(None);
|
|
}
|
|
serde_json::from_slice(&bytes)
|
|
.map(Some)
|
|
.map_err(|_| ApiFailure::bad_request("Invalid JSON body"))
|
|
}
|
|
|
|
async fn read_body_with_limit(body: Incoming, limit: usize) -> Result<Vec<u8>, ApiFailure> {
|
|
let mut collected = Vec::new();
|
|
let mut body = body;
|
|
while let Some(frame_result) = body.frame().await {
|
|
let frame = frame_result.map_err(|_| ApiFailure::bad_request("Invalid request body"))?;
|
|
if let Some(chunk) = frame.data_ref() {
|
|
if collected.len().saturating_add(chunk.len()) > limit {
|
|
return Err(ApiFailure::new(
|
|
StatusCode::PAYLOAD_TOO_LARGE,
|
|
"payload_too_large",
|
|
format!("Body exceeds {} bytes", limit),
|
|
));
|
|
}
|
|
collected.extend_from_slice(chunk);
|
|
}
|
|
}
|
|
Ok(collected)
|
|
}
|