watcher/node/src/watcher/watcher.rs

327 lines
8.9 KiB
Rust

use crate::{
bus::{SystemEvent, publish_system_event, subscribe_system_event}, cli::cli, node::{Node, node::NodeCommand}, watcher::WatcherMode
};
use shared::print_error_chain;
use crossterm::event::{Event, EventStream, KeyCode, KeyEventKind, MouseButton, MouseEventKind};
use futures::StreamExt;
use memory_stats::memory_stats;
use std::io::{self, Write};
use tokio::{
select,
time::{Duration, interval},
};
use vlogger::*;
use super::{ WatcherBuilder, WatcherCommand };
use crate::bus::subscribe_watcher_event;
use crate::log;
use cli_renderer::{
Renderer,
InputMode,
RenderCommand
};
#[allow(dead_code)]
pub struct Watcher {
cmd_buffer: String,
cmd_history: Vec<String>,
history_index: usize,
event_stream: crossterm::event::EventStream,
mode: WatcherMode,
pub renderer: Renderer,
node: Node,
}
impl Watcher {
pub fn new(
cmd_buffer: String,
cmd_history: Vec<String>,
history_index: usize,
renderer: Renderer,
node: Node,
) -> Self {
Self {
cmd_buffer,
cmd_history,
history_index,
renderer,
node,
mode: WatcherMode::Input,
event_stream: EventStream::new(),
}
}
fn init(&self) -> io::Result<()>{
crossterm::execute!(
std::io::stdout(),
crossterm::event::EnableBracketedPaste,
crossterm::event::EnableFocusChange,
crossterm::event::EnableMouseCapture,
)
}
async fn shutdown(&mut self) -> io::Result<()> {
ratatui::restore();
crossterm::execute!(
std::io::stdout(),
crossterm::event::DisableBracketedPaste,
crossterm::event::DisableFocusChange,
crossterm::event::DisableMouseCapture
)
}
pub async fn run(&mut self) -> std::io::Result<()> {
let mut ui_rx = subscribe_watcher_event();
let mut render_interval = interval(Duration::from_millis(32));
let mut terminal = ratatui::init();
let mut system_rx = subscribe_system_event();
self.init()?;
loop {
select! {
poll_res = self.poll() => {
match poll_res {
Ok(event) => {
self.renderer.set_area(terminal.get_frame().area());
match self.handle_event(event).await {
Ok(ret) => if !ret { self.exit(); break }
Err(e) => log(msg!(ERROR, "{}", e)),
}
}
Err(()) => { log(msg!(ERROR, "Failed to read from Stream")) }
}
}
ui_event = ui_rx.recv() => {
match ui_event {
Ok(cmd) => {
self.renderer.set_area(terminal.get_frame().area());
self.command(cmd);
},
Err(e) => {
log(msg!(ERROR, "{}", e))
}
}
}
event_res = system_rx.recv() => {
if let Ok(event) = event_res {
match event {
SystemEvent::Shutdown => {
break ;
}
_ => {}
}
}
}
_ = render_interval.tick() => {
self.renderer.set_area(terminal.get_frame().area());
terminal.draw(|frame| self.renderer.draw(frame))?;
}
}
}
self.shutdown().await
}
pub fn build() -> WatcherBuilder {
WatcherBuilder::new()
}
fn exit(&mut self) {
log(msg!(DEBUG, "Watcher Exit"));
}
fn echo(&mut self, s: Vec<String>) {
let mut str = s.join(" ");
str.push_str("\n");
self.renderer.apply(RenderCommand::StringToPaneId {
str,
pane: cli_renderer::RenderTarget::CliOutput,
});
}
async fn invalid_command(&mut self, str: String) {
self.renderer.apply(RenderCommand::StringToPaneId {
str,
pane: cli_renderer::RenderTarget::CliOutput,
});
}
fn set_mode(&mut self, mode: WatcherMode) {
match &mode {
WatcherMode::Input => {}
WatcherMode::Select{content, title, ..} => {
let rd_cmd = RenderCommand::SetMode(InputMode::PopUp(content.clone(), title.clone(), 0));
self.renderer.apply(rd_cmd);
}
}
self.mode = mode;
}
pub async fn command(&mut self, cmd: WatcherCommand) {
match cmd {
WatcherCommand::NodeResponse(resp) => log(resp),
WatcherCommand::Node(n) => self.node.command(n).await,
WatcherCommand::Render(p) => self.renderer.apply(p),
WatcherCommand::Echo(s) => self.echo(s),
WatcherCommand::Print(s) => log(s),
WatcherCommand::InvalidCommand(str) => self.invalid_command(str).await,
WatcherCommand::Exit => self.exit(),
WatcherCommand::SetMode(mode) => self.set_mode(mode),
}
}
async fn handle_enter(&mut self) {
match &self.mode {
WatcherMode::Input => {
if !self.cmd_buffer.is_empty() {
let exec_event = cli(&self.cmd_buffer);
self.command(exec_event).await;
self.cmd_buffer.clear();
self.renderer.handle_enter()
}
}
WatcherMode::Select { content, callback, index, .. } => {
match &&**callback {
&WatcherCommand::Node(nd_cmd) => {
match nd_cmd {
NodeCommand::DisplayBlockByKey(_) => {
let key = (*content)[*index].clone().to_string();
log(msg!(DEBUG, "KEY IN ENTER: {key}"));
self.node.command(NodeCommand::DisplayBlockByKey(key));
}
_ => {log(msg!(DEBUG, "TODO: Implement callback for {:?}", nd_cmd))}
}
}
_ => {log(msg!(DEBUG, "TODO: Implement callback for {:?}", *callback))}
}
self.mode = WatcherMode::Input;
let rd_cmd = RenderCommand::SetMode(InputMode::Input);
self.renderer.apply(rd_cmd);
}
}
}
fn handle_arrow_key(&mut self, key: KeyCode) {
match key {
KeyCode::Up => {
match &mut self.mode {
&mut WatcherMode::Select { ref mut index, .. } => {
*index = index.saturating_sub(1);
}
_ => {}
}
}
KeyCode::Down => {
match &mut self.mode {
&mut WatcherMode::Select { ref mut index, ref content, ..} => {
if *index < content.len().saturating_sub(1) {
*index = index.saturating_add(1);
}
}
_ => {}
}
}
_ => {}
}
}
pub async fn log_memory() {
tokio::spawn(async move {
let id = format!("{}_{}", current_timestamp(), std::process::id());
let id = id.replace(":", "_");
let mut path = std::path::PathBuf::new();
path.push("proc");
path.push(id);
let mut mem_map = std::fs::OpenOptions::new()
.create(true)
.write(true)
.truncate(true)
.open(path)
.unwrap();
loop {
let _ = tokio::time::sleep(Duration::from_secs(10)).await;
if let Some(usage) = memory_stats() {
let current = current_timestamp();
let _ = mem_map.write_all(
msg!(
INFO,
"{}: Physical memory usage: {} MB",
current,
usage.physical_mem / 1024 / 1024
)
.as_bytes(),
);
let _ = mem_map.write_all(
msg!(
INFO,
"{}: Virtual memory usage: {} MB",
current,
usage.virtual_mem / 1024 / 1024
)
.as_bytes(),
);
}
}
});
}
pub async fn handle_event(&mut self, event: Event) -> io::Result<bool> {
match event {
Event::Mouse(event) => match event.kind {
MouseEventKind::ScrollUp => {
self.renderer.handle_scroll_up();
}
MouseEventKind::ScrollDown => {
self.renderer.handle_scroll_down();
}
MouseEventKind::Down(b) => match b {
MouseButton::Left => {
let rects = self.renderer.rects();
self
.renderer
.handle_mouse_click_left(event.column, event.row, rects);
}
_ => {}
},
_ => {}
},
Event::Key(k) if k.kind == KeyEventKind::Press => match k.code {
KeyCode::Esc => publish_system_event(SystemEvent::Shutdown),
KeyCode::Char(c) => {
self.cmd_buffer.push(c);
self.renderer.handle_char_input(c)
}
KeyCode::Backspace => {
self.cmd_buffer.pop();
self.renderer.handle_backspace()
}
KeyCode::Enter => {
self.handle_enter().await;
}
KeyCode::Up | KeyCode::Down | KeyCode::Left | KeyCode::Right => {
self.handle_arrow_key(k.code);
self.renderer.handle_arrow_key(k.code)
}
_ => {}
},
Event::Paste(text) => {
log(msg!(DEBUG, "Received pasted text: {text}"));
self.renderer.render_string_to_focused(text);
}
_ => {}
}
Ok(true)
}
pub async fn poll(&mut self) -> Result<Event, ()> {
match self.event_stream.next().await {
Some(Ok(event)) => Ok(event),
Some(Err(e)) => Err(print_error_chain(&e.into())),
None => Err(()),
}
}
}