use dialoguer::console::Term;
use dialoguer::Confirm;
use std::io::{self, Write};
use std::sync::mpsc::{self, Receiver, Sender};
use std::thread::{self, JoinHandle};
use std::time::Duration;
#[derive(Debug, PartialEq, Clone, Copy)]
pub enum PromptResult {
Yes,
No,
Timeout,
}
pub struct BackgroundPrompt {
handle: JoinHandle<PromptResult>,
}
impl BackgroundPrompt {
pub fn wait(self) -> PromptResult {
self.handle.join().unwrap_or(PromptResult::No)
}
}
pub fn start_background_prompt(question: &str, timeout_secs: u64) -> Option<BackgroundPrompt> {
if !is_interactive() {
return None;
}
let question = question.to_string();
let handle = thread::spawn(move || run_prompt(&question, timeout_secs));
Some(BackgroundPrompt { handle })
}
fn run_prompt(question: &str, timeout_secs: u64) -> PromptResult {
let (tx, rx): (Sender<bool>, Receiver<bool>) = mpsc::channel();
let question_clone = question.to_string();
thread::spawn(move || {
let result = Confirm::new()
.with_prompt(&question_clone)
.default(false)
.interact();
if let Ok(confirmed) = result {
let _ = tx.send(confirmed);
}
});
thread::sleep(Duration::from_millis(50));
for remaining in (0..=timeout_secs).rev() {
print!("\x1b[s\n\r(auto-skip in {}s) \x1b[u", remaining);
io::stdout().flush().unwrap();
if remaining == 0 {
restore_terminal();
println!("\n\nSkipping video generation (timeout)");
return PromptResult::Timeout;
}
match rx.recv_timeout(Duration::from_secs(1)) {
Ok(confirmed) => {
print!("\n\r\x1b[2K\x1b[1A");
io::stdout().flush().unwrap();
return if confirmed {
PromptResult::Yes
} else {
PromptResult::No
};
}
Err(mpsc::RecvTimeoutError::Timeout) => {
}
Err(mpsc::RecvTimeoutError::Disconnected) => {
print!("\n\r\x1b[2K\x1b[1A");
io::stdout().flush().unwrap();
return PromptResult::No;
}
}
}
PromptResult::Timeout
}
fn restore_terminal() {
let term = Term::stdout();
let _ = term.show_cursor();
let _ = io::stdout().flush();
}
fn is_interactive() -> bool {
use std::io::IsTerminal;
std::io::stdin().is_terminal()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_prompt_result_enum() {
assert_ne!(PromptResult::Yes, PromptResult::No);
assert_ne!(PromptResult::No, PromptResult::Timeout);
assert_ne!(PromptResult::Yes, PromptResult::Timeout);
}
}