logtail-rust 0.3.0

Logtail http client wrapper
Documentation
use super::{HttpClient, LogtailError};
use reqwest::header::{HeaderMap, HeaderValue};
use serde_json::Value;

pub struct ReqwestClient;

impl HttpClient for ReqwestClient {
    async fn post_json(
        &self,
        url: &str,
        body: &Value,
        extra_headers: Option<HeaderMap>,
    ) -> Result<Option<Value>, LogtailError> {
        let mut header_map = HeaderMap::new();

        if let Some(value) = extra_headers {
            header_map.extend(value.iter().map(|(k, v)| (k.clone(), v.clone())))
        }

        let client = reqwest::Client::new();

        let response = client
            .post(url)
            .headers(build_headers(Some(header_map)))
            .json(body)
            .send()
            .await?;

        if response.status().is_success() {
            let body_bytes = response.bytes().await?;

            if !body_bytes.is_empty() {
                let result_value: Value = serde_json::from_slice(&body_bytes)?;
                return Ok(Some(result_value));
            }

            Ok(None)
        } else {
            let status = response.status().as_u16();
            Err(LogtailError::Http {
                status,
                message: format!("HTTP request failed with status {}", status),
            })
        }
    }
}

fn build_headers(input_headers: Option<HeaderMap>) -> HeaderMap {
    let mut headers = HeaderMap::new();
    if let Some(extra) = input_headers {
        headers = extra;
    }

    headers.insert("Accept", HeaderValue::from_static("application/json"));

    headers
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn build_headers_none_adds_accept() {
        let headers = build_headers(None);
        assert_eq!(
            headers.get("Accept").unwrap(),
            HeaderValue::from_static("application/json")
        );
        assert_eq!(headers.len(), 1);
    }

    #[test]
    fn build_headers_merges_extra_headers() {
        let mut extra = HeaderMap::new();
        extra.insert("Authorization", HeaderValue::from_static("Bearer token123"));

        let headers = build_headers(Some(extra));
        assert_eq!(
            headers.get("Authorization").unwrap(),
            HeaderValue::from_static("Bearer token123")
        );
        assert_eq!(
            headers.get("Accept").unwrap(),
            HeaderValue::from_static("application/json")
        );
        assert_eq!(headers.len(), 2);
    }

    #[test]
    fn build_headers_accept_overwrites_extra() {
        let mut extra = HeaderMap::new();
        extra.insert("Accept", HeaderValue::from_static("text/plain"));

        let headers = build_headers(Some(extra));
        assert_eq!(
            headers.get("Accept").unwrap(),
            HeaderValue::from_static("application/json")
        );
    }
}