1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
use crate::{read_string, BoxResult, ReadLine};
use std::{collections::{LinkedList, VecDeque}, fmt::Display};





pub fn read_input_option<T: Display + Clone>(prompt: Option<&str>, default: Option<usize>, choices: &[T]) -> BoxResult<T> {
	let prompt = prompt.unwrap_or("Enter one of the following:");
	let choice_strings =
		choices.iter()
		.map(ToString::to_string)
		.collect::<Vec<_>>();
	loop {
		
		println!("{prompt}");
		for (i, choice) in choice_strings.iter().enumerate() {
			if let Some(default) = default {
				if i == default {
					println!("[{choice}]");
				} else {
					println!(" {choice}");
				}
			} else {
				println!("{choice}");
			}
		}
		
		let output = read_string()?;
		if output.is_empty() && let Some(default) = default {
			return Ok(choices[default].clone());
		}
		
		for (i, choice) in choice_strings.iter().enumerate() {
			if choice.eq_ignore_ascii_case(&output) {
				return Ok(choices[i].clone());
			}
		}
		
		println!("Invalid option.");
	}
}





impl<T: Display + Clone + std::cmp::PartialEq> ReadLine for &[T] {
	type Output = T;
	fn try_read_line(&self, prompt: Option<String>, default: Option<T>) -> BoxResult<Self::Output> {
		let prompt = prompt.as_ref().map(String::as_str);
		let default =
			self.iter().enumerate()
			.find(|v| Some(v.1) == default.as_ref())
			.map(|v| v.0);
		read_input_option(prompt, default, self)
	}
}

impl<T: Display + Clone + std::cmp::PartialEq, const LEN: usize> ReadLine for &[T; LEN] {
	type Output = T;
	fn try_read_line(&self, prompt: Option<String>, default: Option<T>) -> BoxResult<Self::Output> {
		let prompt = prompt.as_ref().map(String::as_str);
		let default =
			self.iter().enumerate()
			.find(|v| Some(v.1) == default.as_ref())
			.map(|v| v.0);
		read_input_option(prompt, default, *self)
	}
}

impl<T: Display + Clone + std::cmp::PartialEq> ReadLine for Vec<T> {
	type Output = T;
	fn try_read_line(&self, prompt: Option<String>, default: Option<T>) -> BoxResult<Self::Output> {
		let prompt = prompt.as_ref().map(String::as_str);
		let default =
			self.iter().enumerate()
			.find(|v| Some(v.1) == default.as_ref())
			.map(|v| v.0);
		read_input_option(prompt, default, &self)
	}
}

impl<T: Display + Clone + std::cmp::PartialEq> ReadLine for VecDeque<T> {
	type Output = T;
	fn try_read_line(&self, prompt: Option<String>, default: Option<T>) -> BoxResult<Self::Output> {
		let prompt = prompt.as_ref().map(String::as_str);
		let default =
			self.iter().enumerate()
			.find(|v| Some(v.1) == default.as_ref())
			.map(|v| v.0);
		read_input_option(prompt, default, &self.iter().cloned().collect::<Vec<_>>())
	}
}

impl<T: Display + Clone + std::cmp::PartialEq> ReadLine for LinkedList<T> {
	type Output = T;
	fn try_read_line(&self, prompt: Option<String>, default: Option<T>) -> BoxResult<Self::Output> {
		let prompt = prompt.as_ref().map(String::as_str);
		let default =
			self.iter().enumerate()
			.find(|v| Some(v.1) == default.as_ref())
			.map(|v| v.0);
		read_input_option(prompt, default, &self.iter().cloned().collect::<Vec<_>>())
	}
}