mirror of
https://github.com/Noratrieb/service-manager.git
synced 2026-01-14 16:35:05 +01:00
improve perf
This commit is contained in:
parent
8de36860af
commit
817a14c3bc
4 changed files with 84 additions and 49 deletions
15
config.toml
15
config.toml
|
|
@ -19,4 +19,17 @@ command = "cat fkasdjfölashjdflksd"
|
||||||
command = "touch uwu.txt"
|
command = "touch uwu.txt"
|
||||||
|
|
||||||
[side-effect-remove]
|
[side-effect-remove]
|
||||||
command = "rm uwu.txt"
|
command = "rm uwu.txt"
|
||||||
|
|
||||||
|
[compile]
|
||||||
|
command = "cargo check"
|
||||||
|
|
||||||
|
[loop]
|
||||||
|
command = """
|
||||||
|
counter=0
|
||||||
|
|
||||||
|
while true; do
|
||||||
|
echo $counter
|
||||||
|
counter=$((counter+1))
|
||||||
|
done
|
||||||
|
"""
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::model::config::Config;
|
use crate::model::config::Config;
|
||||||
use crate::model::{AppState, Service, ServiceStatus, SmError, SmResult};
|
use crate::model::{AppState, Service, ServiceStatus, SmError, SmResult, StdIoStream};
|
||||||
use crate::{view, App};
|
use crate::{view, App};
|
||||||
use crossterm::event;
|
use crossterm::event;
|
||||||
use crossterm::event::{Event, KeyCode};
|
use crossterm::event::{Event, KeyCode};
|
||||||
|
|
@ -9,13 +9,14 @@ use std::io::{ErrorKind, Read, Write};
|
||||||
use std::process::{Child, Command, Stdio};
|
use std::process::{Child, Command, Stdio};
|
||||||
use std::sync::mpsc::TryRecvError;
|
use std::sync::mpsc::TryRecvError;
|
||||||
use std::sync::{mpsc, Mutex};
|
use std::sync::{mpsc, Mutex};
|
||||||
|
use std::time::Duration;
|
||||||
use tui::backend::Backend;
|
use tui::backend::Backend;
|
||||||
use tui::widgets::TableState;
|
use tui::widgets::TableState;
|
||||||
use tui::Terminal;
|
use tui::Terminal;
|
||||||
|
|
||||||
const STDOUT_SEND_BUF_SIZE: usize = 512;
|
const STDIO_SEND_BUF_SIZE: usize = 512;
|
||||||
|
|
||||||
pub type StdoutSendBuf = ([u8; STDOUT_SEND_BUF_SIZE], usize);
|
pub type StdioSendBuf = ([u8; STDIO_SEND_BUF_SIZE], usize);
|
||||||
|
|
||||||
pub fn run_app<B: Backend>(terminal: &mut Terminal<B>, mut app: App) -> SmResult {
|
pub fn run_app<B: Backend>(terminal: &mut Terminal<B>, mut app: App) -> SmResult {
|
||||||
loop {
|
loop {
|
||||||
|
|
@ -23,18 +24,20 @@ pub fn run_app<B: Backend>(terminal: &mut Terminal<B>, mut app: App) -> SmResult
|
||||||
|
|
||||||
app.recv_stdouts();
|
app.recv_stdouts();
|
||||||
|
|
||||||
if let Event::Key(key) = event::read()? {
|
if event::poll(Duration::from_millis(10))? {
|
||||||
match key.code {
|
if let Event::Key(key) = event::read()? {
|
||||||
KeyCode::Char('q') => match app.selected {
|
match key.code {
|
||||||
Some(_) => app.leave_service(),
|
KeyCode::Char('q') => match app.selected {
|
||||||
None => break,
|
Some(_) => app.leave_service(),
|
||||||
},
|
None => break,
|
||||||
KeyCode::Char('r') => app.run_service()?,
|
},
|
||||||
KeyCode::Down => app.next(),
|
KeyCode::Char('r') => app.run_service()?,
|
||||||
KeyCode::Up => app.previous(),
|
KeyCode::Down => app.next(),
|
||||||
KeyCode::Enter => app.select_service(),
|
KeyCode::Up => app.previous(),
|
||||||
KeyCode::Esc => app.leave_service(),
|
KeyCode::Enter => app.select_service(),
|
||||||
_ => {}
|
KeyCode::Esc => app.leave_service(),
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -66,9 +69,11 @@ impl App {
|
||||||
.or_else(|_| std::env::current_dir())?,
|
.or_else(|_| std::env::current_dir())?,
|
||||||
env: service.env.unwrap_or_else(HashMap::new),
|
env: service.env.unwrap_or_else(HashMap::new),
|
||||||
status: Mutex::new(ServiceStatus::NotStarted),
|
status: Mutex::new(ServiceStatus::NotStarted),
|
||||||
stdout_buf: Vec::new(),
|
std_io_buf: Vec::new(),
|
||||||
stdout_recv,
|
stdout: StdIoStream {
|
||||||
stdout_send: Mutex::new(Some(stdout_send)),
|
recv: stdout_recv,
|
||||||
|
send: stdout_send,
|
||||||
|
},
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.collect::<io::Result<_>>()?,
|
.collect::<io::Result<_>>()?,
|
||||||
|
|
@ -84,8 +89,12 @@ impl App {
|
||||||
|
|
||||||
fn recv_stdouts(&mut self) {
|
fn recv_stdouts(&mut self) {
|
||||||
for service in self.table.services.iter_mut() {
|
for service in self.table.services.iter_mut() {
|
||||||
while let Ok((buf, n)) = service.stdout_recv.try_recv() {
|
while let Ok((buf, n)) = service.stdout.recv.try_recv() {
|
||||||
service.stdout_buf.extend_from_slice(&buf[0..n]);
|
service.std_io_buf.extend(&buf[0..n]);
|
||||||
|
|
||||||
|
if service.std_io_buf.len() > 2_000_000 {
|
||||||
|
service.std_io_buf.clear(); // todo don't
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -131,13 +140,20 @@ impl App {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_service(&mut self) -> SmResult {
|
fn run_service(&mut self) -> SmResult {
|
||||||
if let Some(selected) = self.selected {
|
let index = self.selected.or_else(|| self.table.table_state.selected());
|
||||||
self.start_service(selected)
|
|
||||||
} else if let Some(selected) = self.table.table_state.selected() {
|
if let Some(index) = index {
|
||||||
self.start_service(selected)
|
let status = {
|
||||||
} else {
|
let service = &mut self.table.services[index];
|
||||||
Ok(())
|
*service.status.lock()?
|
||||||
|
};
|
||||||
|
|
||||||
|
if status != ServiceStatus::Running {
|
||||||
|
self.start_service(index)?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn start_service(&mut self, service: usize) -> SmResult {
|
fn start_service(&mut self, service: usize) -> SmResult {
|
||||||
|
|
@ -145,12 +161,6 @@ impl App {
|
||||||
|
|
||||||
*service.status.lock()? = ServiceStatus::Running;
|
*service.status.lock()? = ServiceStatus::Running;
|
||||||
|
|
||||||
let stdout_send = service
|
|
||||||
.stdout_send
|
|
||||||
.lock()?
|
|
||||||
.take()
|
|
||||||
.ok_or(SmError::StdioStolen)?;
|
|
||||||
|
|
||||||
let mut cmd = Command::new("sh");
|
let mut cmd = Command::new("sh");
|
||||||
|
|
||||||
cmd.args(["-c", &service.command]);
|
cmd.args(["-c", &service.command]);
|
||||||
|
|
@ -160,9 +170,11 @@ impl App {
|
||||||
cmd.stderr(Stdio::piped());
|
cmd.stderr(Stdio::piped());
|
||||||
cmd.stdin(Stdio::piped());
|
cmd.stdin(Stdio::piped());
|
||||||
|
|
||||||
|
let stdout_send = service.stdout.send.clone();
|
||||||
|
|
||||||
let child = match cmd.spawn() {
|
let child = match cmd.spawn() {
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
let mut buf = [0; STDOUT_SEND_BUF_SIZE];
|
let mut buf = [0; STDIO_SEND_BUF_SIZE];
|
||||||
|
|
||||||
let bytes = err.to_string();
|
let bytes = err.to_string();
|
||||||
|
|
||||||
|
|
@ -177,13 +189,15 @@ impl App {
|
||||||
Ok(child) => child,
|
Ok(child) => child,
|
||||||
};
|
};
|
||||||
|
|
||||||
let (tx, rx) = mpsc::channel();
|
let (terminate_send, terminate_recv) = mpsc::channel();
|
||||||
|
|
||||||
self.thread_terminates.push(tx);
|
self.thread_terminates.push(terminate_send);
|
||||||
|
|
||||||
std::thread::spawn(move || match child_process_thread(child, stdout_send, rx) {
|
std::thread::spawn(move || {
|
||||||
Ok(_) => {}
|
match child_process_thread(child, stdout_send, terminate_recv) {
|
||||||
Err(e) => std::fs::write("error.txt", e.to_string()).unwrap(),
|
Ok(_) => {}
|
||||||
|
Err(e) => std::fs::write("error.txt", e.to_string()).unwrap(),
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
@ -191,7 +205,7 @@ impl App {
|
||||||
}
|
}
|
||||||
fn child_process_thread(
|
fn child_process_thread(
|
||||||
child: Child,
|
child: Child,
|
||||||
stdout_send: mpsc::Sender<StdoutSendBuf>,
|
stdout_send: mpsc::Sender<StdioSendBuf>,
|
||||||
terminate_channel: mpsc::Receiver<()>,
|
terminate_channel: mpsc::Receiver<()>,
|
||||||
) -> io::Result<()> {
|
) -> io::Result<()> {
|
||||||
let mut child = child;
|
let mut child = child;
|
||||||
|
|
@ -203,7 +217,7 @@ fn child_process_thread(
|
||||||
Err(TryRecvError::Empty) => {}
|
Err(TryRecvError::Empty) => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut stdout_buf = [0; STDOUT_SEND_BUF_SIZE];
|
let mut stdout_buf = [0; STDIO_SEND_BUF_SIZE];
|
||||||
|
|
||||||
match stdout.read(&mut stdout_buf) {
|
match stdout.read(&mut stdout_buf) {
|
||||||
Ok(0) => continue,
|
Ok(0) => continue,
|
||||||
|
|
|
||||||
15
src/model.rs
15
src/model.rs
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::controller::StdoutSendBuf;
|
use crate::controller::StdioSendBuf;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::sync::{mpsc, Mutex};
|
use std::sync::{mpsc, Mutex};
|
||||||
|
|
@ -26,12 +26,17 @@ pub struct Service {
|
||||||
pub workdir: PathBuf,
|
pub workdir: PathBuf,
|
||||||
pub env: HashMap<String, String>,
|
pub env: HashMap<String, String>,
|
||||||
pub status: Mutex<ServiceStatus>,
|
pub status: Mutex<ServiceStatus>,
|
||||||
pub stdout_buf: Vec<u8>,
|
pub std_io_buf: Vec<u8>,
|
||||||
pub stdout_recv: mpsc::Receiver<StdoutSendBuf>,
|
pub stdout: StdIoStream,
|
||||||
pub stdout_send: Mutex<Option<mpsc::Sender<StdoutSendBuf>>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
pub struct StdIoStream {
|
||||||
|
pub recv: mpsc::Receiver<StdioSendBuf>,
|
||||||
|
pub send: mpsc::Sender<StdioSendBuf>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub enum ServiceStatus {
|
pub enum ServiceStatus {
|
||||||
NotStarted,
|
NotStarted,
|
||||||
|
|
@ -65,7 +70,6 @@ mod error {
|
||||||
Io(io::Error),
|
Io(io::Error),
|
||||||
FailedToStartChild(io::Error),
|
FailedToStartChild(io::Error),
|
||||||
MutexPoisoned,
|
MutexPoisoned,
|
||||||
StdioStolen,
|
|
||||||
FailedToSendStdio,
|
FailedToSendStdio,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -86,7 +90,6 @@ mod error {
|
||||||
match self {
|
match self {
|
||||||
Self::Io(e) => Display::fmt(e, f),
|
Self::Io(e) => Display::fmt(e, f),
|
||||||
SmError::MutexPoisoned => f.write_str("Mutex was poisoned. This is a bug."),
|
SmError::MutexPoisoned => f.write_str("Mutex was poisoned. This is a bug."),
|
||||||
SmError::StdioStolen => f.write_str("Stdio was stolen. This is a bug."),
|
|
||||||
SmError::FailedToStartChild(e) => write!(f, "Failed to start child process: {}", e),
|
SmError::FailedToStartChild(e) => write!(f, "Failed to start child process: {}", e),
|
||||||
SmError::FailedToSendStdio => {
|
SmError::FailedToSendStdio => {
|
||||||
f.write_str("Failed to send stdio to display thread. This is a bug.")
|
f.write_str("Failed to send stdio to display thread. This is a bug.")
|
||||||
|
|
|
||||||
|
|
@ -41,9 +41,14 @@ fn render_full_view<B: Backend>(f: &mut Frame<B>, state: &mut AppState, index: u
|
||||||
.borders(Borders::ALL)
|
.borders(Borders::ALL)
|
||||||
.title("service".as_ref());
|
.title("service".as_ref());
|
||||||
|
|
||||||
let stdout = String::from_utf8_lossy(&service.stdout_buf);
|
let len = service.std_io_buf.len();
|
||||||
|
let stdout = if len > 10000 {
|
||||||
|
String::from_utf8_lossy(&service.std_io_buf[len - 10000..len])
|
||||||
|
} else {
|
||||||
|
String::from_utf8_lossy(&service.std_io_buf)
|
||||||
|
};
|
||||||
|
|
||||||
let paragraph = Paragraph::new(stdout.as_ref()).block(block);
|
let paragraph = Paragraph::new(stdout.as_ref()).block(block.clone());
|
||||||
|
|
||||||
f.render_widget(paragraph, area)
|
f.render_widget(paragraph, area)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue