microversus_codex 0.1.6

Types and definitions for interfacing with the microversus system
Documentation
mod error;
mod test;

pub use error::*;

use crate::{EventTag, LootAction, LootServiceResponse};
use reqwest::header::{HeaderMap, HeaderValue, ACCEPT, AUTHORIZATION, CONTENT_TYPE};
use reqwest::Client;
use serde::de::DeserializeOwned;
use serde::Serialize;

pub enum MicroversusEnv {
    Dev { world: String },
    Prod { world: String },
}

impl MicroversusEnv {
    pub fn url(&self, path: &str) -> String {
        match self {
            Self::Dev { world } => format!("https://{}.microversus.dev{}", world, path),
            Self::Prod { world } => format!("https://{}.microversus.run{}", world, path),
        }
    }
}

pub struct MicroversusApi {
    client: Client,
    env: MicroversusEnv,
    auth_token: Option<String>,
}

impl MicroversusApi {
    pub fn new(world: &str, env: &str, token: Option<String>) -> Self {
        let client = Client::new();
        match env {
            "prod" => Self {
                client,
                env: MicroversusEnv::Prod {
                    world: world.into(),
                },
                auth_token: token.clone(),
            },
            _ => Self {
                client,
                env: MicroversusEnv::Dev {
                    world: world.into(),
                },
                auth_token: token.clone(),
            },
        }
    }

    pub async fn get_loot(
        &self,
        encounter_id: &str,
        tags: Option<Vec<EventTag>>,
    ) -> Result<LootServiceResponse, MicroversusApiError> {
        post_json(
            &self.client,
            &self.env.url("/loot"),
            self.build_headers(),
            &LootAction::GetResult {
                encounter_id: encounter_id.into(),
                tags,
                record_event: Some(true),
            },
        )
        .await
    }

    fn build_headers(&self) -> HeaderMap {
        let mut headers = HeaderMap::new();

        // Set some static headers
        headers.insert(ACCEPT, HeaderValue::from_static("application/json"));
        headers.insert(CONTENT_TYPE, HeaderValue::from_static("application/json"));

        // Conditionally insert the Authorization header
        if let Some(token_str) = self.auth_token.clone() {
            let value = format!("Bearer {}", token_str);
            headers.insert(
                AUTHORIZATION,
                HeaderValue::from_str(&value).expect("Invalid token format"),
            );
        }

        headers
    }
}

async fn post_json<T: Serialize, R: DeserializeOwned>(
    client: &Client,
    url: &str,
    headers: HeaderMap,
    body: &T,
) -> Result<R, MicroversusApiError> {
    tracing::debug!("url: {} => {}", url, serde_json::to_string(&body).unwrap());

    let response = client
        .post(url)
        .headers(headers)
        .json(body)
        .send()
        .await
        .map_err(MicroversusApiError::Request)?;

    response
        .error_for_status()?
        .json::<R>()
        .await
        .map_err(MicroversusApiError::Request)
}