use std::collections::BTreeMap;
use std::time::Duration;
use color_eyre::eyre::{OptionExt, Result, eyre};
use dialoguer::Select;
use dialoguer::theme::ColorfulTheme;
use reqwest::StatusCode;
use crate::docs_viewer::Viewer;
use crate::metadata::structs::FirmwareDownload;
pub struct FirmwareMultichoice<'a>
{
state: State,
release: &'a str,
variants: Vec<&'a FirmwareDownload>,
friendly_names: Vec<&'a str>,
}
#[derive(Default)]
enum State
{
#[default]
PickFirmware,
PickAction(usize),
ShowDocs(usize, usize),
FlashFirmware(usize),
Cancel,
}
impl<'a> FirmwareMultichoice<'a>
{
pub fn new(release: &'a str, variants: &'a BTreeMap<String, FirmwareDownload>) -> Self
{
let friendly_names: Vec<_> = variants
.values()
.map(|variant| variant.friendly_name.as_str())
.collect();
Self {
state: State::default(),
release,
variants: variants.values().collect(),
friendly_names,
}
}
pub fn complete(&self) -> bool
{
matches!(self.state, State::FlashFirmware(_) | State::Cancel)
}
pub fn step(&mut self) -> Result<()>
{
self.state = match self.state {
State::PickFirmware => self.firmware_selection()?,
State::PickAction(index) => self.action_selection(index)?,
State::ShowDocs(name_index, variant_index) => self.show_documentation(name_index, variant_index)?,
State::FlashFirmware(index) => State::FlashFirmware(index),
State::Cancel => State::Cancel,
};
Ok(())
}
pub fn selection(&self) -> Option<&'a FirmwareDownload>
{
match self.state {
State::FlashFirmware(index) => Some(self.variants[index]),
_ => None,
}
}
fn firmware_selection(&self) -> Result<State>
{
let selection = Select::with_theme(&ColorfulTheme::default())
.with_prompt("Which firmware variant would you like to run on your probe?")
.items(self.friendly_names.as_slice())
.interact_opt()?;
match selection {
Some(index) => Ok(State::PickAction(index)),
None => Ok(State::Cancel),
}
}
fn action_selection(&self, name_index: usize) -> Result<State>
{
let friendly_name = self.friendly_names[name_index];
let (variant_index, _) = self
.variants
.iter()
.enumerate()
.find(|(_, variant)| variant.friendly_name == friendly_name)
.ok_or_eyre(eyre!("The friendly_name '{}' should always be found", friendly_name))?;
let items = ["Flash to probe", "Show documentation", "Choose a different variant"];
let selection = Select::with_theme(&ColorfulTheme::default())
.with_prompt("What action would you like to take with this firmware?")
.items(items)
.interact_opt()?;
Ok(match selection {
Some(item) => match item {
0 => State::FlashFirmware(variant_index),
1 => State::ShowDocs(name_index, variant_index),
2 => State::PickFirmware,
_ => Err(eyre!("Impossible selection for action"))?,
},
None => State::Cancel,
})
}
fn show_documentation(&self, name_index: usize, variant_index: usize) -> Result<State>
{
let variant = self.variants[variant_index];
let docs_uri = variant.build_documentation_url()?;
let client = reqwest::blocking::Client::new();
let response = client.get(docs_uri)
.timeout(Duration::from_secs(2))
.send()?;
match response.status() {
StatusCode::NOT_FOUND => println!(
"No documentation found, please go to {} to find out more",
variant.build_release_uri(self.release)?
),
StatusCode::OK => Viewer::display(&variant.friendly_name, &response.text()?)?,
status => Err(eyre!(
"Something went terribly wrong while grabbing the documentation to display: {}",
status
))?,
};
Ok(State::PickAction(name_index))
}
}