pub mod app;
pub mod flow;
pub mod global_config;
pub mod types;
pub mod ui;
pub mod writer;
use std::io::{self, stdout};
use std::time::Duration;
use crossterm::event::{
Event as CtEvent, EventStream, KeyEventKind, KeyboardEnhancementFlags,
PopKeyboardEnhancementFlags, PushKeyboardEnhancementFlags,
};
use crossterm::execute;
use crossterm::terminal::{
EnterAlternateScreen, LeaveAlternateScreen, disable_raw_mode, enable_raw_mode,
};
use futures::StreamExt;
use ratatui::Terminal;
use ratatui::backend::CrosstermBackend;
use tokio::time::timeout;
use self::flow::build_wiz_flow;
use self::global_config::load_global_config;
use self::types::WizPhase;
use self::ui::draw_wiz;
use self::writer::perform_write;
use crate::harness::config::load_harness_config;
const TERMINAL_PHASE_EXIT_DELAY: Duration = Duration::from_millis(1500);
pub async fn run_wiz(project_root: &str) -> Result<(), Box<dyn std::error::Error>> {
let existing_harnesses = load_harness_config(project_root).ok();
let existing_global = load_global_config().ok();
let questions = build_wiz_flow(existing_harnesses.as_ref(), existing_global.as_ref());
let mut state = self::types::WizState::new(questions);
enable_raw_mode()?;
let mut stdout = stdout();
execute!(
stdout,
EnterAlternateScreen,
PushKeyboardEnhancementFlags(
KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES
| KeyboardEnhancementFlags::REPORT_ALL_KEYS_AS_ESCAPE_CODES
| KeyboardEnhancementFlags::REPORT_ALTERNATE_KEYS
| KeyboardEnhancementFlags::REPORT_EVENT_TYPES
)
)?;
let backend = CrosstermBackend::new(stdout);
let mut terminal = Terminal::new(backend)?;
let panic_hook = std::panic::take_hook();
std::panic::set_hook(Box::new(move |info| {
let _ = disable_raw_mode();
let _ = execute!(
io::stdout(),
PopKeyboardEnhancementFlags,
LeaveAlternateScreen
);
panic_hook(info);
}));
let mut reader = EventStream::new();
terminal.draw(|frame| draw_wiz(frame, &state))?;
loop {
match state.phase {
WizPhase::Writing => {
match perform_write(&state, project_root) {
Ok(()) => state.phase = WizPhase::Done,
Err(e) => {
state.error = Some(e);
state.phase = WizPhase::Done;
}
}
terminal.draw(|frame| draw_wiz(frame, &state))?;
}
WizPhase::Done | WizPhase::Aborted => {
match timeout(TERMINAL_PHASE_EXIT_DELAY, reader.next()).await {
Ok(Some(Ok(CtEvent::Key(key)))) if key.kind == KeyEventKind::Press => break,
Ok(Some(Ok(_))) => continue,
Ok(Some(Err(_))) | Ok(None) | Err(_) => break,
}
}
_ => {
if let Some(Ok(CtEvent::Key(key))) = reader.next().await {
if key.kind == KeyEventKind::Press {
let should_exit = state.handle_key(key);
if should_exit {
break;
}
}
} else {
break;
}
terminal.draw(|frame| draw_wiz(frame, &state))?;
}
}
}
disable_raw_mode()?;
execute!(
terminal.backend_mut(),
PopKeyboardEnhancementFlags,
LeaveAlternateScreen
)?;
terminal.show_cursor()?;
Ok(())
}