327 lines
8.9 KiB
Rust
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(()),
|
|
}
|
|
}
|
|
}
|