pciids 0.1.4

A library to parse the pci.ids data file.
Documentation
use pciids::PciIdData;

use anyhow::{Context, Result};

#[derive(structopt::StructOpt)]
struct Args {
    #[structopt(long = "pci-ids-file")]
    #[cfg_attr(
        target_os = "linux",
        structopt(default_value = "/usr/share/misc/pci.ids")
    )]
    #[cfg_attr(target_os = "redox", structopt(default_value = "/share/misc/pci.ids"))]
    pci_ids_file: String,

    #[structopt(subcommand)]
    subcommand: Subcommand,
}

fn parse_from_hex_u8(src: &str) -> Result<u8> {
    let num = u8::from_str_radix(src, 16)?;
    Ok(num)
}

fn parse_from_hex_u16(src: &str) -> Result<u16> {
    let num = u16::from_str_radix(src, 16)?;
    Ok(num)
}

fn parse_from_hex_u16u16(src: &str) -> Result<(u16, u16)> {
    let n = u32::from_str_radix(src, 16)?;
    Ok(((n >> 16) as u16, (0xFF & n) as u16))
}

#[derive(Debug, PartialEq, structopt::StructOpt)]
enum Subcommand {
    Device(Device),
    Class(Class),
}

#[derive(Debug, PartialEq, structopt::StructOpt)]
struct Device {
    #[structopt(parse(try_from_str = parse_from_hex_u16))]
    vendor: u16,

    #[structopt(parse(try_from_str = parse_from_hex_u16))]
    device: Option<u16>,

    #[structopt(parse(try_from_str = parse_from_hex_u16u16))]
    subsystem: Option<(u16, u16)>,
}

#[derive(Debug, PartialEq, structopt::StructOpt)]
struct Class {
    #[structopt(parse(try_from_str = parse_from_hex_u8))]
    class: u8,

    #[structopt(parse(try_from_str = parse_from_hex_u8))]
    subclass: Option<u8>,

    #[structopt(parse(try_from_str = parse_from_hex_u8))]
    prog_interface: Option<u8>,
}

#[paw::main]
fn main(args: Args) -> Result<()> {
    let mut pci_id_data = PciIdData::new();
    let pci_id_file_contents =
        std::fs::read_to_string(args.pci_ids_file).expect("cannot read file");
    pci_id_data.add_pci_ids_data(&mut pci_id_file_contents.as_bytes())?;

    match args.subcommand {
        Subcommand::Device(d) => print_device(d, &pci_id_data)?,
        Subcommand::Class(c) => print_class(c, &pci_id_data)?,
    };
    Ok(())
}

fn print_device(device_args: Device, pci_data: &PciIdData) -> Result<()> {
    let mut msg = String::from(format!(
        "Looking up about vendor[:device][:subsystem]: {:01$X}",
        device_args.vendor, 4
    ));
    if let Some(device) = device_args.device {
        msg.push_str(format!(":{:01$X}", device, 4).as_str());
        if let Some(subsystem) = device_args.subsystem {
            msg.push_str(format!(".{:02$X}{:02$X}", subsystem.0, subsystem.1, 4).as_str());
        }
    }
    println!("{}", msg);

    let vendor = pci_data
        .get_vendor(&device_args.vendor)
        .context("Vendor not found.")?;
    println!("Vendor name: {}", vendor.name);
    if let Some(device_id) = device_args.device {
        if let Ok(device) = vendor.get_device(&device_id) {
            println!("Device name: {}", device.name);
            if let Some(subsystem_id) = device_args.subsystem {
                if let Ok(subsystem) = device.get_subsystem(&(subsystem_id.0, subsystem_id.1)) {
                    println!("Subsystem name: {}", subsystem.name);
                } else {
                    println!("Subsystem not found.");
                }
            }
        } else {
            println!("Device not found.");
        }

    }
    Ok(())
}

fn print_class(class_args: Class, pci_data: &PciIdData) -> Result<()> {
    let mut msg = String::from(format!(
        "Looking up info about class[:subclass][.prog_interface]: {:01$X}",
        class_args.class, 2
    ));
    if let Some(subclass) = class_args.subclass {
        msg.push_str(format!(":{:01$X}", subclass, 2).as_str());
        if let Some(prog_interface) = class_args.prog_interface {
            msg.push_str(format!(".{:01$X}", prog_interface, 2).as_str());
        }
    }
    println!("{}", msg);

    let class = pci_data
        .get_class(&class_args.class)
        .context("Vendor not found")?;
    println!("Class name: {}", class.name);
    if let Some(subclass_id) = &class_args.subclass {
        let subclass = class
            .get_subclass(&subclass_id)
            .context("Subclass not found")?;
        println!("Subclass name: {}", subclass.name);
        if let Some(prog_interface_id) = &class_args.prog_interface {
            let prog_interface = subclass
                .get_prog_interface(prog_interface_id)
                .context("Programming interface not found.")?;
            println!("Programming interface name: {}", prog_interface.name);
        }
    }
    Ok(())
}