use std::io::{self, Write};
use std::panic;
use std::process::ExitCode;
use crossterm::terminal::{disable_raw_mode, is_raw_mode_enabled};
use tracing::{debug, error, info};
use tracing_subscriber::EnvFilter;
use ulm::cli::{Args, Commands};
use ulm::exec::{copy_to_clipboard, execute_command};
use ulm::query;
use ulm::setup;
use ulm::tui::{display_error, run_tui, UserAction};
use ulm::Result;
fn main() -> ExitCode {
let default_hook = panic::take_hook();
panic::set_hook(Box::new(move |info| {
if is_raw_mode_enabled().unwrap_or(false) {
let _ = disable_raw_mode();
}
default_hook(info);
}));
tracing_subscriber::fmt()
.with_env_filter(
EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("ulm=info")),
)
.with_target(false)
.init();
debug!("ulm starting");
let runtime = match tokio::runtime::Builder::new_multi_thread()
.enable_all()
.build()
{
Ok(rt) => rt,
Err(e) => {
eprintln!("Error: Failed to create tokio runtime: {e}");
return ExitCode::FAILURE;
}
};
match runtime.block_on(run()) {
Ok(code) => {
debug!("ulm completed with code {}", code);
if code == 0 {
ExitCode::SUCCESS
} else {
ExitCode::from(code)
}
}
Err(err) => {
error!("{err:?}");
display_error(&err);
ExitCode::FAILURE
}
}
}
async fn run() -> Result<u8> {
let args = Args::parse_args();
debug!(?args, "parsed arguments");
match args.command {
Some(Commands::Setup) => {
info!("running setup");
setup::run_setup().await?;
Ok(0)
}
Some(Commands::Update) => {
info!("running update");
setup::run_update().await?;
Ok(0)
}
Some(Commands::Clean) => {
info!("running clean");
setup::run_clean()?;
Ok(0)
}
None => {
if args.has_query() {
let query = args.query_string();
info!(%query, "processing query");
process_query_flow(&query).await
} else {
println!("ulm - AI-powered manpage assistant");
println!("Run 'ulm --help' for usage information.");
Ok(0)
}
}
}
}
async fn process_query_flow(query: &str) -> Result<u8> {
let suggestions = query::process_query(query).await?;
if suggestions.is_empty() {
println!("No suggestions found for: {query}");
return Ok(0);
}
let action = run_tui(suggestions)?;
match action {
UserAction::Execute(cmd) => {
info!(command = %cmd, "executing command");
let exit_code = execute_command(&cmd)?;
Ok(exit_code.try_into().unwrap_or(1))
}
UserAction::Copy(cmd) => {
copy_to_clipboard(&cmd)?;
println!("Copied to clipboard: {cmd}");
Ok(0)
}
UserAction::Abort => {
debug!("user aborted");
Ok(0)
}
}
}
#[allow(dead_code)]
fn print_flush(msg: &str) {
print!("{msg}");
let _ = io::stdout().flush();
}