use std::sync::Arc;
use std::time::Duration;
use tokio::sync::{Mutex, Notify};
use crate::app_state::AppState;
use crate::cli::{LocalArgs, ViewArgs};
use crate::common::config::AlertConfig;
use crate::common::config_file::Settings;
use crate::ui::alerts::Alerter;
use crate::view::data_collection::{ReplayDriver, initial_replay_state};
use crate::view::{
data_collector::DataCollector, terminal_manager::TerminalManager, ui_loop::UiLoop,
};
fn build_alert_config(
settings: &Settings,
alert_temp: Option<u32>,
alert_util_low_mins: Option<u32>,
) -> AlertConfig {
settings
.alerts
.clone()
.with_cli_overrides(alert_temp, alert_util_low_mins)
}
pub async fn run_local_mode(args: &LocalArgs, settings: &Settings) {
let mut startup_profiler = crate::utils::StartupProfiler::new();
startup_profiler.checkpoint("Starting run_local_mode");
let mut initial_state = AppState::with_energy_config(&settings.energy);
initial_state.is_local_mode = true;
let alert_config = build_alert_config(settings, args.alert_temp, args.alert_util_low_mins);
initial_state.alerter = Alerter::new(alert_config);
initial_state.display_config = settings.display.clone();
let app_state = Arc::new(Mutex::new(initial_state));
startup_profiler.checkpoint("AppState initialized");
let data_notify = Arc::new(Notify::new());
let _terminal_manager = match TerminalManager::new() {
Ok(manager) => manager,
Err(e) => {
eprintln!("Failed to initialize terminal: {e}");
return;
}
};
startup_profiler.checkpoint("Terminal initialized");
let data_collector =
DataCollector::with_notify(Arc::clone(&app_state), Arc::clone(&data_notify));
let view_args = ViewArgs {
interval: args.interval,
alert_temp: args.alert_temp,
alert_util_low_mins: args.alert_util_low_mins,
..ViewArgs::empty()
};
tokio::spawn(async move {
data_collector.run_local_mode(view_args).await;
});
startup_profiler.checkpoint("Data collector spawned");
let mut ui_loop = match UiLoop::new(app_state, data_notify) {
Ok(ui_loop) => ui_loop,
Err(e) => {
eprintln!("Failed to initialize UI: {e}");
return;
}
};
startup_profiler.checkpoint("UI loop initialized");
startup_profiler.finish();
let view_args = ViewArgs {
interval: args.interval,
alert_temp: args.alert_temp,
alert_util_low_mins: args.alert_util_low_mins,
..ViewArgs::empty()
};
if let Err(e) = ui_loop.run(&view_args).await {
eprintln!("UI loop error: {e}");
}
}
pub async fn run_replay_mode(args: &ViewArgs, settings: &Settings) {
let replay_path = match args.replay.as_ref() {
Some(p) => p.clone(),
None => {
eprintln!("error: --replay requires a file path");
return;
}
};
let mut driver = match ReplayDriver::open(replay_path.clone()) {
Ok(d) => d,
Err(e) => {
eprintln!("error: {e}");
return;
}
};
let mut initial_state = AppState::with_energy_config(&settings.energy);
initial_state.is_local_mode = false;
initial_state.loading = false;
initial_state.alerter = Alerter::new(settings.alerts.clone());
initial_state.display_config = settings.display.clone();
initial_state.display_config = settings.display.clone();
initial_state.replay = Some(initial_replay_state(args.speed.max(0.05), args.replay_loop));
if let Some(start) = args.start.as_deref()
&& let Ok(d) = crate::record::replay::parse_timecode(start)
&& let Some(r) = initial_state.replay.as_mut()
{
r.pending_seek = Some(d);
}
let mut header_hosts = driver.total_hosts();
if header_hosts.len() > crate::record::replay::MAX_HEADER_HOSTS {
header_hosts.truncate(crate::record::replay::MAX_HEADER_HOSTS);
}
if !header_hosts.is_empty() {
let mut tabs = vec![
"All".to_string(),
crate::ui::tabs::USERS_TAB_NAME.to_string(),
crate::ui::tabs::TOPOLOGY_TAB_NAME.to_string(),
];
tabs.extend(header_hosts);
initial_state.tabs = tabs;
}
let app_state = Arc::new(Mutex::new(initial_state));
let data_notify = Arc::new(Notify::new());
let _terminal_manager = match TerminalManager::new() {
Ok(manager) => manager,
Err(e) => {
eprintln!("Failed to initialize terminal: {e}");
return;
}
};
let state_for_driver = Arc::clone(&app_state);
let notify_for_driver = Arc::clone(&data_notify);
let driver_handle = tokio::spawn(async move {
loop {
if let Err(e) = driver.tick(Arc::clone(&state_for_driver)).await {
let mut state = state_for_driver.lock().await;
let _ = state.notifications.error(format!("replay: {e}"));
break;
}
notify_for_driver.notify_one();
tokio::time::sleep(Duration::from_millis(50)).await;
}
});
let mut ui_loop = match UiLoop::new(Arc::clone(&app_state), Arc::clone(&data_notify)) {
Ok(ui_loop) => ui_loop,
Err(e) => {
eprintln!("Failed to initialize UI: {e}");
driver_handle.abort();
return;
}
};
if let Err(e) = ui_loop.run(args).await {
eprintln!("UI loop error: {e}");
}
driver_handle.abort();
}
pub async fn run_ssh_mode(args: &ViewArgs, settings: &Settings) {
use std::time::Duration;
use crate::network::ssh_target::{parse_hostfile, parse_ssh_arg};
use crate::network::ssh_transport::{SshFallbackPolicy, StrictHostKey};
use crate::view::data_collection::{SshStrategy, SshStrategyConfig};
let mut targets = Vec::new();
if let Some(raw) = args.ssh.as_deref() {
match parse_ssh_arg(raw) {
Ok(mut t) => targets.append(&mut t),
Err(e) => {
eprintln!("error: {e}");
return;
}
}
}
if let Some(path) = args.ssh_hostfile.as_deref() {
match parse_hostfile(path) {
Ok(mut t) => targets.append(&mut t),
Err(e) => {
eprintln!("error: {e}");
return;
}
}
}
if targets.is_empty() {
eprintln!("error: --ssh and --ssh-hostfile produced no SSH targets");
return;
}
let strict_host_key = match StrictHostKey::from_cli(&args.ssh_strict_host_key) {
Ok(p) => p,
Err(e) => {
eprintln!("error: {e}");
return;
}
};
let fallback_policy = match SshFallbackPolicy::from_cli(args.ssh_fallback.as_deref()) {
Ok(p) => p,
Err(e) => {
eprintln!("error: {e}");
return;
}
};
let strategy_config = SshStrategyConfig {
targets,
explicit_key: args.ssh_key.clone(),
strict_host_key,
known_hosts: args.ssh_known_hosts.clone(),
connect_timeout: Duration::from_secs(args.ssh_timeout_secs.max(1)),
fallback_policy,
concurrency: args.ssh_concurrency.max(1),
};
let strategy = Arc::new(SshStrategy::new(strategy_config));
let mut initial_state = AppState::with_energy_config(&settings.energy);
initial_state.is_local_mode = false;
let alert_config = build_alert_config(settings, args.alert_temp, args.alert_util_low_mins);
initial_state.alerter = Alerter::new(alert_config);
initial_state.display_config = settings.display.clone();
let app_state = Arc::new(Mutex::new(initial_state));
let data_notify = Arc::new(Notify::new());
let _terminal_manager = match TerminalManager::new() {
Ok(m) => m,
Err(e) => {
eprintln!("Failed to initialize terminal: {e}");
return;
}
};
let data_collector =
DataCollector::with_notify(Arc::clone(&app_state), Arc::clone(&data_notify));
let args_clone = args.clone();
let strategy_clone = Arc::clone(&strategy);
tokio::spawn(async move {
data_collector
.run_ssh_mode(args_clone, strategy_clone)
.await;
});
let mut ui_loop = match UiLoop::new(app_state, data_notify) {
Ok(u) => u,
Err(e) => {
eprintln!("Failed to initialize UI: {e}");
return;
}
};
if let Err(e) = ui_loop.run(args).await {
eprintln!("UI loop error: {e}");
}
}
pub async fn run_view_mode(args: &ViewArgs, settings: &Settings) {
let mut initial_state = AppState::with_energy_config(&settings.energy);
initial_state.is_local_mode = false;
let alert_config = build_alert_config(settings, args.alert_temp, args.alert_util_low_mins);
initial_state.alerter = Alerter::new(alert_config);
initial_state.display_config = settings.display.clone();
initial_state.display_config = settings.display.clone();
let app_state = Arc::new(Mutex::new(initial_state));
let data_notify = Arc::new(Notify::new());
let _terminal_manager = match TerminalManager::new() {
Ok(manager) => manager,
Err(e) => {
eprintln!("Failed to initialize terminal: {e}");
return;
}
};
let data_collector =
DataCollector::with_notify(Arc::clone(&app_state), Arc::clone(&data_notify));
let args_clone = args.clone();
tokio::spawn(async move {
let hosts = args_clone.hosts.clone().unwrap_or_default();
let hostfile = args_clone.hostfile.clone();
data_collector
.run_remote_mode(args_clone, hosts, hostfile)
.await;
});
let mut ui_loop = match UiLoop::new(app_state, data_notify) {
Ok(ui_loop) => ui_loop,
Err(e) => {
eprintln!("Failed to initialize UI: {e}");
return;
}
};
if let Err(e) = ui_loop.run(args).await {
eprintln!("UI loop error: {e}");
}
}