use {
crate::*,
std::io,
};
pub struct Question {
pub md: Option<String>,
pub answers: Vec<Answer>,
pub default_answer: Option<String>,
}
pub struct Answer {
pub key: String,
pub md: String,
}
impl Question {
pub fn new<S: Into<String>>(md: S) -> Self {
Self {
md: Some(md.into()),
answers: Vec::new(),
default_answer: None,
}
}
pub fn add_answer<K: std::fmt::Display, S: Into<String>>(&mut self, key: K, md: S) {
self.answers.push(Answer {
key: key.to_string(),
md: md.into(),
});
}
pub fn set_default<K: std::fmt::Display>(&mut self, default_answer: K) {
self.default_answer = Some(default_answer.to_string());
}
pub fn has_exotic_default(&self) -> bool {
if let Some(da) = self.default_answer.as_ref() {
for answer in &self.answers {
if &answer.key == da {
return false;
}
}
true
} else {
false
}
}
pub fn ask(&self, skin: &MadSkin) -> io::Result<String> {
if let Some(md) = &self.md {
skin.print_text(md);
}
for a in &self.answers {
if self.default_answer.as_ref() == Some(&a.key) {
mad_print_inline!(skin, "[**$0**] ", a.key);
} else {
mad_print_inline!(skin, "[$0] ", a.key);
}
skin.print_text(&a.md);
}
loop {
let mut input = String::new();
io::stdin().read_line(&mut input)?;
input.truncate(input.trim_end().len());
if input.is_empty() {
if let Some(da) = &self.default_answer {
return Ok(da.clone());
}
}
for a in &self.answers {
if a.key == input {
return Ok(input);
}
}
println!("answer {:?} not understood", input);
}
}
}
#[macro_export]
macro_rules! ask {
(
$skin: expr,
$question: expr,
{ $(($key: expr, $answer: expr) => $r: block)+ }
) => {{
let mut question = Question {
md: Some($question.to_string()),
answers: vec![$(Answer { key: $key.to_string(), md: $answer.to_string() }),*],
default_answer: None,
};
let key = question.ask($skin).unwrap();
let mut answers = question.answers.drain(..);
match key {
$(
_ if answers.next().unwrap().key == key => { $r }
)*
_ => { unreachable!(); }
}
}};
(
$skin: expr,
$question: expr,
($default_answer: expr)
{ $(($key: expr, $answer: expr) => $r: block)+ }
) => {{
let mut question = Question {
md: Some($question.to_string()),
answers: vec![$(Answer { key: $key.to_string(), md: $answer.to_string() }),*],
default_answer: Some($default_answer.to_string()),
};
if question.has_exotic_default() {
panic!("default answer when using the ask! macro must be among declared answers");
}
let key = question.ask($skin).unwrap();
let mut answers = question.answers.drain(..);
match key {
$(
_ if answers.next().unwrap().key == key => { $r }
)*
_ => { unreachable!() }
}
}}
}