Skip to content

Commit

Permalink
(wip) refactor recv calls to try_recv
Browse files Browse the repository at this point in the history
  • Loading branch information
Viet Trong Duong authored Sep 8, 2024
1 parent 8c5d93e commit bb9c06f
Show file tree
Hide file tree
Showing 8 changed files with 89 additions and 85 deletions.
73 changes: 48 additions & 25 deletions src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@
//! here - "quitting" implies that the exits immediately (in other words,
//! force-quit), while "closing" doesn't.
use color_eyre::eyre::eyre;
use derive_new::new;
use ratatui::layout::Rect;
use serde::Serialize;
use tokio::sync::mpsc::error::TryRecvError;
use tracing::{
debug,
error,
Expand Down Expand Up @@ -91,24 +93,20 @@ impl App {
/// Starts the app with the provided terminal interface and with a landing
/// [`HomeScreen`].
#[instrument(name = "run-app", skip_all)]
pub async fn run(
mut self,
mut tui: Tui,
config: Config,
) -> crate::Result<()> {
pub fn run(mut self, mut tui: Tui, config: Config) -> crate::Result<()> {
debug!(?config, "using provided config");
self.set_run_state(AppRunState::Running);
tui.enter()?;
self.screen_handler.push_active_screen(HomeScreen);
self.event_loop(tui).await?;
self.event_loop(tui)?;
Ok(())
}

/// App event loop.
async fn event_loop(&mut self, mut tui: Tui) -> crate::Result<()> {
fn event_loop(&mut self, mut tui: Tui) -> crate::Result<()> {
loop {
self.relay_tui_event(&mut tui).await?;
self.process_chan_events(&mut tui).await?;
self.relay_tui_event(&mut tui)?;
self.process_app_events(&mut tui)?;
self.update()?;
if self.run_state == AppRunState::Finished {
break;
Expand All @@ -119,12 +117,40 @@ impl App {

/// Handles an event received from from the provided [`Tui`], then
/// sends that event through the [channel].
async fn relay_tui_event(&mut self, tui: &mut Tui) -> crate::Result<()> {
let Some(event) = tui.recv_tui_event().await else {
info!("no tui events were received");
return Ok(());
fn relay_tui_event(&mut self, tui: &mut Tui) -> crate::Result<()> {
let event = match tui.try_recv_event() {
Ok(event) => event,
Err(err) => return Self::handle_try_recv_err(err, "tui"),
};
error!("something is BOUND to happen");
self.handle_tui_event(event)
}

/// Handles an error encountered while
/// [`try_recv`](tokio::sync::mpsc::UnboundedReceiver::try_recv)ing from a
/// particular event source. The source name is interpolated directly
/// into the log & error messages.
///
/// The function returns something the event loop can propagate
/// back up and handle accordingly. Only a [`TryRecvError::Disconnected`]
/// will result in an error.
fn handle_try_recv_err(
err: TryRecvError,
source: &'static str,
) -> crate::Result<()> {
match err {
TryRecvError::Empty => {
info!("{source} is currently empty");
Ok(())
},
TryRecvError::Disconnected => Err(eyre!(
"while trying to receive from the {source}: {source} event \
channel disconnected"
)),
}
}

/// Handles a given [`TuiEvent`].
fn handle_tui_event(&mut self, event: TuiEvent) -> crate::Result<()> {
match event {
TuiEvent::Hello => {
info!("received init event! very considerate of you, kind tui");
Expand All @@ -138,22 +164,20 @@ impl App {
event => {
self.event_channel
.send(Event::App(event.try_into().unwrap()))?;
error!("SENDING LOTS OF SHIT");
},
}
Ok(())
}

/// Receives and handles all incoming events from the [event
/// channel](Self::event_channel).
async fn process_chan_events(
&mut self,
tui: &mut Tui,
) -> crate::Result<()> {
while let Some(event) = self.event_channel.recv().await {
self.event(tui, &event)?;
fn process_app_events(&mut self, tui: &mut Tui) -> crate::Result<()> {
loop {
match self.event_channel.try_recv() {
Ok(event) => self.event(tui, &event)?,
Err(err) => return Self::handle_try_recv_err(err, "app"),
}
}
Ok(())
}

/// Returns whether the app has reached a point where it can be declared
Expand Down Expand Up @@ -252,8 +276,7 @@ impl App {

/// Logs the error and displays it on a popup in the terminal.
fn error_occurred(&mut self, msg: &str) -> crate::Result<()> {
let msg = format!("received an error message: {msg}");
error!(msg, "received an error");
error!(msg, "an error event occurred");
todo!();
}

Expand All @@ -271,7 +294,7 @@ impl App {
}

/// Handles an focus change event.
fn change_focus(&mut self, change: FocusChange) -> crate::Result<()> {
fn change_focus(&mut self, _change: FocusChange) -> crate::Result<()> {
todo!()
}
}
4 changes: 2 additions & 2 deletions src/components/screens/home.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ impl Screen for HomeScreen {
tracing::info!(?event, "receiving an event honey");
if let Event::App(AppEvent::UserInputs(inputs)) = event {
for input in inputs {
if let InputEvent::Key(key) = input {
if let InputEvent::Key(_key) = input {
event_sender.send(Event::App(AppEvent::CloseApp))?;
}
}
Expand All @@ -55,7 +55,7 @@ impl Screen for HomeScreen {
/// Renders this screen.
fn render(
&mut self,
state: &mut ScreenState,
_state: &mut ScreenState,
frame: &mut Frame,
) -> crate::Result<()> {
frame.render_widget(Paragraph::new("helo, world!"), frame.size());
Expand Down
1 change: 0 additions & 1 deletion src/events/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,6 @@ impl TryFrom<TuiEvent> for AppEvent {
},
TuiEvent::Tick => Self::Tick,
TuiEvent::Render => Self::Render,
TuiEvent::Error(msg) => Self::ErrorOccurred(msg),
TuiEvent::Focus(change) => Self::ChangeFocus(change),
TuiEvent::Paste(text) => Self::PasteText(text),
TuiEvent::Resize(w, h) => Self::ResizeTerminal(w, h),
Expand Down
4 changes: 0 additions & 4 deletions src/events/tui.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,6 @@ pub enum TuiEvent {
/// Renders the application to the terminal.
Render,

/// An error occurred (while retrieving the next terminal event from
/// [`EventStream`](CrosstermEventStream)).
Error(String),

/// The terminal is resized to `(width, height)`.
Resize(u16, u16),

Expand Down
7 changes: 3 additions & 4 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,17 +35,16 @@ mod utils;
/// [Result](color_eyre::eyre::Result) type.
type Result<T, E = color_eyre::eyre::Report> = color_eyre::eyre::Result<T, E>;

async fn run() -> Result<()> {
fn run() -> Result<()> {
initialize_services()?;
let config = Config::fetch()?;
let tui = Tui::with_specs(&config.game_specs)?;
App::default().run(tui, config).await?;
Ok(())
App::default().run(tui, config)
}

#[tokio::main]
async fn main() -> Result<()> {
if let Err(err) = run().await {
if let Err(err) = run() {
Err(err
.wrap_err("oh no! something went unhandled!")
.note("someone get me a paper bag PRONTO")
Expand Down
59 changes: 24 additions & 35 deletions src/tui.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,10 @@ use std::{
time::Duration,
};

use color_eyre::eyre::eyre;
use color_eyre::{
eyre::eyre,
Section,
};
use crossterm::{
cursor::{
DisableBlinking,
Expand All @@ -28,7 +31,6 @@ use crossterm::{
DisableMouseCapture,
EnableBracketedPaste,
EnableFocusChange,
EnableMouseCapture,
EventStream as CrosstermEventStream,
},
execute,
Expand All @@ -50,7 +52,10 @@ use serde::{
Serialize,
};
use tokio::{
sync::mpsc::UnboundedSender,
sync::mpsc::{
error::TryRecvError,
UnboundedSender,
},
task::JoinHandle,
time::interval,
};
Expand Down Expand Up @@ -92,13 +97,13 @@ pub struct Tui {
terminal: Terminal,

/// Handle for event task.
event_task: JoinHandle<()>,
event_task: JoinHandle<crate::Result<()>>,

/// This handler's cancellation token.
pub cancel_token: CancellationToken,

/// [`TuiEvent`] channel.
event_channel: UnboundedChannel<TuiEvent>,
pub event_channel: UnboundedChannel<TuiEvent>,

/// Tick rate - how rapidly to update state.
tick_rate: Duration,
Expand All @@ -113,17 +118,17 @@ impl Tui {
pub fn with_specs(game_specs: &GameSpecs) -> crate::Result<Self> {
Ok(Self::new(
Terminal::new(CrosstermBackend::new(stdout()))?,
tokio::spawn(async {}),
tokio::spawn(async move { Ok(()) }),
CancellationToken::new(),
UnboundedChannel::new(),
game_specs.get_tick_rate()?,
game_specs.get_frame_rate()?,
))
}

/// Receives the next [TUI event](TuiEvent).
pub async fn recv_tui_event(&mut self) -> Option<TuiEvent> {
self.event_channel.recv().await
/// Tries to receive the next [TUI event](TuiEvent).
pub fn try_recv_event(&mut self) -> Result<TuiEvent, TryRecvError> {
self.event_channel.try_recv()
}

/// Sends one [TUI event](TuiEvent) to the [event
Expand All @@ -146,14 +151,13 @@ impl Tui {
cancel_token: CancellationToken,
tick_rate: Duration,
frame_rate: Duration,
) {
) -> crate::Result<()> {
let mut event_stream = CrosstermEventStream::new();
let mut tick_interval = interval(tick_rate);
let mut render_interval = interval(frame_rate);

if let Err(err) = event_sender.send(TuiEvent::Hello) {
error!(%err, "unable to send greetings");
return;
return Err(eyre!("while sending greetings! how rude: {err}"));
}

loop {
Expand All @@ -173,25 +177,24 @@ impl Tui {
event.into()
},
Some(Err(err)) => {
error!(%err, "while receiving from event stream");
TuiEvent::Error(err.to_string())
return Err(eyre!("while receiving from event stream: {err}"));
},
None => {
warn!("event stream closed; no more events are to be consumed");
break;
}
},
};
if let Err(err) = Self::send_tui_event(&event_sender, tui_event) {
error!(
%err,
"failed to send tui event; breaking tui event loop \
now"
);
break;
if let Err(err) =
Self::send_tui_event(&event_sender, tui_event.clone())
{
return Err(eyre!("while sending tui event: {err}").with_note(
|| format!("trying to send event: {tui_event:?}"),
));
}
}
info!("tui event loop is finished");
Ok(())
}

/// Begins event reception and enters the terminal.
Expand Down Expand Up @@ -285,20 +288,6 @@ impl Tui {
)?;
Ok(())
}

/// Enables mouse capture.
pub fn enable_mouse_capture() -> crate::Result<()> {
info!("enabling mouse capture");
execute!(stdout(), EnableMouseCapture)?;
Ok(())
}

/// Disables mouse capture.
pub fn disable_mouse_capture() -> crate::Result<()> {
info!("disabling mouse capture");
execute!(stdout(), DisableMouseCapture)?;
Ok(())
}
}

impl Deref for Tui {
Expand Down
6 changes: 0 additions & 6 deletions src/ui/screens/handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -126,12 +126,6 @@ impl ScreenHandler {
self.screens.clear();
}

/// Gets the current active screen (the final one on the
/// [stack](Self::stack)).
pub fn get_active_screen(&self) -> Option<&ScreenHandle> {
self.screens.last()
}

/// Gets the current active screen (the final one on the
/// [stack](Self::stack)) mutably.
pub fn get_mut_active_screen(&mut self) -> Option<&mut ScreenHandle> {
Expand Down
Loading

0 comments on commit bb9c06f

Please sign in to comment.