diff --git a/src/lib.rs b/src/lib.rs index 814ccb1..0234d74 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -36,17 +36,7 @@ pub fn set_no_interaction(b: bool) { } /// Return current non-interactive state. pub fn is_no_interaction() -> bool { - if NO_INTERACTION.load(Ordering::Relaxed) { - return true; - } - // Also honor NO_INTERACTION=1/true environment variable for convenience/testing - match std::env::var("NO_INTERACTION") { - Ok(v) => { - let v = v.trim(); - v == "1" || v.eq_ignore_ascii_case("true") - } - Err(_) => false, - } + NO_INTERACTION.load(Ordering::Relaxed) } /// Set verbose level (0 = normal, 1 = verbose, 2 = super-verbose) @@ -648,13 +638,8 @@ where } // Print a blank line before the selection prompt to keep output synchronized. printer(""); - let idx = if crate::is_no_interaction() || !crate::stdin_is_tty() { - // Non-interactive: auto-select the first candidate deterministically (as listed) - 0 - } else { - crate::ui::prompt_select_index("Select a Whisper model", &display_names) - .context("Failed to read selection")? - }; + let idx = crate::ui::prompt_select_index("Select a Whisper model", &display_names) + .context("Failed to read selection")?; let chosen = candidates.swap_remove(idx); let _ = std::fs::write(models_dir.join(".last_model"), chosen.display().to_string()); // Print an empty line after selection input diff --git a/src/main.rs b/src/main.rs index 33fd3fa..7241eed 100644 --- a/src/main.rs +++ b/src/main.rs @@ -67,8 +67,7 @@ struct Args { quiet: bool, /// Non-interactive mode: never prompt; use defaults instead. - /// Deprecated alias supported: --no-interation (typo) - #[arg(long = "no-interaction", alias = "no-interation", global = true)] + #[arg(long = "no-interaction", global = true)] no_interaction: bool, /// Disable progress bars (also respects NO_PROGRESS=1). Progress bars render on stderr only when attached to a TTY. @@ -504,17 +503,14 @@ fn run() -> Result<()> { if let Some(out) = &args.output { // Merge target: either only merged, or merged plus separate let outp = PathBuf::from(out); - // Ensure target directory exists appropriately for the chosen mode + if let Some(parent) = outp.parent() { create_dir_all(parent).ok(); } + // Name: _out or _merged depending on flag if args.merge_and_separate { - // When writing inside an output directory, create it directly - create_dir_all(&outp).ok(); // In merge+separate mode, always write merged output inside the provided directory - let base = outp.join(format!("{}_merged", polyscribe::date_prefix())); + let base = PathBuf::from(out).join(format!("{}_merged", polyscribe::date_prefix())); let root = OutputRoot { items: merged_items.clone() }; write_outputs(&base, &root, &out_formats)?; } else { - // For single merged file, ensure the parent dir exists - if let Some(parent) = outp.parent() { create_dir_all(parent).ok(); } let base = outp.with_file_name(format!("{}_{}", polyscribe::date_prefix(), outp.file_name().and_then(|s| s.to_str()).unwrap_or("out"))); let root = OutputRoot { items: merged_items.clone() }; write_outputs(&base, &root, &out_formats)?; @@ -919,26 +915,4 @@ mod tests { std_env::remove_var("POLYSCRIBE_TEST_FORCE_VULKAN"); } } - - #[test] - fn test_no_interaction_disables_speaker_prompt() { - use polyscribe::ui; - // Ensure non-interactive via env and global flag - unsafe { - std_env::set_var("NO_INTERACTION", "1"); - } - polyscribe::set_no_interaction(true); - ui::testing_reset_prompt_call_counters(); - // Build a minimal progress manager - let pf = polyscribe::progress::ProgressFactory::from_config(&polyscribe::Config::default()); - let pm = pf.make_manager(polyscribe::progress::ProgressMode::Single); - let dummy = std::path::PathBuf::from("example.wav"); - let got = super::prompt_speaker_name_for_path(&dummy, "DefaultSpeaker", /*enabled:*/ true, &pm); - assert_eq!(got, "DefaultSpeaker"); - assert_eq!(ui::testing_prompt_call_count(), 0, "no prompt functions should be called when NO_INTERACTION=1"); - // Cleanup - unsafe { - std_env::remove_var("NO_INTERACTION"); - } - } } diff --git a/src/models.rs b/src/models.rs index 77eda43..9b26704 100644 --- a/src/models.rs +++ b/src/models.rs @@ -1135,7 +1135,6 @@ mod tests { #[test] fn test_format_model_list_spacing_and_structure() { - use std::env as std_env; let models = vec![ ModelEntry { name: "tiny.en-q5_1".to_string(), @@ -1376,22 +1375,6 @@ mod tests { } } - #[test] - fn test_no_interaction_models_downloader_skips_prompts() { - // Force non-interactive; verify that no UI prompt functions are invoked - unsafe { std::env::set_var("NO_INTERACTION", "1"); } - crate::set_no_interaction(true); - crate::ui::testing_reset_prompt_call_counters(); - let models = vec![ - ModelEntry { name: "tiny.en-q5_1".to_string(), base: "tiny".to_string(), subtype: "en-q5_1".to_string(), size: 1024, sha256: None, repo: "ggerganov/whisper.cpp".to_string() }, - ModelEntry { name: "tiny-q5_1".to_string(), base: "tiny".to_string(), subtype: "q5_1".to_string(), size: 2048, sha256: None, repo: "ggerganov/whisper.cpp".to_string() }, - ]; - let picked = super::prompt_select_models_two_stage(&models).unwrap(); - assert!(picked.is_empty(), "non-interactive should not select any models by default"); - assert_eq!(crate::ui::testing_prompt_call_count(), 0, "no prompt functions should be called in non-interactive mode"); - unsafe { std::env::remove_var("NO_INTERACTION"); } - } - #[test] fn test_wrong_hash_deletes_temp_and_errors() { use std::sync::{Mutex, OnceLock}; diff --git a/src/ui.rs b/src/ui.rs index 0d33476..04618e4 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -4,24 +4,6 @@ // If you need a new prompt type, add it here so callers don't depend on a specific library. use anyhow::{anyhow, Result}; -use std::sync::atomic::{AtomicUsize, Ordering}; - -// Test-visible counter to detect accidental prompt calls in non-interactive/CI contexts. -static PROMPT_CALLS: AtomicUsize = AtomicUsize::new(0); - -/// Reset the internal prompt call counter (testing aid). -pub fn testing_reset_prompt_call_counters() { - PROMPT_CALLS.store(0, Ordering::Relaxed); -} - -/// Get current prompt call count (testing aid). -pub fn testing_prompt_call_count() -> usize { - PROMPT_CALLS.load(Ordering::Relaxed) -} - -fn note_prompt_call() { - PROMPT_CALLS.fetch_add(1, Ordering::Relaxed); -} /// Prompt the user for a free-text value with a default fallback. /// @@ -30,7 +12,6 @@ fn note_prompt_call() { /// - On any prompt error (e.g., non-TTY, read error), returns an error; callers should /// handle it and typically fall back to `default` in non-interactive contexts. pub fn prompt_text(prompt: &str, default: &str) -> Result { - note_prompt_call(); let res: Result = cliclack::input(prompt) .default_input(default) .interact(); @@ -48,7 +29,6 @@ pub fn prompt_text(prompt: &str, default: &str) -> Result { /// /// Returns the selected boolean. Any underlying prompt error is returned as an error. pub fn prompt_confirm(prompt: &str, default: bool) -> Result { - note_prompt_call(); let res: Result = cliclack::confirm(prompt) .initial_value(default) .interact(); @@ -63,7 +43,6 @@ pub fn prompt_select_index(prompt: &str, items: &[T]) -> R if items.is_empty() { return Err(anyhow!("prompt_select_index called with empty items")); } - note_prompt_call(); let mut sel = cliclack::select(prompt); for (i, it) in items.iter().enumerate() { sel = sel.item(i, format!("{}", it), ""); @@ -95,7 +74,6 @@ pub fn prompt_multiselect_indices( for (i, it) in items.iter().enumerate() { ms = ms.item(i, format!("{}", it), ""); } - note_prompt_call(); let indices: Vec = ms .initial_values(defaults.to_vec()) .required(false) diff --git a/tests/integration_cli.rs b/tests/integration_cli.rs index 1bb58ca..cf98d89 100644 --- a/tests/integration_cli.rs +++ b/tests/integration_cli.rs @@ -950,30 +950,3 @@ fn out_format_multiple_json_and_srt() { } */ - - -#[test] -fn cli_no_interation_alias_skips_speaker_prompts_and_uses_defaults() { - let exe = env!("CARGO_BIN_EXE_polyscribe"); - - let input1 = manifest_path("input/1-s0wlz.json"); - let input2 = manifest_path("input/2-vikingowl.json"); - - let output = Command::new(exe) - .arg(input1.as_os_str()) - .arg(input2.as_os_str()) - .arg("-m") - .arg("--set-speaker-names") - .arg("--no-interation") - .output() - .expect("failed to spawn polyscribe"); - - assert!(output.status.success(), "CLI did not exit successfully"); - - let stdout = String::from_utf8(output.stdout).expect("stdout not UTF-8"); - let root: OutputRoot = serde_json::from_str(&stdout).unwrap(); - let speakers: std::collections::HashSet = - root.items.into_iter().map(|e| e.speaker).collect(); - assert!(speakers.contains("s0wlz"), "default s0wlz not used (alias)"); - assert!(speakers.contains("vikingowl"), "default vikingowl not used (alias)"); -}