hpm_isp 0.5.0

ISP tool for HPMicro MCUs.
Documentation
use std::error::Error;
use std::fs;
use std::path::Path;

use dialoguer::{theme::ColorfulTheme, Confirm, Select};

use crate::config::Config;
use hpm_isp::memory_config::{MemoryConfig, PinGroup, PortConnection, QuadIOEnableSequence};

trait SelectPromptModel: Sized {
    const COUNT: u32;

    fn to_printable_str(&self) -> &'static str;
    fn from_num(num: u32) -> Option<Self>;

    fn show_select_prompt(prompt: &str) -> std::io::Result<Self> {
        let items: Vec<&str> = (0..Self::COUNT)
            .map(|num| Self::from_num(num).unwrap().to_printable_str())
            .collect();
        let selection = Select::with_theme(&ColorfulTheme::default())
            .items(&items)
            .with_prompt(prompt)
            .default(0)
            .interact()?;
        Ok(Self::from_num(selection as u32).unwrap())
    }
}

impl SelectPromptModel for PinGroup {
    const COUNT: u32 = 2;

    fn from_num(num: u32) -> Option<Self> {
        match num {
            0 => Some(Self::Group1),
            1 => Some(Self::Group2),
            _ => None,
        }
    }

    fn to_printable_str(&self) -> &'static str {
        match self {
            PinGroup::Group1 => "Group 1",
            PinGroup::Group2 => "Group 2",
        }
    }
}

impl SelectPromptModel for PortConnection {
    const COUNT: u32 = 5;

    fn to_printable_str(&self) -> &'static str {
        match self {
            PortConnection::PortACs0 => "Port A with CS 0",
            PortConnection::PortBCs0 => "Port B with CS 0",
            PortConnection::PortACs0PortBCs0 => "Port A with CS 0, Port B with CS 0",
            PortConnection::PortACs0PortACs1 => "Port A with CS 0, Port A with CS 1",
            PortConnection::PortBCs0PortBCs1 => "Port B with CS 0, Port B with CS 1",
        }
    }

    fn from_num(num: u32) -> Option<Self> {
        match num {
            0 => Some(Self::PortACs0),
            1 => Some(Self::PortBCs0),
            2 => Some(Self::PortACs0PortBCs0),
            3 => Some(Self::PortACs0PortACs1),
            4 => Some(Self::PortBCs0PortBCs1),
            _ => None,
        }
    }
}

impl SelectPromptModel for QuadIOEnableSequence {
    const COUNT: u32 = 5;

    fn to_printable_str(&self) -> &'static str {
        match self {
            QuadIOEnableSequence::None => "None",
            QuadIOEnableSequence::Status1Bit6 => "Status Register 1 bit 6",
            QuadIOEnableSequence::Status2Bit1 => "Status Register 2 bit 1",
            QuadIOEnableSequence::Status2Bit7 => "Status Register 2 bit 7",
            QuadIOEnableSequence::Status2Bit1ProgrammedBy0x31 => {
                "Status Register 2 bit 1, programmed by 0x31"
            }
        }
    }

    fn from_num(num: u32) -> Option<Self> {
        match num {
            0 => Some(Self::None),
            1 => Some(Self::Status1Bit6),
            2 => Some(Self::Status2Bit1),
            3 => Some(Self::Status2Bit7),
            4 => Some(Self::Status2Bit1ProgrammedBy0x31),
            _ => None,
        }
    }
}

pub fn config_wizard<P>(path: P) -> Result<(), Box<dyn Error>>
where
    P: AsRef<Path>,
{
    let port_connection = PortConnection::show_select_prompt("Select port connection")?;
    let pin_group = PinGroup::show_select_prompt("Select pin group")?;
    let quad_io_enable_sequence =
        QuadIOEnableSequence::show_select_prompt("Select Quad Enable sequence")?;

    let config = MemoryConfig::new()
        .port_connection(port_connection)
        .pin_group(pin_group)
        .quad_io_enable_sequence(quad_io_enable_sequence);

    if path.as_ref().exists() {
        let replace = Confirm::with_theme(&ColorfulTheme::default())
            .with_prompt(format!(
                "{} already exists, overwrite it?",
                path.as_ref().display()
            ))
            .interact()?;
        if !replace {
            return Ok(());
        }
    }

    let output = Config::new(config).to_toml_string()?;
    fs::write(&path, output)?;

    println!(
        "Config file was successfully saved to: {}",
        fs::canonicalize(path)?.display()
    );

    Ok(())
}