Skip to content

Commit

Permalink
Replace Option<Action> with ActionOption
Browse files Browse the repository at this point in the history
This allows us to mark ActionOption as
`#[must_use]`, which helps prevent ignoring
actions accidentally (e.g. by forgetting to handle
a return value).  See
rust-lang/rust#71368 for
more context.
  • Loading branch information
matta committed May 22, 2024
1 parent 08baa92 commit 8bf3af6
Show file tree
Hide file tree
Showing 9 changed files with 62 additions and 51 deletions.
16 changes: 15 additions & 1 deletion tui/src/state_store/action.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,19 @@
#[derive(Debug, Clone)]
/// The set of actions that can be performed in the application's state store.
///
/// This enum represents the different types of actions that can be dispatched to the
/// state store, which will then update the application's state accordingly.
#[must_use]
#[derive(Debug, Clone, PartialEq)]
pub enum Action {
/// No action.
///
/// This is used when no action is needed. While we could model this as an
/// `Option<Action>`, this would preclude marking the type as #[must_use],
/// and thus invite code that accidentally ignores actions returned from functions.
///
/// See https://github.com/rust-lang/rust/issues/71368 for a more detailed
/// discussion of this issue.
None,
ConnectToServerRequest { addr: String },
SendMessage { content: String },
SelectRoom { room: String },
Expand Down
3 changes: 1 addition & 2 deletions tui/src/ui_management/components/component.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,7 @@ pub trait Component {
// Returns a name used to describe the component in the UI.
fn name(&self) -> &str;

#[must_use]
fn handle_key_event(&mut self, key: KeyEvent) -> Option<Action>;
fn handle_key_event(&mut self, key: KeyEvent) -> Action;
}

pub trait ComponentRender<Props> {
Expand Down
4 changes: 2 additions & 2 deletions tui/src/ui_management/components/input_box.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ impl Component for InputBox {
"Input Box"
}

fn handle_key_event(&mut self, key: KeyEvent) -> Option<Action> {
fn handle_key_event(&mut self, key: KeyEvent) -> Action {
if key.kind == KeyEventKind::Press {
match key.code {
KeyCode::Char(to_insert) => {
Expand All @@ -113,7 +113,7 @@ impl Component for InputBox {
_ => {}
}
}
None
Action::None
}
}

Expand Down
22 changes: 10 additions & 12 deletions tui/src/ui_management/pages/chat_page/chat_page.rs
Original file line number Diff line number Diff line change
Expand Up @@ -171,9 +171,9 @@ impl Component for ChatPage {
"Chat Page"
}

fn handle_key_event(&mut self, key: KeyEvent) -> Option<Action> {
fn handle_key_event(&mut self, key: KeyEvent) -> Action {
if key.kind != KeyEventKind::Press {
return None;
return Action::None;
}

let active_section = self.active_section.clone();
Expand All @@ -186,25 +186,23 @@ impl Component for ChatPage {
self.active_section = Some(last_hovered_section.clone());
self.get_section_activation_for_section(&last_hovered_section)
.activate();
None
Action::None
}
KeyCode::Left => {
self.hover_previous();
None
Action::None
}
KeyCode::Right => {
self.hover_next();
None
Action::None
}
KeyCode::Char('q') => Some(Action::Exit),
KeyCode::Char('c') if key.modifiers.contains(KeyModifiers::CONTROL) => {
Some(Action::Exit)
}
_ => None,
KeyCode::Char('q') => Action::Exit,
KeyCode::Char('c') if key.modifiers.contains(KeyModifiers::CONTROL) => Action::Exit,
_ => Action::None,
},
Some(section) => {
// FIXME: return the result of this function call!!!!
let action = self.get_component_for_section_mut(&section)
let action = self
.get_component_for_section_mut(&section)
.handle_key_event(key);

// disable the section according to the action taken
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,13 +43,12 @@ impl MessageInputBox {
}
}

#[must_use]
fn submit_message(&mut self) -> Option<Action> {
let mut ret = None;
fn submit_message(&mut self) -> Action {
let mut ret = Action::None;
if !self.input_box.is_empty() {
ret = Some(Action::SendMessage {
ret = Action::SendMessage {
content: String::from(self.input_box.text()),
});
};
self.input_box.reset();
}
ret
Expand All @@ -65,16 +64,16 @@ impl Component for MessageInputBox {
"Message Input"
}

fn handle_key_event(&mut self, key: KeyEvent) -> Option<Action> {
fn handle_key_event(&mut self, key: KeyEvent) -> Action {
if key.kind == KeyEventKind::Press && self.props.active_room.is_some() {
let action = self.input_box.handle_key_event(key);
assert!(action.is_none());
assert_eq!(action, Action::None);

if key.code == KeyCode::Enter {
return self.submit_message();
}
}
None
Action::None
}
}

Expand Down
14 changes: 7 additions & 7 deletions tui/src/ui_management/pages/chat_page/components/room_list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -124,30 +124,30 @@ impl Component for RoomList {
"Room List"
}

fn handle_key_event(&mut self, key: KeyEvent) -> Option<Action>{
fn handle_key_event(&mut self, key: KeyEvent) -> Action {
if key.kind != KeyEventKind::Press {
None
Action::None
} else {
match key.code {
KeyCode::Up => {
self.previous();
None
Action::None
}
KeyCode::Down => {
self.next();
None
Action::None
}
KeyCode::Enter if self.list_state.selected().is_some() => {
let selected_idx = self.list_state.selected().unwrap();

let rooms = self.rooms();
let room_state = rooms.get(selected_idx).unwrap();

Some(Action::SelectRoom {
Action::SelectRoom {
room: room_state.name.clone(),
})
}
}
_ => None,
_ => Action::None,
}
}
}
Expand Down
34 changes: 17 additions & 17 deletions tui/src/ui_management/pages/connect_page/connect_page.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
use crossterm::event::{KeyCode, KeyEvent, KeyEventKind, KeyModifiers};
use ratatui::{prelude::*, widgets::*, Frame};

use crate::state_store::ServerConnectionStatus;
use crate::state_store::{action::Action, State};

use crate::ui_management::components::input_box;
use crate::ui_management::components::{input_box::InputBox, Component, ComponentRender};
use crate::{
state_store::{action::Action, ServerConnectionStatus, State},
ui_management::components::{
input_box::{self, InputBox},
Component, ComponentRender,
},
};

struct Props {
error_message: Option<String>,
Expand Down Expand Up @@ -47,14 +49,14 @@ impl ConnectPage {
}
}

fn connect_to_server(&mut self) -> Option<Action> {
fn connect_to_server(&mut self) -> Action {
if self.input_box.is_empty() {
return None;
return Action::None;
}

Some(Action::ConnectToServerRequest {
Action::ConnectToServerRequest {
addr: self.input_box.text().to_string(),
})
}
}
}

Expand All @@ -69,21 +71,19 @@ impl Component for ConnectPage {
"Connect Page"
}

fn handle_key_event(&mut self, key: KeyEvent) -> Option<Action> {
fn handle_key_event(&mut self, key: KeyEvent) -> Action {
let action = self.input_box.handle_key_event(key);
assert!(action.is_none());
assert_eq!(action, Action::None);

if key.kind != KeyEventKind::Press {
return None;
return Action::None;
}

match key.code {
KeyCode::Enter => self.connect_to_server(),
KeyCode::Char('q') => Some(Action::Exit),
KeyCode::Char('c') if key.modifiers.contains(KeyModifiers::CONTROL) => {
Some(Action::Exit)
}
_ => None
KeyCode::Char('q') => Action::Exit,
KeyCode::Char('c') if key.modifiers.contains(KeyModifiers::CONTROL) => Action::Exit,
_ => Action::None,
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion tui/src/ui_management/pages/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ impl Component for AppRouter {
self.get_active_page_component().name()
}

fn handle_key_event(&mut self, key: KeyEvent) -> Option<Action> {
fn handle_key_event(&mut self, key: KeyEvent) -> Action {
self.get_active_page_component_mut().handle_key_event(key)
}
}
Expand Down
3 changes: 2 additions & 1 deletion tui/src/ui_management/ui_manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,8 @@ impl UiManager {
// Catch and handle crossterm events
maybe_event = crossterm_events.next() => match maybe_event {
Some(Ok(Event::Key(key))) => {
if let Some(action) = app_router.handle_key_event(key) {
let action = app_router.handle_key_event(key);
if action != Action::None {
self.action_tx.send(action)?;
}
},
Expand Down

0 comments on commit 8bf3af6

Please sign in to comment.