mirror of
https://gitlab.com/fabinfra/fabaccess/sute.git
synced 2025-03-12 14:41:52 +01:00
Reworks state signalling
This commit is contained in:
parent
21550b411d
commit
3e7127f3df
90
src/app.rs
90
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
|
// TODO: BE SMART. Inputs change the state, resize signals change the state, futures completing
|
||||||
// change the state.
|
// change the state.
|
||||||
|
|
||||||
state: SuteState<'a>,
|
state: Mutable<SuteState>,
|
||||||
|
|
||||||
signal: S,
|
signal: S,
|
||||||
inputs: Inputs,
|
inputs: Inputs,
|
||||||
api: Option<API>,
|
api: Option<API>,
|
||||||
log: Logger,
|
log: Logger,
|
||||||
|
|
||||||
|
drain: Arc<LogDrain<'a>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, S: Unpin> Sute<'a, S> {
|
impl<'a, S: Unpin> Sute<'a, S> {
|
||||||
pub fn new(s: S, log: Logger, drain: Arc<LogDrain<'a>>, api: API) -> Self {
|
pub fn new(signal: S, log: Logger, drain: Arc<LogDrain<'a>>, api: Option<API>) -> Self {
|
||||||
let inputs = Inputs::new();
|
let inputs = Inputs::new();
|
||||||
let state = SuteState::new(drain);
|
let state = Mutable::new(SuteState::new());
|
||||||
|
|
||||||
Self {
|
Self { state, signal, inputs, api, log, drain, }
|
||||||
state: state,
|
|
||||||
|
|
||||||
signal: s,
|
|
||||||
inputs: inputs,
|
|
||||||
api: Some(api),
|
|
||||||
log: log,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_cmd(&mut self, cmdline: String) {
|
fn run_cmd(&mut self, cmdline: String) {
|
||||||
let mut words = cmdline.split_ascii_whitespace();
|
let mut words = cmdline.split_ascii_whitespace();
|
||||||
match words.next() {
|
match words.next() {
|
||||||
Some("quit") => self.state.running = false,
|
Some("quit") => self.state.lock_mut().running = false,
|
||||||
Some("connect") => {
|
Some("connect") => {
|
||||||
self.connect(cmdline)
|
self.connect(cmdline)
|
||||||
},
|
},
|
||||||
@ -74,98 +69,85 @@ impl<'a, S: Unpin> Sute<'a, S> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn handle_resize(&mut self, new_size: (u16,u16)) {
|
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> {
|
pub fn get_state(&self) -> SuteState {
|
||||||
self.state.clone()
|
self.state.get_cloned()
|
||||||
|
}
|
||||||
|
|
||||||
|
// We can make this more efficient by not cloning. But who cares?
|
||||||
|
pub fn signal(&self) -> impl Signal<Item=SuteState> {
|
||||||
|
self.state.signal_cloned()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn handle_key(&mut self, key: Key) {
|
pub fn handle_key(&mut self, key: Key) {
|
||||||
|
let mut state = self.state.lock_mut();
|
||||||
match key {
|
match key {
|
||||||
Key::Char('\n') => {
|
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);
|
self.run_cmd(cmd);
|
||||||
},
|
},
|
||||||
Key::Char(c) => {
|
Key::Char(c) => {
|
||||||
self.state.cmd_line.push(c);
|
state.cmd_line.push(c);
|
||||||
},
|
},
|
||||||
Key::Backspace => {
|
Key::Backspace => {
|
||||||
self.state.cmd_line.pop();
|
state.cmd_line.pop();
|
||||||
},
|
},
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, S: Signal<Item=(u16,u16)> + Unpin> Signal for Sute<'a, S> {
|
impl <'a, S: Unpin + Signal<Item=(u16,u16)>> Future for Sute<'a, S> {
|
||||||
type Item = SuteState<'a>;
|
type Output = ();
|
||||||
fn poll_change(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<Option<Self::Item>> {
|
fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
|
||||||
let mut dirty = false;
|
// 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) {
|
if let Poll::Ready(Some(size)) = Pin::new(&mut self.signal).poll_change(cx) {
|
||||||
dirty = true;
|
|
||||||
self.handle_resize(size);
|
self.handle_resize(size);
|
||||||
}
|
}
|
||||||
match ready!(Pin::new(&mut self.inputs).poll_next(cx)) {
|
match ready!(Pin::new(&mut self.inputs).poll_next(cx)) {
|
||||||
Some(key) => {
|
Some(key) => {
|
||||||
dirty = true;
|
|
||||||
self.handle_key(key);
|
self.handle_key(key);
|
||||||
},
|
},
|
||||||
// If the input closes stop the program
|
// If the input closes stop the program
|
||||||
None => {
|
None => {
|
||||||
self.state.running = false;
|
self.state.lock_mut().running = false;
|
||||||
return Poll::Ready(None);
|
return Poll::Ready(());
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
if dirty {
|
|
||||||
Poll::Ready(Some(self.get_state()))
|
|
||||||
} else {
|
|
||||||
Poll::Pending
|
Poll::Pending
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a, S> Future for Sute<'a, S> {
|
|
||||||
type Output = ();
|
|
||||||
fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
|
|
||||||
if self.state.running {
|
|
||||||
Poll::Pending
|
|
||||||
} else {
|
|
||||||
Poll::Ready(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
// TODO: `Signal` struct changes using this struct?
|
pub struct SuteState {
|
||||||
// TODO: If so, procmacro here?
|
|
||||||
enum SuteStateChange {
|
|
||||||
active_win(Window),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct SuteState<'a> {
|
|
||||||
pub active_win: Window,
|
pub active_win: Window,
|
||||||
pub size: (u16,u16),
|
pub size: (u16,u16),
|
||||||
pub running: bool,
|
pub running: bool,
|
||||||
pub tick: usize,
|
pub tick: usize,
|
||||||
pub server: Option<&'a str>,
|
pub server: Option<String>,
|
||||||
pub log: Arc<LogDrain<'a>>,
|
|
||||||
pub cmd_line: String,
|
pub cmd_line: String,
|
||||||
pub tick_c: char,
|
pub tick_c: char,
|
||||||
|
|
||||||
|
// TODO: Figure out how to put log lines here signaled
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> SuteState<'a> {
|
impl SuteState {
|
||||||
pub fn new(log: Arc<LogDrain<'a>>) -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
active_win: Window::Main,
|
active_win: Window::Main,
|
||||||
size: (80,20),
|
size: (80,20),
|
||||||
running: true,
|
running: true,
|
||||||
tick: 0,
|
tick: 0,
|
||||||
server: None,
|
server: None,
|
||||||
log: log,
|
|
||||||
cmd_line: String::new(),
|
cmd_line: String::new(),
|
||||||
tick_c: '|',
|
tick_c: '|',
|
||||||
}
|
}
|
||||||
|
19
src/main.rs
19
src/main.rs
@ -69,10 +69,11 @@ fn main() -> Result<(), io::Error> {
|
|||||||
|
|
||||||
let (rpc_future, mut api) = schema::bootstrap(log.clone(), stream);
|
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 app_state = app.get_state();
|
||||||
let mut stream = app.to_stream();
|
let mut ui_state = ui::UIState::new(app_state, drain);
|
||||||
|
let mut stream = app.signal().to_stream();
|
||||||
|
|
||||||
let ui_future = async move {
|
let ui_future = async move {
|
||||||
let stdout = io::stdout().into_raw_mode()?;
|
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.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 {
|
loop {
|
||||||
if let Some(mut state) = stream.next().await {
|
if let Some(mut state) = stream.next().await {
|
||||||
if !state.running {
|
if !state.running {
|
||||||
@ -102,7 +103,8 @@ fn main() -> Result<(), io::Error> {
|
|||||||
_ => '|',
|
_ => '|',
|
||||||
};
|
};
|
||||||
state.tick_c = tick_c;
|
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 {
|
} else {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -114,9 +116,10 @@ fn main() -> Result<(), io::Error> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
};
|
};
|
||||||
|
|
||||||
//lex.spawn(rpc_future).detach();
|
lex.spawn(rpc_future).detach();
|
||||||
let r: Result<(), io::Error> = smol::block_on(lex.run(ui_future));
|
let t: Task<Result<(), io::Error>> = lex.spawn(ui_future);
|
||||||
//smol::block_on(lex.run(Box::pin(app)));
|
t.detach();
|
||||||
|
smol::block_on(lex.run(app));
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
use tui::{
|
use tui::{
|
||||||
backend::Backend,
|
backend::Backend,
|
||||||
layout::{Layout, Direction, Constraint, Rect},
|
layout::{Layout, Direction, Constraint, Rect},
|
||||||
@ -5,9 +7,19 @@ use tui::{
|
|||||||
Frame,
|
Frame,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::app::SuteState;
|
use crate::app::{SuteState, LogDrain};
|
||||||
|
|
||||||
pub fn draw_ui<B: Backend>(f: &mut Frame<B>, app: &mut SuteState) {
|
pub struct UIState<'a> {
|
||||||
|
pub app_state: SuteState,
|
||||||
|
pub loglines: Arc<LogDrain<'a>>,
|
||||||
|
}
|
||||||
|
impl<'a> UIState<'a> {
|
||||||
|
pub fn new(app_state: SuteState, loglines: Arc<LogDrain<'a>>) -> Self {
|
||||||
|
Self { app_state, loglines }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn draw_ui<B: Backend>(f: &mut Frame<B>, state: &mut UIState) {
|
||||||
let outer_layout = Layout::default()
|
let outer_layout = Layout::default()
|
||||||
.direction(Direction::Vertical)
|
.direction(Direction::Vertical)
|
||||||
.constraints([
|
.constraints([
|
||||||
@ -19,32 +31,32 @@ pub fn draw_ui<B: Backend>(f: &mut Frame<B>, app: &mut SuteState) {
|
|||||||
.split(f.size());
|
.split(f.size());
|
||||||
|
|
||||||
|
|
||||||
draw_header(f, app, outer_layout[0]);
|
draw_header(f, state, outer_layout[0]);
|
||||||
draw_main(f, app, outer_layout[1]);
|
draw_main(f, state, outer_layout[1]);
|
||||||
draw_command_line(f, app, outer_layout[2]);
|
draw_command_line(f, state, outer_layout[2]);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw_header<B: Backend>(f: &mut Frame<B>, app: &mut SuteState, layout_chunk: Rect) {
|
fn draw_header<B: Backend>(f: &mut Frame<B>, state: &mut UIState, layout_chunk: Rect) {
|
||||||
f.render_widget(Block::default()
|
f.render_widget(Block::default()
|
||||||
.title("Status")
|
.title("Status")
|
||||||
.borders(Borders::ALL), layout_chunk);
|
.borders(Borders::ALL), layout_chunk);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw_main<B: Backend>(f: &mut Frame<B>, app: &mut SuteState, layout_chunk: Rect) {
|
fn draw_main<B: Backend>(f: &mut Frame<B>, state: &mut UIState, layout_chunk: Rect) {
|
||||||
// let chunk = Layout::default()
|
// let chunk = Layout::default()
|
||||||
// .direction(Direction::Horizontal)
|
// .direction(Direction::Horizontal)
|
||||||
// .constraints([Constraint::Percentage(20), Constraint::Percentage(80)].as_ref())
|
// .constraints([Constraint::Percentage(20), Constraint::Percentage(80)].as_ref())
|
||||||
// .split(layout_chunk);
|
// .split(layout_chunk);
|
||||||
|
|
||||||
draw_logs(f, app, layout_chunk)
|
draw_logs(f, state, layout_chunk)
|
||||||
}
|
}
|
||||||
fn draw_logs<B: Backend>(f: &mut Frame<B>, app: &mut SuteState, layout_chunk: Rect) {
|
fn draw_logs<B: Backend>(f: &mut Frame<B>, state: &mut UIState, layout_chunk: Rect) {
|
||||||
// TODO: Just use a signal.
|
// TODO: Just use a signal.
|
||||||
let v = app.log.get_inner().clone();
|
let v = state.loglines.get_inner().clone();
|
||||||
f.render_widget(Paragraph::new(v.into_iter().collect::<Vec<tui::text::Spans>>()), layout_chunk);
|
f.render_widget(Paragraph::new(v), layout_chunk);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw_command_line<B: Backend>(f: &mut Frame<B>, app: &mut SuteState, layout_chunk: Rect) {
|
fn draw_command_line<B: Backend>(f: &mut Frame<B>, state: &mut UIState, layout_chunk: Rect) {
|
||||||
let block = Block::default()
|
let block = Block::default()
|
||||||
.title("Command line")
|
.title("Command line")
|
||||||
.borders(Borders::ALL);
|
.borders(Borders::ALL);
|
||||||
@ -52,6 +64,6 @@ fn draw_command_line<B: Backend>(f: &mut Frame<B>, app: &mut SuteState, layout_c
|
|||||||
|
|
||||||
f.render_widget(block, layout_chunk);
|
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);
|
f.render_widget(Paragraph::new(&cmdline[..]), inner_rect);
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user