devcade_onboard_types 0.1.0

Types used by the devcade onboard socket
Documentation
pub mod schema;
use crate::schema::*;
use anyhow::Error;
use serde::{Deserialize, Serialize};
pub use serde_json::{Map, Value};
use std::fmt::{self, Display};
use std::process::ExitStatus;
use std::thread::JoinHandle;

/// Identifies which user is using the machine
#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq)]
pub enum Player {
    /// Player 1 (left controls)
    P1,
    /// Player 2 (right controls)
    P2,
}

impl Display for Player {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        use Player::*;
        match self {
            P1 => write!(f, "P1"),
            P2 => write!(f, "P2"),
        }
    }
}

impl From<Player> for u8 {
    fn from(player: Player) -> Self {
        use Player::*;
        match player {
            P1 => 0,
            P2 => 1,
        }
    }
}

/**
 * A request received by the backend from the frontend.
 */
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Request {
    /// A unique id for this request. This ID will be sent in
    /// the [`Response::request_id`] field for the response to this request
    pub request_id: u32,
    /// Body of this request, contains arguments relevant to the command being
    /// run
    #[serde(flatten)]
    pub body: RequestBody,
}

/**
 * Body of a request received by the backend from the frontend.
 */
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type", content = "data")]
pub enum RequestBody {
    Ping, // Used to check if the backend is alive

    GetGameList,
    GetGameListFromFs,
    GetGame(String),        // String is the game ID
    DownloadGame(String),   // String is the game ID
    DownloadIcon(String),   // String is the game ID
    DownloadBanner(String), // String is the game ID

    GetTagList,
    GetTag(String),             // String is the tag name
    GetGameListFromTag(String), // String is the tag name

    GetUser(String), // String is the user ID

    SetProduction(bool), // Sets prod / dev api url

    LaunchGame(String), // String is the game ID

    GetNfcTag(Player),  // u8 is the index of the reader. Right now just 0.
    GetNfcUser(String), // String is the association ID
}

impl RequestBody {
    /**
     * Get a list of all request variants for debugging purposes.
     */
    pub fn variants() -> Vec<Self> {
        vec![
            Self::Ping,
            Self::GetGameList,
            Self::GetGameListFromFs,
            Self::GetGame(String::new()),
            Self::DownloadGame(String::new()),
            Self::DownloadIcon(String::new()),
            Self::DownloadBanner(String::new()),
            Self::GetTagList,
            Self::GetTag(String::new()),
            Self::GetGameListFromTag(String::new()),
            Self::SetProduction(false),
            Self::LaunchGame(String::new()),
            Self::GetNfcTag(Player::P1),
            Self::GetNfcUser(String::new()),
        ]
    }
}

/**
 * A response sent by the backend to the frontend.
 */
#[derive(Debug, Serialize, Deserialize)]
pub struct Response {
    /// Request ID that caused this response to be generated.
    pub request_id: u32,
    /// Response generated by the command
    #[serde(flatten)]
    pub body: ResponseBody,
}

/**
 * A response sent by the backend to the frontend.
 */
#[derive(Debug, Serialize, Deserialize)]
#[serde(tag = "type", content = "data")]
pub enum ResponseBody {
    Pong,

    Ok,
    Err(String),

    GameList(Vec<DevcadeGame>),
    Game(DevcadeGame),

    TagList(Vec<Tag>),
    Tag(Tag),

    User(User),

    NfcTag(Option<String>),
    NfcUser(Map<String, Value>),

    #[serde(skip)]
    InternalGame(JoinHandle<ExitStatus>),
}

impl From<Error> for ResponseBody {
    fn from(error: Error) -> Self {
        Self::Err(error.to_string())
    }
}

impl ResponseBody {
    /**
     * Get all enum variants as a vector for debugging.
     */
    pub fn variants() -> Vec<Self> {
        vec![
            Self::Pong,
            Self::Ok,
            Self::Err(String::new()),
            Self::GameList(Vec::new()),
            Self::Game(DevcadeGame::default()),
            Self::TagList(Vec::new()),
            Self::Tag(Tag::default()),
            Self::InternalGame(std::thread::spawn(|| std::process::exit(0))),
            Self::NfcTag(None),
            Self::NfcUser(Map::default()),
        ]
    }
}

impl Display for RequestBody {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match &self {
            Self::Ping => write!(f, "Ping"),
            Self::GetGameList => write!(f, "Get Game List"),
            Self::GetGameListFromFs => write!(f, "Get Game List From Filesystem"),
            Self::GetGame(game_id) => {
                write!(f, "Get Game object with id '{game_id}'")
            }
            Self::DownloadGame(game_id) => {
                write!(f, "Download game with id '{game_id}'")
            }
            Self::DownloadIcon(game_id) => {
                write!(f, "Download icon with id '{game_id}'")
            }
            Self::DownloadBanner(game_id) => {
                write!(f, "Download banner with id '{game_id}'")
            }
            Self::LaunchGame(game_id) => {
                write!(f, "Launch game with id '{game_id}'")
            }
            Self::SetProduction(prod) => {
                write!(
                    f,
                    "Set API to '{}'",
                    if *prod { "production" } else { "development" }
                )
            }
            Self::GetTagList => write!(f, "Get Tag List"),
            Self::GetTag(tag_name) => write!(f, "Get Tag with name '{tag_name}'"),
            Self::GetGameListFromTag(tag_name) => {
                write!(f, "Get Game List from Tag with name '{tag_name}'")
            }
            Self::GetUser(uid) => write!(f, "Get User with id '{uid}'"),
            Self::GetNfcTag(player) => {
                write!(f, "Get NFC tags for player '{player}'")
            }
            Self::GetNfcUser(association_id) => {
                write!(f, "Get NFC users for association ID '{association_id}'")
            }
        }
    }
}

impl Display for Request {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        let id = self.request_id;
        let body = &self.body;
        write!(f, "[{id:9}] {body}")
    }
}

// Used for debug logging
impl Display for Response {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        let id = self.request_id;
        let body = &self.body;
        write!(f, "[{id:9}] {body}")
    }
}

impl Display for ResponseBody {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match &self {
            Self::Pong => write!(f, "Pong"),
            Self::Ok => write!(f, "Ok"),
            Self::Err(err) => write!(f, "Err: {err}"),
            Self::GameList(games) => {
                write!(f, "Got game list with {} games", games.len())
            }
            Self::Game(DevcadeGame { id, .. }) => {
                write!(f, "Downloaded game with id '{}'", id)
            }
            Self::InternalGame(_) => write!(f, "Launched game"),
            Self::TagList(tags) => {
                write!(f, "Got tag list with {} tags", tags.len())
            }
            Self::Tag(Tag { name, .. }) => write!(f, "Got tag with name '{name}'"),
            Self::User(User { id, .. }) => write!(f, "Got user with id '{id}'"),
            Self::NfcTag(tag_id) => {
                write!(f, "Got NFC tag ID '{tag_id:?}'")
            }
            Self::NfcUser(user) => {
                write!(f, "Got NFC user '{user:?}'")
            }
        }
    }
}