feat: reworked CLI interface with subcommands

This commit is contained in:
əlemi 2022-11-05 03:28:35 +01:00
parent 4345a9e9b9
commit a6bc0da6fa
Signed by: alemi
GPG key ID: A4895B84D311642C

View file

@ -9,8 +9,8 @@ use tracing::{info, error};
use tracing_subscriber::filter::filter_fn; use tracing_subscriber::filter::filter_fn;
use eframe::egui::Context; use eframe::egui::Context;
use clap::Parser; use clap::{Parser, Subcommand};
use tokio::sync::watch; use tokio::sync::{watch, mpsc};
use sea_orm::Database; use sea_orm::Database;
use worker::visualizer::AppState; use worker::visualizer::AppState;
@ -25,16 +25,8 @@ use gui::{
#[derive(Parser, Debug)] #[derive(Parser, Debug)]
#[command(author, version, about)] #[command(author, version, about)]
struct CliArgs { struct CliArgs {
/// Connection string for database to use #[clap(subcommand)]
db: String, mode: Mode,
/// Run background worker
#[arg(long, default_value_t = false)]
worker: bool,
/// Run user interface
#[arg(long, default_value_t = false)]
gui: bool,
/// Check interval for background worker /// Check interval for background worker
#[arg(short, long, default_value_t = 10)] #[arg(short, long, default_value_t = 10)]
@ -49,6 +41,19 @@ struct CliArgs {
log_size: u64, log_size: u64,
} }
#[derive(Subcommand, Clone, Debug)]
enum Mode {
/// Run as background service fetching sources from db
Worker {
/// Connection string for database to use
db_uri: String,
},
/// Run as foreground user interface displaying collected data
GUI {
},
}
// When compiling for web: // When compiling for web:
#[cfg(target_arch = "wasm32")] #[cfg(target_arch = "wasm32")]
fn setup_tracing(_layer: InternalLoggerLayer) { fn setup_tracing(_layer: InternalLoggerLayer) {
@ -83,125 +88,148 @@ fn main() {
setup_tracing(logger.layer()); setup_tracing(logger.layer());
let state = match AppState::new( match args.mode {
width_rx, Mode::Worker { db_uri } => {
args.interval as i64, let run_rx_clone = run_rx.clone();
args.cache_time as i64, let worker = std::thread::spawn(move || {
) { tokio::runtime::Builder::new_current_thread()
Ok(s) => s, .enable_all()
Err(e) => { .build()
error!(target: "launcher", "Could not create application state: {:?}", e); .unwrap()
return; .block_on(async {
} let db = match Database::connect(db_uri.clone()).await {
}; Ok(v) => v,
Err(e) => {
let view = state.view(); error!(target: "launcher", "Could not connect to db: {:?}", e);
let run_rx_clone = run_rx.clone(); return;
let db_uri = args.db.clone(); }
};
let worker = std::thread::spawn(move || { info!(target: "launcher", "Connected to '{}'", db_uri);
tokio::runtime::Builder::new_current_thread()
.enable_all() let mut jobs = vec![];
.build()
.unwrap() jobs.push(
.block_on(async { tokio::spawn(logger.worker(run_rx_clone.clone()))
let db = match Database::connect(db_uri.clone()).await { );
Ok(v) => v,
Err(e) => { jobs.push(
error!(target: "launcher", "Could not connect to db: {:?}", e); tokio::spawn(
return; surveyor_loop(
} db.clone(),
}; args.interval as i64,
info!(target: "launcher", "Connected to '{}'", db_uri); args.cache_time as i64,
run_rx_clone.clone(),
let mut jobs = vec![]; )
)
let run_rx_clone_clone = run_rx_clone.clone(); );
jobs.push( for (i, job) in jobs.into_iter().enumerate() {
tokio::spawn(async move { if let Err(e) = job.await {
while *run_rx_clone_clone.borrow() { error!(target: "launcher", "Could not join task #{}: {:?}", i, e);
if let Some(ctx) = &*ctx_rx.borrow() {
ctx.request_repaint();
} }
tokio::time::sleep(std::time::Duration::from_secs(args.interval)).await;
} }
info!(target: "launcher", "Stopping background worker");
}) })
); });
jobs.push( worker.join().unwrap();
tokio::spawn(logger.worker(run_rx_clone.clone())) },
); Mode::GUI { } => {
let (uri_tx, uri_rx) = mpsc::channel(10);
let state = match AppState::new(
width_rx,
uri_rx,
args.interval as i64,
args.cache_time as i64,
) {
Ok(s) => s,
Err(e) => {
error!(target: "launcher", "Could not create application state: {:?}", e);
return;
}
};
let view = state.view();
let run_rx_clone = run_rx.clone();
let worker = std::thread::spawn(move || {
tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
.unwrap()
.block_on(async {
let mut jobs = vec![];
if args.worker { let run_rx_clone_clone = run_rx_clone.clone();
jobs.push(
tokio::spawn( jobs.push(
surveyor_loop( tokio::spawn(async move {
db.clone(), while *run_rx_clone_clone.borrow() {
if let Some(ctx) = &*ctx_rx.borrow() {
ctx.request_repaint();
}
tokio::time::sleep(std::time::Duration::from_secs(args.interval)).await;
}
})
);
jobs.push(
tokio::spawn(logger.worker(run_rx_clone.clone()))
);
jobs.push(
tokio::spawn(
state.worker(run_rx_clone.clone())
)
);
for (i, job) in jobs.into_iter().enumerate() {
if let Err(e) = job.await {
error!(target: "launcher", "Could not join task #{}: {:?}", i, e);
}
}
info!(target: "launcher", "Stopping background worker");
})
});
let native_options = eframe::NativeOptions::default();
info!(target: "launcher", "Starting native GUI");
eframe::run_native(
// TODO replace this with a loop that ends so we can cleanly exit the background worker
"dashboard",
native_options,
Box::new(
move |cc| {
if let Err(_e) = ctx_tx.send(Some(cc.egui_ctx.clone())) {
error!(target: "launcher", "Could not share reference to egui context (won't be able to periodically refresh window)");
};
Box::new(
App::new(
cc,
uri_tx,
args.interval as i64, args.interval as i64,
args.cache_time as i64, view,
run_rx_clone.clone(), width_tx,
logger_view,
) )
) )
);
}
if args.gui {
jobs.push(
tokio::spawn(
state.worker(db, run_rx_clone.clone())
)
);
}
for (i, job) in jobs.into_iter().enumerate() {
if let Err(e) = job.await {
error!(target: "launcher", "Could not join task #{}: {:?}", i, e);
} }
} ),
);
info!(target: "launcher", "Stopping background worker"); info!(target: "launcher", "Stopping native GUI");
})
});
if args.gui { if let Err(e) = run_tx.send(false) {
let native_options = eframe::NativeOptions::default(); error!(target: "launcher", "Error signaling end to workers: {:?}", e);
}
info!(target: "launcher", "Starting native GUI"); if let Err(e) = worker.join() {
error!(target: "launcher", "Error joining background thread : {:?}", e);
let db_name = args.db.clone().split('/').last().unwrap_or("").to_string(); }
eframe::run_native(
// TODO replace this with a loop that ends so we can cleanly exit the background worker
"dashboard",
native_options,
Box::new(
move |cc| {
if let Err(_e) = ctx_tx.send(Some(cc.egui_ctx.clone())) {
error!(target: "launcher", "Could not share reference to egui context (won't be able to periodically refresh window)");
};
Box::new(
App::new(
cc,
db_name,
args.interval as i64,
view,
width_tx,
logger_view,
)
)
}
),
);
info!(target: "launcher", "Stopping native GUI");
if let Err(e) = run_tx.send(false) {
error!(target: "launcher", "Error signaling end to workers: {:?}", e);
} }
} }
if let Err(e) = worker.join() {
error!(target: "launcher", "Error joining background thread : {:?}", e);
}
} }