wcgi 0.3.0

Common abstractions for defining a WCGI server.
Documentation
use std::io::{BufReader, Write};

use http::{HeaderMap, Method, Uri};

use crate::{Body, BodyInner, WcgiError};

#[derive(serde::Serialize, serde::Deserialize)]
struct SerdeRequest {
    #[serde(with = "http_serde::method")]
    method: Method,

    #[serde(with = "http_serde::uri")]
    uri: Uri,

    #[serde(with = "http_serde::header_map")]
    headers: HeaderMap,

    body: Option<Vec<u8>>,
}

pub const ENV_VAR_REQUEST_JSON_V1: &str = "__WCGI_REQUEST_JSON_V1";

// Parse a request from the enviornment (env vars, stdin, etc).
pub fn extract_request() -> Result<crate::Request, WcgiError> {
    let header = std::env::var(ENV_VAR_REQUEST_JSON_V1).map_err(|_| {
        let env = std::env::vars().collect::<Vec<_>>();
        WcgiError::msg(format!(
            "Could not read {ENV_VAR_REQUEST_JSON_V1} env var \n\n({env:?})"
        ))
    })?;

    let sr: SerdeRequest = serde_json::from_slice(header.as_bytes())
        .map_err(|err| WcgiError::wrap(err, "could not deserialize request header"))?;

    let body = if let Some(data) = sr.body {
        Body::from(data)
    } else {
        let lock = std::io::stdin().lock();
        Body(BodyInner::Stream(BufReader::new(Box::new(lock))))
    };

    let mut request = http::Request::new(body);
    *request.method_mut() = sr.method;
    *request.uri_mut() = sr.uri;
    *request.headers_mut() = sr.headers;

    Ok(request)
}

pub fn serialize_request_header_v1_json(
    request: http::Request<Option<Vec<u8>>>,
) -> Result<String, serde_json::Error> {
    let (parts, body) = request.into_parts();

    let request2 = SerdeRequest {
        method: parts.method,
        uri: parts.uri,
        headers: parts.headers,
        body,
    };

    serde_json::to_string(&request2)
}

#[derive(serde::Serialize, serde::Deserialize)]
struct SerdeResponse {
    #[serde(with = "http_serde::status_code")]
    status: http::StatusCode,

    #[serde(with = "http_serde::header_map")]
    headers: HeaderMap,
}

pub fn deserialize_response_header_v1_json(
    data: &[u8],
) -> Result<http::Response<Option<Vec<u8>>>, WcgiError> {
    let res: SerdeResponse = serde_json::from_slice(data)
        .map_err(|err| WcgiError::wrap(err, "Could not deserialize response"))?;

    let mut res2 = http::Response::new(None);
    *res2.status_mut() = res.status;
    *res2.headers_mut() = res.headers;

    Ok(res2)
}

pub fn send_response_and_terminate(res: crate::Response) {
    let (parts, body) = res.into_parts();
    let res = SerdeResponse {
        status: parts.status,
        headers: parts.headers,
    };
    let mut json_header = serde_json::to_vec(&res).unwrap();
    json_header.push(b'\n');

    let mut stdout = std::io::stdout().lock();
    stdout
        .write_all(&json_header)
        .expect("could not write response header");
    match body.0 {
        BodyInner::Empty => {}
        BodyInner::Data(data) => {
            stdout
                .write_all(&data.into_inner())
                .expect("could not write response body");
        }
        BodyInner::Stream(mut stream) => {
            std::io::copy(&mut stream, &mut stdout).expect("could not write response body");
        }
    }
}