somfy-sdk 0.2.2

A Rust-based SDK for interacting with Somfy smart home devices and APIs.
Documentation
use crate::err::http::RequestError;
use reqwest::header::HeaderMap;
use reqwest::Body;
use serde::de::DeserializeOwned;
use std::collections::HashMap;

#[derive(Debug, Clone, PartialEq, Default)]
pub enum HttpMethod {
    #[default]
    GET,
    POST,
    DELETE,
}

#[derive(Default)]
pub struct RequestData {
    pub header_map: HeaderMap,
    pub body: Body,
    pub query_params: HashMap<String, String>,
    pub path: String,
    pub method: HttpMethod,
}

impl RequestData {
    pub fn get_content_length(&self) -> String {
        self.body.as_bytes().unwrap_or_default().len().to_string()
    }

    pub fn default_post_headers() -> Result<HeaderMap, RequestError> {
        let mut headers = HeaderMap::new();
        headers.insert(
            "content-type",
            reqwest::header::HeaderValue::from_str("application/json")
                .map_err(|e| RequestError::Server(e.into()))?,
        );

        Ok(headers)
    }
}

pub trait SomfyApiRequestCommand {
    type Response: SomfyApiRequestResponse;
    fn to_request(&self) -> Result<RequestData, RequestError>;
}

pub trait SomfyApiRequestResponse: DeserializeOwned {
    fn from_body(body: &str) -> Result<Self, RequestError>
    where
        Self: DeserializeOwned,
    {
        Ok(serde_json::from_str(body)?)
    }
}

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

    #[test]
    fn test_request_data_defaults() {
        let request = RequestData::default();

        assert_eq!(request.method, HttpMethod::GET);
        assert_eq!(request.path, "");
        assert!(request.query_params.is_empty());
        assert!(request.header_map.is_empty());
        assert!(request
            .body
            .as_bytes()
            .expect("should read body bytes")
            .is_empty());
    }

    #[test]
    fn test_http_method_default() {
        let method = HttpMethod::default();
        assert_eq!(method, HttpMethod::GET);
    }

    #[test]
    fn test_get_content_length_empty_body() {
        let request = RequestData::default();
        assert_eq!(request.get_content_length(), "0");
    }

    #[test]
    fn test_get_content_length_with_body() {
        let request = RequestData {
            body: reqwest::Body::from("test body"),
            ..Default::default()
        };
        assert_eq!(request.get_content_length(), "9");
    }

    #[test]
    fn test_default_post_headers() {
        let headers =
            RequestData::default_post_headers().expect("should create default post headers");
        assert_eq!(headers.get("content-type").unwrap(), "application/json");
    }
}