From b6d953003906cd3cbc8d9b99a1d0ac87483a34a2 Mon Sep 17 00:00:00 2001 From: Enrico Risa Date: Wed, 25 Sep 2024 18:49:45 +0200 Subject: [PATCH] chore: refactor cmd system to be async non blocking of TUI main loop (#24) --- edc-connector-tui/src/app.rs | 17 +++--- edc-connector-tui/src/app/action.rs | 4 -- edc-connector-tui/src/components.rs | 61 +++++++------------ edc-connector-tui/src/components/resources.rs | 9 ++- edc-connector-tui/src/components/table.rs | 2 +- edc-connector-tui/src/runner.rs | 33 +++------- 6 files changed, 46 insertions(+), 80 deletions(-) diff --git a/edc-connector-tui/src/app.rs b/edc-connector-tui/src/app.rs index cdaef66..a1ea7ec 100644 --- a/edc-connector-tui/src/app.rs +++ b/edc-connector-tui/src/app.rs @@ -6,6 +6,7 @@ mod msg; use crossterm::event::{self, Event, KeyCode}; use edc_connector_client::{Auth, EdcConnectorClient}; +use futures::FutureExt; use keyring::Entry; use ratatui::{ layout::{Constraint, Direction, Layout, Rect}, @@ -18,8 +19,8 @@ use crate::{ contract_definitions::ContractDefinitionsComponent, contract_negotiations::ContractNegotiationsComponent, footer::Footer, header::HeaderComponent, launch_bar::LaunchBar, policies::PolicyDefinitionsComponent, - transfer_processes::TransferProcessesComponent, Action, Component, ComponentEvent, - ComponentMsg, ComponentReturn, Notification, NotificationMsg, + transfer_processes::TransferProcessesComponent, Component, ComponentEvent, ComponentMsg, + ComponentReturn, Notification, NotificationMsg, }, config::{AuthKind, Config, ConnectorConfig}, types::{ @@ -126,11 +127,13 @@ impl App { let timeout = noty.timeout(); self.footer.show_notification(noty); - let action = Action::spawn(async move { - tokio::time::sleep(Duration::from_secs(timeout)).await; - Ok(Action::ClearNotification) - }); - Ok(ComponentReturn::action(action)) + Ok(ComponentReturn::cmd( + async move { + tokio::time::sleep(Duration::from_secs(timeout)).await; + Ok(vec![AppMsg::NontificationMsg(NotificationMsg::Clear).into()]) + } + .boxed(), + )) } pub fn clear_notification(&mut self) -> anyhow::Result> { diff --git a/edc-connector-tui/src/app/action.rs b/edc-connector-tui/src/app/action.rs index 8a340a6..ad572ac 100644 --- a/edc-connector-tui/src/app/action.rs +++ b/edc-connector-tui/src/app/action.rs @@ -16,10 +16,6 @@ impl ActionHandler for App { crate::components::NotificationMsg::Show(noty), ) .into()]), - (_, Action::ClearNotification) => Ok(vec![AppMsg::NontificationMsg( - crate::components::NotificationMsg::Clear, - ) - .into()]), _ => Ok(vec![]), } } diff --git a/edc-connector-tui/src/components.rs b/edc-connector-tui/src/components.rs index e60c21a..2249357 100644 --- a/edc-connector-tui/src/components.rs +++ b/edc-connector-tui/src/components.rs @@ -1,4 +1,4 @@ -use std::{fmt::Debug, future::Future, sync::Arc}; +use std::{fmt::Debug, sync::Arc}; use crossterm::event::Event; use futures::{future::BoxFuture, FutureExt}; @@ -26,7 +26,7 @@ pub trait StatelessComponent { #[async_trait::async_trait] pub trait Component { - type Msg: Send; + type Msg: Send + 'static; type Props: Send; async fn init(&mut self, _props: Self::Props) -> anyhow::Result> { @@ -49,14 +49,14 @@ pub trait Component { Ok(vec![]) } - async fn forward_update<'a, F, C>( - other: &'a mut C, + async fn forward_update( + other: &mut C, msg: ComponentMsg, mapper: F, ) -> anyhow::Result> where - F: Fn(C::Msg) -> Self::Msg + Send + Sync + 'a, - C: Component + Sync + Send + 'a, + F: Fn(C::Msg) -> Self::Msg + Send + Sync + 'static, + C: Component + Sync + Send + 'static, { Ok(other.update(msg).await?.map(mapper)) } @@ -67,8 +67,8 @@ pub trait Component { mapper: F, ) -> anyhow::Result> where - F: Fn(C::Msg) -> Self::Msg + Send + Sync + 'a, - C: Component + Sync + Send + 'a, + F: Fn(C::Msg) -> Self::Msg + Send + Sync + 'static, + C: Component + Sync + Send + 'static, { Ok(other.init(props).await?.map(mapper)) } @@ -94,13 +94,13 @@ pub trait Component { pub struct ComponentMsg(T); #[derive(Default)] -pub struct ComponentReturn<'a, T> { +pub struct ComponentReturn { pub(crate) msgs: Vec>, - pub(crate) cmds: Vec>>>>, + pub(crate) cmds: Vec>>>>, pub(crate) actions: Vec, } -impl<'a, T: Debug> Debug for ComponentReturn<'a, T> { +impl Debug for ComponentReturn { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("ComponentReturn") .field("msgs", &self.msgs) @@ -109,34 +109,13 @@ impl<'a, T: Debug> Debug for ComponentReturn<'a, T> { } } +#[derive(Debug)] pub enum Action { Quit, Esc, NavTo(Nav), ChangeSheet, Notification(Notification), - ClearNotification, - Spawn(BoxFuture<'static, anyhow::Result>), -} - -impl Action { - pub fn spawn(fut: impl Future> + Send + 'static) -> Action { - Action::Spawn(fut.boxed()) - } -} - -impl Debug for Action { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Self::Quit => write!(f, "Quit"), - Self::Esc => write!(f, "Esc"), - Self::NavTo(arg0) => f.debug_tuple("NavTo").field(arg0).finish(), - Self::ChangeSheet => write!(f, "ChangeSheet"), - Self::Notification(arg0) => f.debug_tuple("Notification").field(arg0).finish(), - Self::Spawn(_arg0) => f.debug_tuple("Spawn").finish(), - Self::ClearNotification => f.debug_tuple("ClearNotification").finish(), - } - } } #[derive(Debug, Clone)] @@ -202,8 +181,10 @@ impl ComponentMsg { } } -impl<'a, T: 'a> ComponentReturn<'a, T> { - pub fn cmd(cmd: BoxFuture<'a, anyhow::Result>>>) -> ComponentReturn<'a, T> { +impl ComponentReturn { + pub fn cmd( + cmd: BoxFuture<'static, anyhow::Result>>>, + ) -> ComponentReturn { ComponentReturn { msgs: vec![], cmds: vec![cmd], @@ -211,7 +192,7 @@ impl<'a, T: 'a> ComponentReturn<'a, T> { } } - pub fn empty() -> ComponentReturn<'a, T> { + pub fn empty() -> ComponentReturn { ComponentReturn { msgs: vec![], cmds: vec![], @@ -219,7 +200,7 @@ impl<'a, T: 'a> ComponentReturn<'a, T> { } } - pub fn action(action: Action) -> ComponentReturn<'a, T> { + pub fn action(action: Action) -> ComponentReturn { ComponentReturn { msgs: vec![], cmds: vec![], @@ -227,9 +208,9 @@ impl<'a, T: 'a> ComponentReturn<'a, T> { } } - pub fn map(self, mapper: F) -> ComponentReturn<'a, M> + pub fn map(self, mapper: F) -> ComponentReturn where - F: Fn(T) -> M + Sync + Send + 'a, + F: Fn(T) -> M + Sync + Send + 'static, { let msgs = self.msgs.into_iter().map(|msg| msg.map(&mapper)).collect(); @@ -265,7 +246,7 @@ impl From for ComponentMsg { } } -impl From> for ComponentReturn<'_, T> { +impl From> for ComponentReturn { fn from(value: ComponentMsg) -> Self { ComponentReturn { msgs: vec![value], diff --git a/edc-connector-tui/src/components/resources.rs b/edc-connector-tui/src/components/resources.rs index 5ac1166..7c4e82e 100644 --- a/edc-connector-tui/src/components/resources.rs +++ b/edc-connector-tui/src/components/resources.rs @@ -43,7 +43,7 @@ pub struct ResourcesComponent { page_size: u32, } -impl ResourcesComponent { +impl ResourcesComponent { pub fn on_fetch(mut self, on_fetch: F) -> Self where F: Fn(Connector, Query) -> Fut + Send + Sync + 'static, @@ -77,9 +77,12 @@ impl ResourcesComponent { if let (Some(connector), Some(on_fetch)) = (self.connector.as_ref(), self.on_fetch.as_ref()) { let query = self.query.clone(); + + let connector = connector.clone(); + let on_fetch = on_fetch.clone(); Ok(ComponentReturn::cmd( async move { - match on_fetch(connector, query).await { + match on_fetch(&connector, query).await { Ok(elements) => Ok(vec![ResourcesMsg::ResourcesFetched(elements).into()]), Err(err) => Ok(vec![ ResourcesMsg::ResourcesFetchFailed(err.to_string()).into() @@ -160,7 +163,7 @@ impl Default for ResourcesComponent } #[async_trait::async_trait] -impl Component for ResourcesComponent { +impl Component for ResourcesComponent { type Msg = ResourcesMsg; type Props = Connector; diff --git a/edc-connector-tui/src/components/table.rs b/edc-connector-tui/src/components/table.rs index 1e3799c..1a7c29f 100644 --- a/edc-connector-tui/src/components/table.rs +++ b/edc-connector-tui/src/components/table.rs @@ -54,7 +54,7 @@ pub trait TableEntry { } #[async_trait::async_trait] -impl Component for UiTable { +impl Component for UiTable { type Msg = TableMsg; type Props = (); diff --git a/edc-connector-tui/src/runner.rs b/edc-connector-tui/src/runner.rs index 69f06b7..1d4e322 100644 --- a/edc-connector-tui/src/runner.rs +++ b/edc-connector-tui/src/runner.rs @@ -23,7 +23,6 @@ impl::Msg> + Send> Runner terminal.clear()?; let mut should_quit = false; - let action_queue = Arc::new(Mutex::new(VecDeque::::new())); let async_msgs = Arc::new(Mutex::new( VecDeque::::Msg>>::new(), )); @@ -63,38 +62,22 @@ impl::Msg> + Send> Runner } for c in ret.cmds { - for m in c.await.unwrap() { - msgs.push_back(m); - } + let inner_async_msg = async_msgs.clone(); + tokio::task::spawn(async move { + for m in c.await.unwrap() { + let mut msg_guard = inner_async_msg.lock().await; + msg_guard.push_back(m); + } + }); } ret.actions }; for a in actions { - if let Action::Spawn(handler) = a { - let inner_action_queue = action_queue.clone(); - tokio::task::spawn(async move { - if let Ok(action) = handler.await { - inner_action_queue.lock().await.push_back(action) - } - }); - } else { - should_quit = should_quit || matches!(a, Action::Quit); - for m in self.component.handle_action(a)? { - msgs.push_back(m) - } - } - } - } - let mut guard = action_queue.lock().await; - let mut msg_guard = async_msgs.lock().await; - while let Some(a) = guard.pop_front() { - if let Action::Spawn(_) = a { - } else { should_quit = should_quit || matches!(a, Action::Quit); for m in self.component.handle_action(a)? { - msg_guard.push_back(m) + msgs.push_back(m) } } }