gamesense 0.1.2

SteelSeries GameSenseā„¢ client written in Rust
Documentation
use std::env;
use std::fs;
use crate::handler;
use anyhow::{Result, anyhow, bail};
use reqwest;
use serde_json;
use serde::Serialize;
use serde_json::json;

macro_rules! cond_argument {
    ($data:expr, $key:literal, $option_value:ident) => {
        if let Some(value) = $option_value {
            $data.as_object_mut().unwrap().insert(String::from($key), json!(value));
        }
    };
}

pub struct RawGameSenseClient {
    client: reqwest::blocking::Client,
    address: String
}

impl RawGameSenseClient {
    pub fn new() -> Result<RawGameSenseClient> {
        let path = match env::consts::OS {
            "macos" => Ok(String::from("/Library/Application Support/SteelSeries Engine 3/coreProps.json")),
            "windows" => Ok(env::var("PROGRAMDATA")? + "/SteelSeries/SteelSeries Engine 3/coreProps.json"),
            _ => Err(anyhow!("Discovery failed: Platform must be either MacOS or Windows - Got {}", env::consts::OS))
        };

        let config: serde_json::Value = serde_json::from_str(&fs::read_to_string(path?)?)?;

        return Ok(RawGameSenseClient {
            client: reqwest::blocking::Client::new(),
            address: config["address"].as_str().expect("Discovery failed: `address` not found").to_owned()
        });
    }

    pub fn send_data(&self, endpoint: &str, data: &serde_json::Value) -> Result<String> {
        let data = self.client.post(format!("http://{}/{}", self.address, endpoint))
            .json(data)
            .send()?
            .text()?;

        if data == "Page not found" {
            bail!("Endpoint not found");
        }

        let data: serde_json::Map<String, serde_json::Value> = serde_json::from_str(&data)?;

        let (key, value) = data.iter().next().unwrap();
        let value = value.as_str().unwrap_or(&value.to_string()).to_owned();

        match key.as_str() {
            "error" => bail!(value),
            _ => Ok(value)
        }
    }

    pub fn game_event(&self, game: &str, event: &str, value: isize, frame: Option<serde_json::Value>) -> Result<String> {
        let mut data = json!({
            "game": game,
            "event": event,
            "data": {
                "value": value
            }
        });

        cond_argument!(data.get_mut("data").unwrap(), "frame", frame);

        self.send_data("game_event", &data)
    }

    pub fn heartbeat(&self, game: &str) -> Result<String> {
        let data = json!({
            "game": game
        });

        self.send_data("game_heartbeat", &data)
    }

    pub fn register_game(&self, game: &str, game_display_name: Option<&str>, developer: Option<&str>, deinitialize_timer_length_ms: Option<u16>) -> Result<String> {
        let mut data = json!({
            "game": game
        });

        cond_argument!(data, "game_display_name", game_display_name);
        cond_argument!(data, "developer", developer);
        cond_argument!(data, "deinitialize_timer_length_ms", deinitialize_timer_length_ms);

        self.send_data("game_metadata", &data)
    }

    pub fn remove_game(&self, game: &str) -> Result<String> {
        let data = json!({
            "game": game,
        });

        self.send_data("remove_game", &data)
    }

    pub fn bind_event<T: Serialize + handler::Handler>(&self, game: &str, event: &str, min_value: Option<isize>, max_value: Option<isize>, icon_id: Option<u8>, value_optional: Option<bool>, handlers: Vec<T>) -> Result<String> {
        let mut data = json!({
            "game": game,
            "event": event,
            "handlers": handlers
        });

        cond_argument!(data, "min_value", min_value);
        cond_argument!(data, "max_value", max_value);
        cond_argument!(data, "icon_id", icon_id);
        cond_argument!(data, "value_optional", value_optional);

        self.send_data("bind_game_event", &data)
    }

    pub fn register_event(&self, game: &str, event: &str, min_value: Option<isize>, max_value: Option<isize>, icon_id: Option<u8>, value_optional: Option<bool>) -> Result<String> {
        let mut data = json!({
            "game": game,
            "event": event
        });

        cond_argument!(data, "min_value", min_value);
        cond_argument!(data, "max_value", max_value);
        cond_argument!(data, "icon_id", icon_id);
        cond_argument!(data, "value_optional", value_optional);

        self.send_data("register_game_event", &data)
    }

    pub fn remove_event(&self, game: &str, event: &str) -> Result<String> {
        let data = json!({
            "game": game,
            "event": event
        });

        self.send_data("remove_game_event", &data)
    }
}