From 3e7127f3dfc036470e4716780c0e0565732eb01d Mon Sep 17 00:00:00 2001 From: Gregor Reitzenstein Date: Wed, 4 Nov 2020 11:51:16 +0100 Subject: [PATCH] Reworks state signalling --- src/app.rs | 92 +++++++++++++++++++++------------------------------ src/main.rs | 19 ++++++----- src/ui/mod.rs | 38 +++++++++++++-------- 3 files changed, 73 insertions(+), 76 deletions(-) diff --git a/src/app.rs b/src/app.rs index 221ef83..c191679 100644 --- a/src/app.rs +++ b/src/app.rs @@ -35,33 +35,28 @@ pub struct Sute<'a, S> { // TODO: BE SMART. Inputs change the state, resize signals change the state, futures completing // change the state. - state: SuteState<'a>, + state: Mutable, signal: S, inputs: Inputs, api: Option, log: Logger, + + drain: Arc>, } impl<'a, S: Unpin> Sute<'a, S> { - pub fn new(s: S, log: Logger, drain: Arc>, api: API) -> Self { + pub fn new(signal: S, log: Logger, drain: Arc>, api: Option) -> Self { let inputs = Inputs::new(); - let state = SuteState::new(drain); + let state = Mutable::new(SuteState::new()); - Self { - state: state, - - signal: s, - inputs: inputs, - api: Some(api), - log: log, - } + Self { state, signal, inputs, api, log, drain, } } fn run_cmd(&mut self, cmdline: String) { let mut words = cmdline.split_ascii_whitespace(); match words.next() { - Some("quit") => self.state.running = false, + Some("quit") => self.state.lock_mut().running = false, Some("connect") => { self.connect(cmdline) }, @@ -74,98 +69,85 @@ impl<'a, S: Unpin> Sute<'a, S> { } fn handle_resize(&mut self, new_size: (u16,u16)) { - self.state.size = new_size; + self.state.lock_mut().size = new_size; } - pub fn get_state(&self) -> SuteState<'a> { - self.state.clone() + pub fn get_state(&self) -> SuteState { + self.state.get_cloned() + } + + // We can make this more efficient by not cloning. But who cares? + pub fn signal(&self) -> impl Signal { + self.state.signal_cloned() } pub fn handle_key(&mut self, key: Key) { + let mut state = self.state.lock_mut(); match key { Key::Char('\n') => { - let cmd = mem::replace(&mut self.state.cmd_line, String::new()); + let cmd = mem::replace(&mut state.cmd_line, String::new()); + // drop the mutably borrowed state here so we can mutably re-borrow self afterwards + mem::drop(state); self.run_cmd(cmd); }, Key::Char(c) => { - self.state.cmd_line.push(c); + state.cmd_line.push(c); }, Key::Backspace => { - self.state.cmd_line.pop(); + state.cmd_line.pop(); }, _ => {} } } } -impl<'a, S: Signal + Unpin> Signal for Sute<'a, S> { - type Item = SuteState<'a>; - fn poll_change(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll> { - let mut dirty = false; +impl <'a, S: Unpin + Signal> Future for Sute<'a, S> { + type Output = (); + fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll { + // Return early if we're not running anymore + if ! self.state.lock_ref().running { + return Poll::Ready(()); + } if let Poll::Ready(Some(size)) = Pin::new(&mut self.signal).poll_change(cx) { - dirty = true; self.handle_resize(size); } match ready!(Pin::new(&mut self.inputs).poll_next(cx)) { Some(key) => { - dirty = true; self.handle_key(key); }, // If the input closes stop the program None => { - self.state.running = false; - return Poll::Ready(None); + self.state.lock_mut().running = false; + return Poll::Ready(()); }, } - if dirty { - Poll::Ready(Some(self.get_state())) - } else { - Poll::Pending - } - } -} - -impl <'a, S> Future for Sute<'a, S> { - type Output = (); - fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll { - if self.state.running { - Poll::Pending - } else { - Poll::Ready(()) - } + Poll::Pending } } #[derive(Debug, Clone)] -// TODO: `Signal` struct changes using this struct? -// TODO: If so, procmacro here? -enum SuteStateChange { - active_win(Window), -} - -#[derive(Debug, Clone)] -pub struct SuteState<'a> { +pub struct SuteState { pub active_win: Window, pub size: (u16,u16), pub running: bool, pub tick: usize, - pub server: Option<&'a str>, - pub log: Arc>, + pub server: Option, pub cmd_line: String, pub tick_c: char, + + // TODO: Figure out how to put log lines here signaled } -impl<'a> SuteState<'a> { - pub fn new(log: Arc>) -> Self { +impl SuteState { + pub fn new() -> Self { Self { active_win: Window::Main, size: (80,20), running: true, tick: 0, server: None, - log: log, cmd_line: String::new(), tick_c: '|', } diff --git a/src/main.rs b/src/main.rs index a577f55..70e58e9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -69,10 +69,11 @@ fn main() -> Result<(), io::Error> { let (rpc_future, mut api) = schema::bootstrap(log.clone(), stream); - let app = app::Sute::new(resize, log.clone(), drain, api); + let app = app::Sute::new(resize, log.clone(), drain.clone(), Some(api)); - let mut state = app.get_state(); - let mut stream = app.to_stream(); + let mut app_state = app.get_state(); + let mut ui_state = ui::UIState::new(app_state, drain); + let mut stream = app.signal().to_stream(); let ui_future = async move { let stdout = io::stdout().into_raw_mode()?; @@ -87,7 +88,7 @@ fn main() -> Result<(), io::Error> { terminal.resize(tui::layout::Rect::new(0, 0, x,y)).unwrap(); } - terminal.draw(|f| ui::draw_ui(f, &mut state)); + terminal.draw(|f| ui::draw_ui(f, &mut ui_state)); loop { if let Some(mut state) = stream.next().await { if !state.running { @@ -102,7 +103,8 @@ fn main() -> Result<(), io::Error> { _ => '|', }; state.tick_c = tick_c; - terminal.draw(|f| ui::draw_ui(f, &mut state))?; + ui_state.app_state = state; + terminal.draw(|f| ui::draw_ui(f, &mut ui_state))?; } else { break; } @@ -114,9 +116,10 @@ fn main() -> Result<(), io::Error> { Ok(()) }; - //lex.spawn(rpc_future).detach(); - let r: Result<(), io::Error> = smol::block_on(lex.run(ui_future)); - //smol::block_on(lex.run(Box::pin(app))); + lex.spawn(rpc_future).detach(); + let t: Task> = lex.spawn(ui_future); + t.detach(); + smol::block_on(lex.run(app)); Ok(()) } diff --git a/src/ui/mod.rs b/src/ui/mod.rs index 0d8f208..9bff226 100644 --- a/src/ui/mod.rs +++ b/src/ui/mod.rs @@ -1,3 +1,5 @@ +use std::sync::Arc; + use tui::{ backend::Backend, layout::{Layout, Direction, Constraint, Rect}, @@ -5,9 +7,19 @@ use tui::{ Frame, }; -use crate::app::SuteState; +use crate::app::{SuteState, LogDrain}; -pub fn draw_ui(f: &mut Frame, app: &mut SuteState) { +pub struct UIState<'a> { + pub app_state: SuteState, + pub loglines: Arc>, +} +impl<'a> UIState<'a> { + pub fn new(app_state: SuteState, loglines: Arc>) -> Self { + Self { app_state, loglines } + } +} + +pub fn draw_ui(f: &mut Frame, state: &mut UIState) { let outer_layout = Layout::default() .direction(Direction::Vertical) .constraints([ @@ -19,32 +31,32 @@ pub fn draw_ui(f: &mut Frame, app: &mut SuteState) { .split(f.size()); - draw_header(f, app, outer_layout[0]); - draw_main(f, app, outer_layout[1]); - draw_command_line(f, app, outer_layout[2]); + draw_header(f, state, outer_layout[0]); + draw_main(f, state, outer_layout[1]); + draw_command_line(f, state, outer_layout[2]); } -fn draw_header(f: &mut Frame, app: &mut SuteState, layout_chunk: Rect) { +fn draw_header(f: &mut Frame, state: &mut UIState, layout_chunk: Rect) { f.render_widget(Block::default() .title("Status") .borders(Borders::ALL), layout_chunk); } -fn draw_main(f: &mut Frame, app: &mut SuteState, layout_chunk: Rect) { +fn draw_main(f: &mut Frame, state: &mut UIState, layout_chunk: Rect) { // let chunk = Layout::default() // .direction(Direction::Horizontal) // .constraints([Constraint::Percentage(20), Constraint::Percentage(80)].as_ref()) // .split(layout_chunk); - draw_logs(f, app, layout_chunk) + draw_logs(f, state, layout_chunk) } -fn draw_logs(f: &mut Frame, app: &mut SuteState, layout_chunk: Rect) { +fn draw_logs(f: &mut Frame, state: &mut UIState, layout_chunk: Rect) { // TODO: Just use a signal. - let v = app.log.get_inner().clone(); - f.render_widget(Paragraph::new(v.into_iter().collect::>()), layout_chunk); + let v = state.loglines.get_inner().clone(); + f.render_widget(Paragraph::new(v), layout_chunk); } -fn draw_command_line(f: &mut Frame, app: &mut SuteState, layout_chunk: Rect) { +fn draw_command_line(f: &mut Frame, state: &mut UIState, layout_chunk: Rect) { let block = Block::default() .title("Command line") .borders(Borders::ALL); @@ -52,6 +64,6 @@ fn draw_command_line(f: &mut Frame, app: &mut SuteState, layout_c f.render_widget(block, layout_chunk); - let cmdline = format!("{} > {}", app.tick_c, app.cmd_line); + let cmdline = format!("{} > {}", state.app_state.tick_c, state.app_state.cmd_line); f.render_widget(Paragraph::new(&cmdline[..]), inner_rect); }