cargo_dev_install/
tui_select.rs1use std::io::{self, BufRead, Write};
2
3pub fn select_bin<R: BufRead, W: Write>(
4 bin_names: &[String],
5 reader: &mut R,
6 writer: &mut W,
7) -> io::Result<String> {
8 if bin_names.len() == 1 {
9 return Ok(bin_names[0].clone());
10 }
11
12 if bin_names.is_empty() {
13 return Err(io::Error::new(
14 io::ErrorKind::InvalidInput,
15 "no binaries available",
16 ));
17 }
18
19 writeln!(writer, "Select a binary:")?;
20 for (idx, name) in bin_names.iter().enumerate() {
21 writeln!(writer, " {}) {}", idx + 1, name)?;
22 }
23
24 loop {
25 write!(writer, "Enter choice (1-{}): ", bin_names.len())?;
26 writer.flush()?;
27
28 let mut input = String::new();
29 if reader.read_line(&mut input)? == 0 {
30 return Err(io::Error::new(
31 io::ErrorKind::UnexpectedEof,
32 "no selection provided",
33 ));
34 }
35
36 let trimmed = input.trim();
37 if let Ok(choice) = trimmed.parse::<usize>() {
38 if (1..=bin_names.len()).contains(&choice) {
39 return Ok(bin_names[choice - 1].clone());
40 }
41 }
42
43 writeln!(writer, "Invalid selection. Try again.")?;
44 }
45}
46
47#[cfg(test)]
48mod tests {
49 use super::*;
50 use std::io::Cursor;
51
52 #[test]
53 fn select_bin_returns_single_entry() {
54 let mut input = Cursor::new("");
55 let mut output = Vec::new();
56 let bins = vec!["demo".to_string()];
57 let selected = select_bin(&bins, &mut input, &mut output).expect("select bin");
58 assert_eq!(selected, "demo");
59 assert!(output.is_empty());
60 }
61
62 #[test]
63 fn select_bin_prompts_and_selects_choice() {
64 let mut input = Cursor::new("2\n");
65 let mut output = Vec::new();
66 let bins = vec!["alpha".to_string(), "beta".to_string()];
67 let selected = select_bin(&bins, &mut input, &mut output).expect("select bin");
68 assert_eq!(selected, "beta");
69 let output_str = String::from_utf8(output).expect("utf8");
70 assert!(output_str.contains("Select a binary:"));
71 assert!(output_str.contains("1) alpha"));
72 assert!(output_str.contains("2) beta"));
73 }
74
75 #[test]
76 fn select_bin_reprompts_on_invalid_input() {
77 let mut input = Cursor::new("foo\n1\n");
78 let mut output = Vec::new();
79 let bins = vec!["alpha".to_string(), "beta".to_string()];
80 let selected = select_bin(&bins, &mut input, &mut output).expect("select bin");
81 assert_eq!(selected, "alpha");
82 let output_str = String::from_utf8(output).expect("utf8");
83 assert!(output_str.contains("Invalid selection"));
84 }
85
86 #[test]
87 fn select_bin_errors_on_empty_list() {
88 let mut input = Cursor::new("");
89 let mut output = Vec::new();
90 let bins = Vec::<String>::new();
91 let err = select_bin(&bins, &mut input, &mut output).expect_err("expected error");
92 assert_eq!(err.kind(), io::ErrorKind::InvalidInput);
93 }
94}