Skip to main content

folk_plugin_http/
payload.rs

1//! HTTP payload encoding: axum Request/Response ↔ `serde_json::Value`.
2//!
3//! No JSON string serialization — data flows as structured Values.
4
5use std::collections::HashMap;
6
7use anyhow::{Context, Result};
8use axum::body::Body;
9use bytes::Bytes;
10
11/// Encode an axum Request to a `serde_json::Value` (no string serialization).
12pub async fn encode_request(
13    req: axum::http::Request<Body>,
14    max_body_size: usize,
15) -> Result<serde_json::Value> {
16    let (parts, body) = req.into_parts();
17    let body_bytes = axum::body::to_bytes(body, max_body_size)
18        .await
19        .context("read request body")?;
20
21    let headers: HashMap<String, String> = parts
22        .headers
23        .iter()
24        .filter_map(|(k, v)| Some((k.to_string(), v.to_str().ok()?.to_string())))
25        .collect();
26
27    Ok(serde_json::json!({
28        "method": parts.method.to_string(),
29        "uri": parts.uri.to_string(),
30        "headers": headers,
31        "body": String::from_utf8_lossy(&body_bytes),
32    }))
33}
34
35/// Decode a `serde_json::Value` to an axum Response (no string deserialization).
36pub fn decode_response(value: serde_json::Value) -> Result<axum::http::Response<Body>> {
37    let status = value.get("status").and_then(|v| v.as_u64()).unwrap_or(200) as u16;
38
39    let body = value.get("body").and_then(|v| v.as_str()).unwrap_or("");
40
41    let mut builder = axum::http::Response::builder().status(status);
42
43    if let Some(headers) = value.get("headers").and_then(|v| v.as_object()) {
44        for (k, v) in headers {
45            if let Some(v_str) = v.as_str() {
46                builder = builder.header(k.as_str(), v_str);
47            }
48        }
49    }
50
51    builder
52        .body(Body::from(Bytes::from(body.to_string())))
53        .context("build response")
54}