Skip to main content

systemprompt_cli/
interactive.rs

1use crate::CliConfig;
2use anyhow::{anyhow, Result};
3use dialoguer::theme::ColorfulTheme;
4use dialoguer::{Confirm, Select};
5
6pub fn require_confirmation(
7    message: &str,
8    skip_confirmation: bool,
9    config: &CliConfig,
10) -> Result<()> {
11    if skip_confirmation {
12        return Ok(());
13    }
14
15    if !config.is_interactive() {
16        return Err(anyhow!("--yes is required in non-interactive mode"));
17    }
18
19    let confirmed = Confirm::with_theme(&ColorfulTheme::default())
20        .with_prompt(message)
21        .default(false)
22        .interact()?;
23
24    if confirmed {
25        Ok(())
26    } else {
27        Err(anyhow!("Operation cancelled"))
28    }
29}
30
31pub fn require_confirmation_default_yes(
32    message: &str,
33    skip_confirmation: bool,
34    config: &CliConfig,
35) -> Result<()> {
36    if skip_confirmation {
37        return Ok(());
38    }
39
40    if !config.is_interactive() {
41        return Err(anyhow!("--yes is required in non-interactive mode"));
42    }
43
44    let confirmed = Confirm::with_theme(&ColorfulTheme::default())
45        .with_prompt(message)
46        .default(true)
47        .interact()?;
48
49    if confirmed {
50        Ok(())
51    } else {
52        Err(anyhow!("Operation cancelled"))
53    }
54}
55
56pub fn resolve_required<T, F>(
57    value: Option<T>,
58    flag_name: &str,
59    config: &CliConfig,
60    prompt_fn: F,
61) -> Result<T>
62where
63    F: FnOnce() -> Result<T>,
64{
65    match value {
66        Some(v) => Ok(v),
67        None if config.is_interactive() => prompt_fn(),
68        None => Err(anyhow!(
69            "--{} is required in non-interactive mode",
70            flag_name
71        )),
72    }
73}
74
75pub fn select_from_list<T: ToString + Clone>(
76    prompt: &str,
77    items: &[T],
78    flag_name: &str,
79    config: &CliConfig,
80) -> Result<T> {
81    if items.is_empty() {
82        return Err(anyhow!("No items available for selection"));
83    }
84
85    if !config.is_interactive() {
86        return Err(anyhow!(
87            "--{} is required in non-interactive mode",
88            flag_name
89        ));
90    }
91
92    let display: Vec<String> = items.iter().map(ToString::to_string).collect();
93
94    let idx = Select::with_theme(&ColorfulTheme::default())
95        .with_prompt(prompt)
96        .items(&display)
97        .default(0)
98        .interact()?;
99
100    Ok(items[idx].clone())
101}
102
103pub fn select_index(prompt: &str, items: &[&str], config: &CliConfig) -> Result<Option<usize>> {
104    if !config.is_interactive() {
105        return Ok(None);
106    }
107
108    let idx = Select::with_theme(&ColorfulTheme::default())
109        .with_prompt(prompt)
110        .items(items)
111        .default(0)
112        .interact()?;
113
114    Ok(Some(idx))
115}
116
117pub fn prompt_input(prompt: &str, flag_name: &str, config: &CliConfig) -> Result<String> {
118    if !config.is_interactive() {
119        return Err(anyhow!(
120            "--{} is required in non-interactive mode",
121            flag_name
122        ));
123    }
124
125    let input = dialoguer::Input::<String>::with_theme(&ColorfulTheme::default())
126        .with_prompt(prompt)
127        .interact_text()?;
128
129    Ok(input)
130}
131
132pub fn prompt_input_with_default(
133    prompt: &str,
134    default: &str,
135    config: &CliConfig,
136) -> Result<String> {
137    if !config.is_interactive() {
138        return Ok(default.to_string());
139    }
140
141    let input = dialoguer::Input::<String>::with_theme(&ColorfulTheme::default())
142        .with_prompt(prompt)
143        .default(default.to_string())
144        .interact_text()?;
145
146    Ok(input)
147}
148
149pub fn confirm_optional(message: &str, default: bool, config: &CliConfig) -> Result<bool> {
150    if !config.is_interactive() {
151        return Ok(default);
152    }
153
154    let confirmed = Confirm::with_theme(&ColorfulTheme::default())
155        .with_prompt(message)
156        .default(default)
157        .interact()?;
158
159    Ok(confirmed)
160}