use std::io::{self, Stdout};
use std::sync::Arc;
use crossterm::event::{DisableMouseCapture, EnableMouseCapture};
use crossterm::execute;
use crossterm::terminal::{
disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen,
};
use ratatui::backend::CrosstermBackend;
use ratatui::Terminal;
use bosun::app::App;
use bosun::config::Config;
use bosun::error::{BosunError, Result};
use bosun::store::Store;
use bosun::tmux::{
attach::emergency_unbind, status_bar::emergency_uninstall as emergency_status_bar,
TokioTmuxClient,
};
#[tokio::main]
async fn main() -> Result<()> {
if std::env::args().any(|a| a == "--version" || a == "-V") {
println!("bosun {}", env!("CARGO_PKG_VERSION"));
return Ok(());
}
init_tracing();
let config = Config::from_env();
let socket = config.tmux_socket.clone();
let client: Arc<TokioTmuxClient> = match &socket {
Some(s) => Arc::new(TokioTmuxClient::with_socket(s.clone())),
None => Arc::new(TokioTmuxClient::new()),
};
let store = Arc::new(match Store::open_default() {
Ok(s) => s,
Err(e) => {
tracing::warn!("store open failed, using in-memory: {}", e);
Store::in_memory().expect("in-memory store cannot fail")
}
});
let socket_for_hook = socket.clone();
let default_hook = std::panic::take_hook();
std::panic::set_hook(Box::new(move |info| {
let _ = disable_raw_mode();
let _ = execute!(io::stdout(), DisableMouseCapture, LeaveAlternateScreen);
emergency_unbind(socket_for_hook.as_deref());
emergency_status_bar(socket_for_hook.as_deref());
default_hook(info);
}));
let mut terminal = setup_terminal()?;
let mut app = App::new(client, socket, config, store);
let run_result = app.run(&mut terminal).await;
restore_terminal(&mut terminal)?;
run_result
}
fn setup_terminal() -> Result<Terminal<CrosstermBackend<Stdout>>> {
enable_raw_mode().map_err(BosunError::Io)?;
let mut stdout = io::stdout();
execute!(stdout, EnterAlternateScreen, EnableMouseCapture).map_err(BosunError::Io)?;
let backend = CrosstermBackend::new(stdout);
Terminal::new(backend).map_err(BosunError::Io)
}
fn restore_terminal(terminal: &mut Terminal<CrosstermBackend<Stdout>>) -> Result<()> {
disable_raw_mode().map_err(BosunError::Io)?;
execute!(
terminal.backend_mut(),
DisableMouseCapture,
LeaveAlternateScreen,
)
.map_err(BosunError::Io)?;
terminal.show_cursor().map_err(BosunError::Io)?;
Ok(())
}
fn init_tracing() {
use tracing_subscriber::{fmt, EnvFilter};
let filter = EnvFilter::try_from_env("BOSUN_LOG").unwrap_or_else(|_| EnvFilter::new("off"));
let _ = fmt()
.with_env_filter(filter)
.with_writer(io::stderr)
.try_init();
}