zero-trust-rps 0.0.5

Online Multiplayer Rock Paper Scissors
Documentation
use crate::{
    client::ui::MIN_ROOM_CODE_LEN,
    common::{message::RpsData, result::DynResult, rps::simple_move::SimpleUserMove},
};

use super::app::App;
use crossterm::event::{Event as CrosstermEvent, KeyCode, KeyEvent, KeyModifiers};
use tui_input::backend::crossterm::EventHandler;

/// Handles the key events and updates the state of [`App`].
pub async fn handle_key_events(key_event: KeyEvent, app: &mut App) -> DynResult<()> {
    match key_event.code {
        KeyCode::Enter => handle_confirm_action(app).await?,
        // Exit application on Esc
        KeyCode::Esc => {
            if app.pop_up.is_some() {
                app.close_err_pop_up();
            } else {
                app.quit();
            }
        }
        // Exit application on `Ctrl-C`
        KeyCode::Char('c') | KeyCode::Char('C') if key_event.modifiers == KeyModifiers::CONTROL => {
            app.quit();
        }
        _ => {
            if app.text_input_allowed() {
                if let Some(state) = app.input.handle_event(&CrosstermEvent::Key(key_event)) {
                    // dismiss err if value changed
                    if state.value {
                        app.close_err_pop_up();
                    }
                }
            }
        }
    };

    Ok(())
}

pub async fn handle_confirm_action(app: &mut App) -> DynResult<()> {
    if app.user_tried_to_change_state_already || app.is_waiting_for_others() {
        return Ok(());
    }
    if app.submit_error.is_some() {
        app.close_err_pop_up();
        return Ok(());
    }

    app.submit_error = None;

    use crate::common::rps::state::RpsState::*;
    match app.client_state.state {
        BeforeRoom => {
            if app.input.value().len() < MIN_ROOM_CODE_LEN {
                app.submit_error = Some(format!(
                    "The provided room code is too short, has to be at least {MIN_ROOM_CODE_LEN} long."
                ))
            } else {
                match app.input.value().parse() {
                    Ok(room_id) => {
                        app.do_move(SimpleUserMove::JoinRoom(room_id))?;
                        app.input.reset();
                    }
                    Err(err) => app.submit_error = Some(err.to_string()),
                }
            }
        }
        InRoom => {
            let max_len = app.input_max_length() as usize;

            let input_bytes = app.input.value().as_bytes();

            if app.input.value().is_empty() {
                app.submit_error = Some("Cannot submit an empty string.".into())
            } else if input_bytes.len() > max_len {
                app.submit_error = Some(format!(
                    "The provided input is too long, it can be at most {max_len} bytes long."
                ))
            } else {
                let mut arr = [0u8; 20];
                arr[..input_bytes.len()].copy_from_slice(input_bytes);

                let data = RpsData::new(arr).expect(
                    "should never fail as input_bytes has to be valid utf8 and can't be longer than allowed",
                );
                app.do_move(SimpleUserMove::Play(data))?;
                app.input.reset();
            }
        }
        Played => {
            app.do_move(SimpleUserMove::ConfirmPlay)?;
        }
        Confirmed => {
            app.do_move(SimpleUserMove::BackToRoom)?;
        }
    };

    if app.submit_error.is_none() {
        app.user_tried_to_change_state_already = true;
    }

    Ok(())
}