bionic_reading_api 0.1.1

Unofficial Rust library to get a Bionic Reading converted string from their Rapid API.
Documentation
use crate::{
    bionic::{Error as BionicError, Fixation, Saccade, Text as BionicText},
    client::{self, Client as BionicClient},
};
use reqwest::{multipart::Form, Client as ReqwestClient};

const CONTENT_KEY: &str = "content";
const REQUEST_TYPE_KEY: &str = "request_type";
const REQUEST_TYPE: &str = "html";
const RESPONSE_TYPE_KEY: &str = "response_type";
const RESPONSE_TYPE: &str = "html";
const FIXATION_KEY: &str = "fixation";
const SACCADE_KEY: &str = "saccade";
const HEADER_RAPID_API_KEY: &str = "X-RapidAPI-Key";
const HEADER_RAPID_API_HOST: &str = "X-RapidAPI-Host";
const CONTENT_TYPE_KEY: &str = "content_type";
const CONTENT_TYPE: &str = "application/x-www-form-urlencoded";

#[derive(Default)]
pub struct Request {
    input: String,
    fixation: Fixation,
    saccade: Saccade,
}

pub struct Builder {
    client: BionicClient,
    request: Request,
}

impl Builder {
    pub(crate) fn new(client: BionicClient, input: String) -> Self {
        Self {
            client,
            request: Request {
                input,
                ..Request::default()
            },
        }
    }

    #[allow(clippy::missing_const_for_fn)]
    pub fn fixation(self, fixation: Fixation) -> Self {
        Self {
            request: Request {
                fixation,
                ..self.request
            },
            ..self
        }
    }

    #[allow(clippy::missing_const_for_fn)]
    pub fn saccade(self, saccade: Saccade) -> Self {
        Self {
            request: Request {
                saccade,
                ..self.request
            },
            ..self
        }
    }

    pub async fn send(self) -> Result<BionicText, BionicError> {
        let client = ReqwestClient::new();

        let form = Form::new()
            .text(CONTENT_KEY, self.request.input)
            .text(REQUEST_TYPE_KEY, REQUEST_TYPE)
            .text(RESPONSE_TYPE_KEY, RESPONSE_TYPE)
            .text(FIXATION_KEY, u8::from(self.request.fixation).to_string())
            .text(SACCADE_KEY, u8::from(self.request.saccade).to_string());

        let url = self.client.url.clone();
        let domain = self.client.url.domain().unwrap_or(client::BASE_URL_DOMAIN);

        let res = client
            .post(url)
            .multipart(form)
            .header(HEADER_RAPID_API_KEY, self.client.api_key.secret())
            .header(HEADER_RAPID_API_HOST, domain)
            .header(CONTENT_TYPE_KEY, CONTENT_TYPE)
            .send()
            .await?;

        match res.error_for_status() {
            Ok(res) => Ok(BionicText {
                html: res.text().await.ok(),
            }),
            Err(err) => Err(BionicError::FailedToConvert(err)),
        }
    }
}

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

    #[test]
    fn test_set_fixation() {
        let builder = Builder::new(BionicClient::new("api_key"), String::from("Input"));

        let builder = builder.fixation(Fixation::Strongest);

        let actual: u8 = builder.request.fixation.into();
        let expected: u8 = Fixation::Strongest.into();
        let default: u8 = Fixation::default().into();

        assert_ne!(
            actual, default,
            "Fixation was still default after being modified"
        );

        assert_eq!(actual, expected, "Fixation not set to given value");
    }

    #[test]
    fn test_set_saccade() {
        let builder = Builder::new(BionicClient::new("api_key"), String::from("Input"));

        let builder = builder.saccade(Saccade::Most);

        let actual: u8 = builder.request.saccade.into();
        let expected: u8 = Saccade::Most.into();
        let default: u8 = Saccade::default().into();

        assert_ne!(
            actual, default,
            "Saccade was still default after being modified"
        );

        assert_eq!(actual, expected, "Saccade not set to given value");
    }
}