halo_api 0.3.0

Unofficial Halo Infinite REST API client for Rust (CSR, service record, match history, and more).
Documentation
#![allow(dead_code)]

use std::env;
use std::io::{self, Write};
use std::sync::Arc;

use halo_api::auth::HaloAuthClient;
use halo_api::clients::hi::{HaloInfiniteClient, Player};
use xbox::{RelyingParty, XboxClient, auth::LegacyPasswordProvider, models::Xuid};

pub type ExampleError = Box<dyn std::error::Error>;
pub type ExampleXboxClient = XboxClient<LegacyPasswordProvider>;

fn prompt_line(label: &str) -> Result<String, ExampleError> {
    print!("{label}: ");
    io::stdout().flush()?;
    let mut input = String::new();
    io::stdin().read_line(&mut input)?;
    Ok(input.trim().to_string())
}

pub fn value(name: &str, label: &str) -> Result<String, ExampleError> {
    match env::var(name) {
        Ok(value) => Ok(value),
        Err(_) => prompt_line(label),
    }
}

pub fn comma_separated(name: &str, label: &str) -> Result<Vec<String>, ExampleError> {
    let values = value(name, label)?
        .split(',')
        .map(str::trim)
        .filter(|item| !item.is_empty())
        .map(str::to_owned)
        .collect::<Vec<_>>();
    if values.is_empty() {
        return Err(format!("{name} must contain at least one value").into());
    }
    Ok(values)
}

pub async fn player_xuid(halo: &HaloInfiniteClient) -> Result<(String, Player), ExampleError> {
    let gamertag = value("HALO_GAMERTAG", "Gamertag")?;
    let user = halo.user(&Player::gamertag(gamertag)).await?;
    Ok((user.gamertag, Player::xuid(user.xuid)))
}

pub fn xbox_client() -> Result<Arc<ExampleXboxClient>, ExampleError> {
    let username = value("XBOX_USERNAME", "Xbox Live email")?;
    let password = match env::var("XBOX_PASSWORD") {
        Ok(password) => password,
        Err(_) => rpassword::prompt_password("Xbox Live password: ")?,
    };
    Ok(Arc::new(XboxClient::new(LegacyPasswordProvider::new(
        username, password,
    ))))
}

pub fn halo_infinite_client() -> Result<(Arc<ExampleXboxClient>, HaloInfiniteClient), ExampleError>
{
    let xbox = xbox_client()?;
    let auth = HaloAuthClient::from_xbox_client(xbox.clone());
    Ok((xbox, HaloInfiniteClient::new(auth)))
}

pub async fn logged_in_player(xbox: &ExampleXboxClient) -> Result<(String, Xuid), ExampleError> {
    let ticket = xbox.xsts_ticket(RelyingParty::XBOX).await?;
    let gamertag = ticket.gamertag().unwrap_or("<unknown>").to_string();
    let xuid = ticket.xuid().ok_or("Xbox ticket did not contain an XUID")?;
    Ok((gamertag, xuid))
}