probe-run 0.3.6

Runs embedded programs just like native ones
use std::str::FromStr;

use anyhow::{anyhow, bail};
use probe_rs::{DebugProbeInfo, Probe};

use crate::cli;

const NO_PROBE_FOUND_ERR: &str = "no probe was found.\n
Common reasons for this are faulty cables or missing permissions.
For detailed instructions, visit: https://github.com/knurling-rs/probe-run/tree/2f138c3#troubleshooting";

pub fn open(opts: &cli::Opts) -> Result<Probe, anyhow::Error> {
    let all_probes = Probe::list_all();
    let filtered_probes = if let Some(probe_opt) = opts.probe.as_deref() {
        let selector = probe_opt.parse()?;
        filter(&all_probes, &selector)
    } else {
        all_probes
    };

    if filtered_probes.is_empty() {
        bail!("{}", NO_PROBE_FOUND_ERR)
    }

    log::debug!("found {} probes", filtered_probes.len());

    if filtered_probes.len() > 1 {
        print(&filtered_probes);
        bail!("more than one probe found; use --probe to specify which one to use");
    }

    let mut probe = filtered_probes[0].open()?;
    log::debug!("opened probe");

    if let Some(speed) = opts.speed {
        probe.set_speed(speed)?;
    }

    Ok(probe)
}

pub fn print(probes: &[DebugProbeInfo]) {
    if !probes.is_empty() {
        println!("the following probes were found:");
        probes
            .iter()
            .enumerate()
            .for_each(|(num, link)| println!("[{}]: {:?}", num, link));
    } else {
        println!("Error: {}", NO_PROBE_FOUND_ERR);
    }
}

fn filter(probes: &[DebugProbeInfo], selector: &ProbeFilter) -> Vec<DebugProbeInfo> {
    probes
        .iter()
        .filter(|probe| {
            if let Some((vid, pid)) = selector.vid_pid {
                if probe.vendor_id != vid || probe.product_id != pid {
                    return false;
                }
            }

            if let Some(serial) = &selector.serial {
                if probe.serial_number.as_deref() != Some(serial) {
                    return false;
                }
            }

            true
        })
        .cloned()
        .collect()
}

struct ProbeFilter {
    vid_pid: Option<(u16, u16)>,
    serial: Option<String>,
}

impl FromStr for ProbeFilter {
    type Err = anyhow::Error;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let parts = s.split(':').collect::<Vec<_>>();
        match *parts {
            [serial] => Ok(Self {
                vid_pid: None,
                serial: Some(serial.to_string()),
            }),
            [vid, pid] => Ok(Self {
                vid_pid: Some((u16::from_str_radix(vid, 16)?, u16::from_str_radix(pid, 16)?)),
                serial: None,
            }),
            [vid, pid, serial] => Ok(Self {
                vid_pid: Some((u16::from_str_radix(vid, 16)?, u16::from_str_radix(pid, 16)?)),
                serial: Some(serial.to_string()),
            }),
            _ => Err(anyhow!("invalid probe filter")),
        }
    }
}