pciids 0.1.4

A library to parse the pci.ids data file.
Documentation
use std::collections::HashMap;
use std::concat;

use anyhow::{anyhow, Context, Result};
use log::{debug, info};
use pest::Parser;
use pest_derive::Parser;

#[derive(Debug)]
pub struct PciIdData {
    vendors: PciVendors,
    classes: PciClasses,
}

impl PciIdData {
    pub fn new() -> Self {
        PciIdData {
            vendors: PciVendors::new(),
            classes: PciClasses::new(),
        }
    }
}

impl Default for PciIdData {
    fn default() -> Self {
        Self::new()
    }
}

impl PciIdData {
    pub fn add_pci_ids_data(&mut self, pciids_data_stream: &mut dyn std::io::Read) -> Result<()> {
        let mut num_vendors = 0;
        let mut num_classes = 0;

        info!("Parsing pci.id data!");
        let mut unparsed_data = String::new();
        pciids_data_stream.read_to_string(&mut unparsed_data)?;
        if let Ok(parse) = PciIdsParser::parse(Rule::file, &unparsed_data) {
            for line_pair in parse {
                match line_pair.as_rule() {
                    Rule::vendor => {
                        num_vendors += 1;
                        debug!("vendor: {:#?}", &line_pair);
                        self.add_vendor_from_vendor_pairs(&mut line_pair.into_inner())?;
                    }
                    Rule::class => {
                        num_classes += 1;
                        debug!("class: {:#?}", &line_pair);
                        self.add_class_from_class_pairs(&mut line_pair.into_inner())?;
                    }
                    Rule::EOI => info!("End of input reached."),
                    _ => unreachable!(),
                }
            }
            info!(
                concat!(
                    "Number of objects imported from the pci.ids database: ",
                    "vendors({}) and classes({})",
                ),
                num_vendors, num_classes
            );
        } else {
            info!("Couldn't parse pci.ids file.");
        }

        Ok(())
    }

    fn add_vendor_from_vendor_pairs(
        &mut self,
        vendor_pairs: &mut pest::iterators::Pairs<Rule>,
    ) -> Result<()> {
        let vendor_id_pair = vendor_pairs
            .next()
            .ok_or_else(|| anyhow!("No vendor id found."))?;
        let vendor_id = u16::from_str_radix(vendor_id_pair.as_str(), 16)
            .with_context(|| format!("Invalid vendor_id: {}", vendor_id_pair.as_str()))?;
        let vendor_name = vendor_pairs
            .next()
            .ok_or_else(|| anyhow!("No vendor name found."))?
            .as_str();
        let mut pci_vendor = PciVendor::new(vendor_id, &vendor_name);
        for device_pair in vendor_pairs {
            pci_vendor.add_device_from_device_pairs(&mut device_pair.into_inner())?;
        }
        self.vendors.entry(vendor_id).or_insert(pci_vendor);
        Ok(())
    }

    pub fn get_vendor(&self, vendor_id: &u16) -> Result<&PciVendor> {
        Ok(self
            .vendors
            .get(&vendor_id)
            .ok_or_else(|| anyhow!("Vendor {} not found.", vendor_id))?)
    }

    fn add_class_from_class_pairs(
        &mut self,
        class_pairs: &mut pest::iterators::Pairs<Rule>,
    ) -> Result<()> {
        let class_id_pair = class_pairs
            .next()
            .ok_or_else(|| anyhow!("No class id found."))?;
        let class_id = u8::from_str_radix(class_id_pair.as_str(), 16)
            .with_context(|| format!("Invalid class_id: {}", class_id_pair.as_str()))?;
        let class_name = class_pairs
            .next()
            .ok_or_else(|| anyhow!("No class name found."))?
            .as_str();
        let mut class = PciClass::new(class_id, &class_name);
        for subclass_pair in class_pairs {
            class.add_subclass_from_subclass_pairs(&mut subclass_pair.into_inner())?;
        }
        self.classes.entry(class_id).or_insert(class);
        Ok(())
    }

    pub fn get_class(&self, class_id: &u8) -> Result<&PciClass> {
        Ok(self
            .classes
            .get(&class_id)
            .ok_or_else(|| anyhow!("Class {} not found.", class_id))?)
    }
}

type PciVendors = HashMap<u16, PciVendor>;

#[derive(Debug)]
pub struct PciVendor {
    pub id: u16,
    pub name: String,
    devices: HashMap<u16, PciDevice>,
}

impl PciVendor {
    fn new(id: u16, name: &str) -> Self {
        PciVendor {
            id,
            name: String::from(name),
            devices: HashMap::new(),
        }
    }

    fn add_device_from_device_pairs(
        &mut self,
        device_pairs: &mut pest::iterators::Pairs<Rule>,
    ) -> Result<()> {
        let device_id_pair = device_pairs
            .next()
            .ok_or_else(|| anyhow!("No device id found."))?;
        let device_id = u16::from_str_radix(device_id_pair.as_str(), 16)
            .with_context(|| format!("Invalid device: {}", device_id_pair.as_str()))?;
        let device_name = device_pairs
            .next()
            .ok_or_else(|| anyhow!("No device name found."))?
            .as_str();
        let mut pci_device = PciDevice::new(device_id, &device_name);
        for subsystem_pair in device_pairs {
            pci_device.add_subsystem_from_subsystem_pairs(&mut subsystem_pair.into_inner())?;
        }
        self.devices.entry(device_id).or_insert(pci_device);
        Ok(())
    }

    pub fn get_device(&self, device_id: &u16) -> Result<&PciDevice> {
        Ok(self
            .devices
            .get(&device_id)
            .ok_or_else(|| anyhow!("Device {} not found.", device_id))?)
    }
}

impl PartialEq for PciVendor {
    fn eq(&self, other: &Self) -> bool {
        self.id == other.id && self.name == other.name
    }
}

impl Eq for PciVendor {}

#[derive(Debug)]
pub struct PciDevice {
    pub id: u16,
    pub name: String,
    subsystems: HashMap<(u16, u16), PciSubsystem>,
}

impl PciDevice {
    fn new(id: u16, name: &str) -> Self {
        PciDevice {
            id,
            name: String::from(name),
            subsystems: HashMap::new(),
        }
    }

    fn add_subsystem_from_subsystem_pairs(
        &mut self,
        subsystem_pairs: &mut pest::iterators::Pairs<Rule>,
    ) -> Result<()> {
        let subsystem_id_pair = subsystem_pairs
            .next()
            .ok_or_else(|| anyhow!("No subsystem id found."))?;
        let mut subsystem_id_inners = match subsystem_id_pair.as_rule() {
            Rule::subsystem_id => Ok(subsystem_id_pair.into_inner()),
            _ => Err(anyhow!("Tried to add non subsystem to subsystem data.")),
        }?;
        let subvendor_id_pair = subsystem_id_inners
            .next()
            .ok_or_else(|| anyhow!("No subvendor id found."))?;
        let subvendor_id = u16::from_str_radix(subvendor_id_pair.as_str(), 16)?;
        debug!("subvendor_id_pair: {:#?}", &subvendor_id_pair);
        let subdevice_id_pair = subsystem_id_inners
            .next()
            .ok_or_else(|| anyhow!("No subdevice id found."))?;
        let subdevice_id = u16::from_str_radix(subdevice_id_pair.as_str(), 16)?;
        let subsystem_name = subsystem_pairs
            .next()
            .ok_or_else(|| anyhow!("No subsystem name found."))?
            .as_str();
        let pci_subsystem = PciSubsystem::new(subvendor_id, subdevice_id, &subsystem_name);
        self.subsystems
            .entry((subvendor_id, subdevice_id))
            .or_insert(pci_subsystem);
        Ok(())
    }

    pub fn get_subsystem(&self, subsystem_id: &(u16, u16)) -> Result<&PciSubsystem> {
        Ok(self
            .subsystems
            .get(&subsystem_id)
            .ok_or_else(|| anyhow!("Subsystem {} {} not found.", subsystem_id.0, subsystem_id.1))?)
    }
}

impl PartialEq for PciDevice {
    fn eq(&self, other: &Self) -> bool {
        self.id == other.id && self.name == other.name
    }
}

impl Eq for PciDevice {}

#[derive(Debug)]
pub struct PciSubsystem {
    pub subvendor_id: u16,
    pub subdevice_id: u16,
    pub name: String,
}

impl PciSubsystem {
    fn new(subvendor_id: u16, subdevice_id: u16, name: &str) -> Self {
        PciSubsystem {
            subvendor_id,
            subdevice_id,
            name: String::from(name),
        }
    }
}

impl PartialEq for PciSubsystem {
    fn eq(&self, other: &Self) -> bool {
        self.subvendor_id == other.subvendor_id
            && self.subdevice_id == other.subdevice_id
            && self.name == other.name
    }
}

impl Eq for PciSubsystem {}

type PciClasses = HashMap<u8, PciClass>;

#[derive(Debug)]
pub struct PciClass {
    pub id: u8,
    pub name: String,
    subclasses: HashMap<u8, PciSubclass>,
}

impl PciClass {
    fn new(id: u8, name: &str) -> Self {
        PciClass {
            id,
            name: String::from(name),
            subclasses: HashMap::new(),
        }
    }

    fn add_subclass_from_subclass_pairs(
        &mut self,
        subclass_pairs: &mut pest::iterators::Pairs<Rule>,
    ) -> Result<()> {
        let subclass_id_pair = subclass_pairs
            .next()
            .ok_or_else(|| anyhow!("No subclass id found."))?;
        let subclass_id = u8::from_str_radix(subclass_id_pair.as_str(), 16)
            .with_context(|| format!("Invalid subclass: {}", subclass_id_pair.as_str()))?;
        let subclass_name = subclass_pairs
            .next()
            .ok_or_else(|| anyhow!("No subclass name found."))?
            .as_str();
        let mut subclass = PciSubclass::new(subclass_id, &subclass_name);
        for prog_if_pair in subclass_pairs {
            subclass.add_prog_if_from_prog_if_pairs(&mut prog_if_pair.into_inner())?;
        }
        self.subclasses.entry(subclass_id).or_insert(subclass);
        Ok(())
    }

    pub fn get_subclass(&self, subclass_id: &u8) -> Result<&PciSubclass> {
        Ok(self
            .subclasses
            .get(&subclass_id)
            .ok_or_else(|| anyhow!("Subclass {} not found.", subclass_id))?)
    }
}

impl PartialEq for PciClass {
    fn eq(&self, other: &Self) -> bool {
        self.id == other.id && self.name == other.name
    }
}

impl Eq for PciClass {}

#[derive(Debug)]
pub struct PciSubclass {
    pub id: u8,
    pub name: String,
    prog_interfaces: HashMap<u8, PciProgInterface>,
}

impl PciSubclass {
    fn new(id: u8, name: &str) -> Self {
        PciSubclass {
            id,
            name: String::from(name),
            prog_interfaces: HashMap::new(),
        }
    }

    fn add_prog_if_from_prog_if_pairs(
        &mut self,
        prog_if_pairs: &mut pest::iterators::Pairs<Rule>,
    ) -> Result<()> {
        let prog_if_id_pair = prog_if_pairs
            .next()
            .ok_or_else(|| anyhow!("No programming interface id found."))?;
        let prog_if_id = u8::from_str_radix(prog_if_id_pair.as_str(), 16).with_context(|| {
            format!(
                "Invalid programming interface: {}",
                prog_if_id_pair.as_str()
            )
        })?;
        let prog_if_name = prog_if_pairs
            .next()
            .ok_or_else(|| anyhow!("No programming interface name found."))?
            .as_str();
        let prog_if = PciProgInterface::new(prog_if_id, &prog_if_name);
        self.prog_interfaces.entry(prog_if_id).or_insert(prog_if);
        Ok(())
    }

    pub fn get_prog_interface(&self, prog_interface_id: &u8) -> Result<&PciProgInterface> {
        Ok(self
            .prog_interfaces
            .get(&prog_interface_id)
            .ok_or_else(|| anyhow!("Programming interface {} not found.", prog_interface_id))?)
    }
}

impl PartialEq for PciSubclass {
    fn eq(&self, other: &Self) -> bool {
        self.id == other.id && self.name == other.name
    }
}

impl Eq for PciSubclass {}

#[derive(Debug)]
pub struct PciProgInterface {
    pub id: u8,
    pub name: String,
}

impl PciProgInterface {
    fn new(id: u8, name: &str) -> Self {
        PciProgInterface {
            id,
            name: String::from(name),
        }
    }
}

impl PartialEq for PciProgInterface {
    fn eq(&self, other: &Self) -> bool {
        self.id == other.id && self.name == other.name
    }
}

impl Eq for PciProgInterface {}

#[derive(Parser)]
#[grammar = "pciids.pest"]
struct PciIdsParser;

#[cfg(test)]
mod tests {
    use super::*;
    use pest::consumes_to;
    use rand::{Rng, SeedableRng};
    use std::vec::Vec;


    fn hex_char_width<T>() -> usize {
        2 * std::mem::size_of::<T>()
    }

    fn as_max_len_hex_string<T: std::fmt::UpperHex>(n: T) -> String {
        format!("{:0width$X}", n, width = hex_char_width::<T>())
    }

    struct FakeVendorData {
        expected_id: u16,
        expected_id_hex_string: String,
        expected_name: String,
        unparsed_data: String,
        simple_unparsed_data: String,
        devices: Vec<FakeDeviceData>,
    }

    impl FakeVendorData {
        fn new_with_devices(devices: Vec<FakeDeviceData>) -> Self {
            let expected_id = rand::random::<_>();
            let expected_name = format!("Fake vendor ({})", as_max_len_hex_string(expected_id));
            let simple_unparsed_data =
                format!("{} {}", as_max_len_hex_string(expected_id), expected_name);
            let full_unparsed_data = devices
                .iter()
                .map(|d| d.unparsed_data.clone())
                .fold(simple_unparsed_data.clone(), |acc, x| {
                    format!("{}\n{}", acc, x)
                });
            FakeVendorData {
                expected_id: expected_id,
                expected_id_hex_string: as_max_len_hex_string(expected_id),
                expected_name: expected_name.clone(),
                unparsed_data: full_unparsed_data,
                simple_unparsed_data: simple_unparsed_data,
                devices: devices,
            }
        }

        fn new() -> Self {
            Self::new_with_devices(Vec::<_>::new())
        }

        fn check(&self, vendor: &PciVendor) -> Result<()> {
            assert_eq!(vendor.id, self.expected_id);
            assert_eq!(vendor.name, self.expected_name);
            Ok(())
        }
    }

    #[test]
    fn test_vendor_simple_parse() -> Result<()> {
        let fake_vendor_data = FakeVendorData::new();

        println!("Unparsed_data: {:?}", &fake_vendor_data.unparsed_data);

        pest::parses_to! {
            parser: PciIdsParser,
            input: &fake_vendor_data.unparsed_data,
            rule: Rule::vendor,
            tokens: [
                vendor(0, fake_vendor_data.unparsed_data.len(), [
                    vendor_id(0, 4),
                    vendor_name(5, fake_vendor_data.unparsed_data.len())
                ])
            ]
        };

        let mut parsed_data = PciIdsParser::parse(Rule::vendor, &fake_vendor_data.unparsed_data)?;

        let vendor_pair = parsed_data.next().ok_or(anyhow!("No vendor line."))?;
        let mut vendor_inners = match vendor_pair.as_rule() {
            Rule::vendor => Ok(vendor_pair.into_inner()),
            x => Err(anyhow!(format!(
                "Vendor line didn't parse as such. Parsed as {:?}",
                x
            ))),
        }?;
        let end = parsed_data.next();
        assert!(end.is_none(), "Something found after vendor line.");

        let vendor_id_pair = vendor_inners.next().ok_or(anyhow!("No vendor id."))?;
        assert_eq!(
            vendor_id_pair.as_str(),
            fake_vendor_data.expected_id_hex_string,
            "Vendor id doesn't match."
        );
        let vendor_name_pair = vendor_inners.next().ok_or(anyhow!("No vendor name."))?;
        assert_eq!(
            vendor_name_pair.as_str(),
            fake_vendor_data.expected_name,
            "Vendor name doesn't match."
        );
        let end = vendor_inners.next();
        assert!(end.is_none(), "Something found after vendor name.");
        Ok(())
    }

    #[test]
    fn test_vendor_simple_add() -> Result<()> {
        let fake_vendor_data = FakeVendorData::new();

        println!("Unparsed_data: {:?}", &fake_vendor_data.unparsed_data);

        pest::parses_to! {
            parser: PciIdsParser,
            input: &fake_vendor_data.unparsed_data,
            rule: Rule::vendor,
            tokens: [
                vendor(0, fake_vendor_data.unparsed_data.len(), [
                    vendor_id(0, 4),
                    vendor_name(5, fake_vendor_data.unparsed_data.len())
                ])
            ]
        };

        let mut parsed_vendor = PciIdsParser::parse(Rule::vendor, &fake_vendor_data.unparsed_data)?;
        println!("parsed_vendor: {:#?}", &parsed_vendor);
        let vendor_pair = parsed_vendor.next().context("No parsed vendor.")?;
        println!("vendor_pair: {:#?}", &vendor_pair);

        let mut pci_data = PciIdData::new();
        pci_data.add_vendor_from_vendor_pairs(&mut vendor_pair.into_inner())?;

        let vendor = &pci_data.get_vendor(&fake_vendor_data.expected_id)?;
        fake_vendor_data.check(vendor)?;

        Ok(())
    }

    #[test]
    fn test_vendor_complex_add() -> Result<()> {
        let fake_subsystem_data = FakeSubsystemData::new();
        let fake_device_data = FakeDeviceData::new_with_subsystems(vec![fake_subsystem_data]);
        let fake_vendor_data = FakeVendorData::new_with_devices(vec![fake_device_data]);

        println!("Unparsed_data: {:?}", &fake_vendor_data.unparsed_data);

        let vendor_end = fake_vendor_data.simple_unparsed_data.len();
        let device_start = vendor_end + 1;
        println!("{}", device_start);
        let device_end = device_start + fake_vendor_data.devices[0].simple_unparsed_data.len();
        println!("{}", device_end);
        let subsystem_start = device_end + 1;

        pest::parses_to! {
            parser: PciIdsParser,
            input: &fake_vendor_data.unparsed_data,
            rule: Rule::vendor,
            tokens: [
                vendor(0, fake_vendor_data.unparsed_data.len(), [
                    vendor_id(0, 4),
                    vendor_name(5, vendor_end),
                    device(device_start + 0, fake_vendor_data.unparsed_data.len(), [
                        device_id(device_start + 1, device_start + 5),
                        device_name(device_start + 6, device_end),
                        subsystem(subsystem_start, fake_vendor_data.unparsed_data.len(), [
                            subsystem_id(subsystem_start + 2, subsystem_start+11, [
                                subvendor_id(subsystem_start+2, subsystem_start+6),
                                subdevice_id(subsystem_start+7, subsystem_start+11)
                            ]),
                            subsystem_name(subsystem_start+12, fake_vendor_data.unparsed_data.len()),
                        ])
                    ])
                ])
            ]
        };

        let mut parsed_vendor = PciIdsParser::parse(Rule::vendor, &fake_vendor_data.unparsed_data)?;
        println!("parsed_vendor: {:#?}", &parsed_vendor);
        let vendor_pair = parsed_vendor.next().context("No parsed vendor.")?;
        println!("vendor_pair: {:#?}", &vendor_pair);

        let mut pci_data = PciIdData::new();
        pci_data.add_vendor_from_vendor_pairs(&mut vendor_pair.into_inner())?;
        println!("pci_data: {:#?}", pci_data);

        let vendor = &pci_data.get_vendor(&fake_vendor_data.expected_id)?;
        fake_vendor_data.check(&vendor)?;

        let device = &vendor.get_device(&fake_vendor_data.devices[0].expected_id)?;
        fake_vendor_data.devices[0].check(device)?;

        let subsystem = &device.get_subsystem(&(
            fake_vendor_data.devices[0].subsystems[0].expected_subvendor_id,
            fake_vendor_data.devices[0].subsystems[0].expected_subdevice_id,
        ))?;
        fake_vendor_data.devices[0].subsystems[0].check(&subsystem)?;

        Ok(())
    }

    struct FakeDeviceData {
        expected_id: u16,
        expected_id_hex_string: String,
        expected_name: String,
        unparsed_data: String,
        simple_unparsed_data: String,
        subsystems: Vec<FakeSubsystemData>,
    }

    impl FakeDeviceData {
        fn new_with_subsystems(subsystems: Vec<FakeSubsystemData>) -> Self {
            let expected_id = rand::random::<_>();
            let expected_id_hex_string = as_max_len_hex_string(expected_id);
            let expected_name = format!("Fake device ({})", as_max_len_hex_string(expected_id));
            let simple_unparsed_data =
                format!("\t{} {}", as_max_len_hex_string(expected_id), expected_name);
            let full_unparsed_data = subsystems
                .iter()
                .map(|d| d.unparsed_data.clone())
                .fold(simple_unparsed_data.clone(), |acc, x| {
                    format!("{}\n{}", acc, x)
                });
            FakeDeviceData {
                expected_id: expected_id,
                expected_id_hex_string: expected_id_hex_string.clone(),
                expected_name: expected_name.clone(),
                unparsed_data: full_unparsed_data,
                simple_unparsed_data: simple_unparsed_data,
                subsystems: subsystems,
            }
        }

        fn new() -> Self {
            Self::new_with_subsystems(Vec::<_>::new())
        }

        fn check(&self, device: &PciDevice) -> Result<()> {
            assert_eq!(device.id, self.expected_id);
            assert_eq!(device.name, self.expected_name);
            Ok(())
        }
    }

    #[test]
    fn test_device_simple_parse() -> Result<()> {
        let fake_device_data = FakeDeviceData::new();

        println!("Unparsed_data: {:?}", &fake_device_data.unparsed_data);

        pest::parses_to! {
            parser: PciIdsParser,
            input: &fake_device_data.unparsed_data,
            rule: Rule::device,
            tokens: [
                device(0, fake_device_data.unparsed_data.len(), [
                    device_id(1, 5),
                    device_name(6, fake_device_data.unparsed_data.len())
                ])
            ]
        };

        let mut parsed_device = PciIdsParser::parse(Rule::device, &fake_device_data.unparsed_data)?;

        let device_pair = parsed_device.next().ok_or(anyhow!("No device line."))?;
        let mut device_inners = match device_pair.as_rule() {
            Rule::device => Ok(device_pair.into_inner()),
            x => Err(anyhow!(format!(
                "Device line didn't parse as such. Parsed as {:?}",
                x
            ))),
        }?;
        let end = parsed_device.next();
        assert!(end.is_none(), "Something found after device line.");

        let device_id_pair = device_inners.next().ok_or(anyhow!("No device id."))?;
        assert_eq!(
            device_id_pair.as_str(),
            fake_device_data.expected_id_hex_string,
            "Device id doesn't match."
        );
        let device_name_pair = device_inners.next().ok_or(anyhow!("No device name."))?;
        assert_eq!(
            device_name_pair.as_str(),
            fake_device_data.expected_name,
            "Device name doesn't match."
        );
        let end = device_inners.next();
        assert!(end.is_none(), "Something found after device name.");
        Ok(())
    }

    #[test]
    fn test_device_simple_add() -> Result<()> {
        let fake_device_data = FakeDeviceData::new();

        println!("Unparsed_data: {:?}", &fake_device_data.unparsed_data);

        let mut parsed_device = PciIdsParser::parse(Rule::device, &fake_device_data.unparsed_data)?;
        println!("parsed_device: {:#?}", &parsed_device);
        let device_pair = parsed_device.next().context("No parsed device.")?;
        println!("device_pair: {:#?}", &device_pair);

        let mut vendor_data = PciVendor::new(rand::random::<_>(), "Fake vendor");
        vendor_data.add_device_from_device_pairs(&mut device_pair.into_inner())?;
        println!("vendor_data: {:#?}", vendor_data);

        let device_data = &vendor_data.get_device(&fake_device_data.expected_id)?;
        assert_eq!(device_data.id, fake_device_data.expected_id);
        assert_eq!(device_data.name, fake_device_data.expected_name);
        Ok(())
    }

    #[test]
    fn test_device_complex_add() -> Result<()> {
        let fake_subsystem_data = FakeSubsystemData::new();
        let fake_device_data = FakeDeviceData::new_with_subsystems(vec![fake_subsystem_data]);

        println!("Unparsed_data: {:?}", &fake_device_data.unparsed_data);

        let unparsed_device_string_len = fake_device_data.simple_unparsed_data.len();

        pest::parses_to! {
            parser: PciIdsParser,
            input: &fake_device_data.unparsed_data,
            rule: Rule::device,
            tokens: [
                device(0, fake_device_data.unparsed_data.len(), [
                    device_id(1, 5),
                    device_name(6, unparsed_device_string_len),
                    subsystem(unparsed_device_string_len+1, fake_device_data.unparsed_data.len(), [
                        subsystem_id(unparsed_device_string_len + 3, unparsed_device_string_len+12, [
                            subvendor_id(unparsed_device_string_len+3, unparsed_device_string_len+7),
                            subdevice_id(unparsed_device_string_len+8, unparsed_device_string_len+12)
                        ]),
                        subsystem_name(unparsed_device_string_len+13, fake_device_data.unparsed_data.len()),
                    ])
                ])
            ]
        };

        let mut parsed_device = PciIdsParser::parse(Rule::device, &fake_device_data.unparsed_data)?;
        println!("parsed_device: {:#?}", &parsed_device);
        let device_pair = parsed_device.next().context("No parsed device.")?;
        println!("device_pair: {:#?}", &device_pair);

        let mut vendor = PciVendor::new(rand::random::<_>(), "Fake vendor");
        vendor.add_device_from_device_pairs(&mut device_pair.into_inner())?;
        println!("vendor: {:#?}", vendor);

        let device = &vendor.get_device(&fake_device_data.expected_id)?;
        fake_device_data.check(device)?;

        let subsystem = &device.get_subsystem(&(
            fake_device_data.subsystems[0].expected_subvendor_id,
            fake_device_data.subsystems[0].expected_subdevice_id,
        ))?;
        fake_device_data.subsystems[0].check(&subsystem)?;

        Ok(())
    }

    struct FakeSubsystemData {
        expected_subvendor_id: u16,
        expected_subvendor_id_hex_string: String,
        expected_subdevice_id: u16,
        expected_subdevice_id_hex_string: String,
        expected_subsystem_name: String,
        unparsed_data: String,
    }

    impl FakeSubsystemData {
        fn new() -> Self {
            let expected_subvendor_id = rand::random::<_>();
            let expected_subvendor_id_hex_string = as_max_len_hex_string(expected_subvendor_id);
            let expected_subdevice_id = rand::random::<_>();
            let expected_subdevice_id_hex_string = as_max_len_hex_string(expected_subdevice_id);
            let expected_subsystem_name = format!(
                "Fake subsystem ({}:{})",
                as_max_len_hex_string(expected_subvendor_id),
                as_max_len_hex_string(expected_subdevice_id)
            );
            FakeSubsystemData {
                expected_subvendor_id: expected_subvendor_id,
                expected_subvendor_id_hex_string: expected_subvendor_id_hex_string.clone(),
                expected_subdevice_id: expected_subdevice_id,
                expected_subdevice_id_hex_string: expected_subdevice_id_hex_string.clone(),
                expected_subsystem_name: expected_subsystem_name.clone(),
                unparsed_data: format!(
                    "\t\t{} {} {}",
                    expected_subvendor_id_hex_string,
                    expected_subdevice_id_hex_string,
                    expected_subsystem_name
                ),
            }
        }

        fn check(&self, subsystem: &PciSubsystem) -> Result<()> {
            assert_eq!(subsystem.subvendor_id, self.expected_subvendor_id);
            assert_eq!(subsystem.subdevice_id, self.expected_subdevice_id);
            assert_eq!(subsystem.name, self.expected_subsystem_name);
            Ok(())
        }
    }

    #[test]
    fn test_subsystem_simple_parse() -> Result<()> {
        let fake_subsystem_data = FakeSubsystemData::new();

        println!("Unparsed_data: {:?}", &fake_subsystem_data.unparsed_data);

        pest::parses_to! {
            parser: PciIdsParser,
            input: &fake_subsystem_data.unparsed_data,
            rule: Rule::subsystem,
            tokens: [
                subsystem(0, fake_subsystem_data.unparsed_data.len(), [
                    subsystem_id(2, 11, [
                        subvendor_id(2, 6),
                        subdevice_id(7, 11)
                    ]),
                    subsystem_name(12, fake_subsystem_data.unparsed_data.len())
                ])
            ]
        };

        let mut parsed_data =
            PciIdsParser::parse(Rule::subsystem_line, &fake_subsystem_data.unparsed_data)?;

        let subsystem_id_pairs = parsed_data.next().ok_or(anyhow!("No subsystem ids."))?;
        let mut subsystem_id_inners = match subsystem_id_pairs.as_rule() {
            Rule::subsystem_id => Ok(subsystem_id_pairs.into_inner()),
            x => Err(anyhow!(format!(
                "Subsystem ids didn't parse as such. Parsed as {:?}",
                x
            ))),
        }?;

        let subvendor_id_pair = subsystem_id_inners
            .next()
            .ok_or(anyhow!("No subvendor id."))?;
        match subvendor_id_pair.as_rule() {
            Rule::subvendor_id => Ok(()),
            _ => Err(anyhow!(format!(
                "Subvendor id didn't parse as such. Parsed as {:?}",
                subvendor_id_pair.as_rule()
            ))),
        }?;

        let subdevice_id_pair = subsystem_id_inners
            .next()
            .ok_or(anyhow!("No subdevice id."))?;
        match subdevice_id_pair.as_rule() {
            Rule::subdevice_id => Ok(()),
            _ => Err(anyhow!(format!(
                "Subdevice id didn't parse as such. Parsed as {:?}",
                subdevice_id_pair.as_rule()
            ))),
        }?;

        let end = subsystem_id_inners.next();
        assert!(end.is_none(), "Something found after subsystem ids.");

        let subsystem_name_pair = parsed_data.next().ok_or(anyhow!("No subsystem name."))?;
        match subsystem_name_pair.as_rule() {
            Rule::subsystem_name => Ok(()),
            _ => Err(anyhow!(format!(
                "Subvendor name didn't parse as such. Parsed as {:?}",
                subsystem_name_pair.as_rule()
            ))),
        }?;

        let end = parsed_data.next();
        assert!(end.is_none(), "Something found after subsystem name.");

        assert_eq!(
            subvendor_id_pair.as_str(),
            fake_subsystem_data.expected_subvendor_id_hex_string,
            "Subvendor id doesn't match."
        );
        assert_eq!(
            subdevice_id_pair.as_str(),
            fake_subsystem_data.expected_subdevice_id_hex_string,
            "Subdevice id doesn't match."
        );
        assert_eq!(
            subsystem_name_pair.as_str(),
            fake_subsystem_data.expected_subsystem_name,
            "Subsystem name doesn't match."
        );
        Ok(())
    }

    #[test]
    fn test_subsystem_simple_add() -> Result<()> {
        let fake_subsystem_data = FakeSubsystemData::new();

        println!("Unparsed_data: {:?}", &fake_subsystem_data.unparsed_data);

        let mut parsed_subsystem =
            PciIdsParser::parse(Rule::subsystem, &fake_subsystem_data.unparsed_data)?;
        println!("parsed_subsystem: {:#?}", &parsed_subsystem);
        let subsystem_pair = parsed_subsystem.next().context("No parsed subsystem.")?;
        println!("parsed_subsystem: {:#?}", &subsystem_pair);

        let mut device = PciDevice::new(rand::random::<_>(), "Fake device");
        device.add_subsystem_from_subsystem_pairs(&mut subsystem_pair.into_inner())?;
        println!("{:#?}", &device);

        let subsystem = &device.get_subsystem(&(
            fake_subsystem_data.expected_subvendor_id,
            fake_subsystem_data.expected_subdevice_id,
        ))?;
        fake_subsystem_data.check(&subsystem)?;

        Ok(())
    }

    struct FakeClassData {
        expected_id: u8,
        expected_id_hex_string: String,
        expected_name: String,
        unparsed_data: String,
        simple_unparsed_data: String,
        subclasses: Vec<FakeSubclassData>,
    }

    impl FakeClassData {
        fn new_with_subclasses(subclasses: Vec<FakeSubclassData>) -> Self {
            let expected_id = rand::random::<_>();
            let expected_id_hex_string = as_max_len_hex_string(expected_id);
            let expected_name = format!("Fake class ({})", as_max_len_hex_string(expected_id));
            let simple_unparsed_data = format!("C {} {}", expected_id_hex_string, expected_name);
            let full_unparsed_data = subclasses
                .iter()
                .map(|d| d.unparsed_data.clone())
                .fold(simple_unparsed_data.clone(), |acc, x| {
                    format!("{}\n{}", acc, x)
                });
            FakeClassData {
                expected_id: expected_id,
                expected_id_hex_string: expected_id_hex_string.clone(),
                expected_name: expected_name.clone(),
                unparsed_data: full_unparsed_data,
                simple_unparsed_data: simple_unparsed_data,
                subclasses: subclasses,
            }
        }

        fn new() -> Self {
            Self::new_with_subclasses(Vec::<_>::new())
        }

        fn check(&self, class: &PciClass) -> Result<()> {
            assert_eq!(class.id, self.expected_id);
            assert_eq!(class.name, self.expected_name);
            Ok(())
        }
    }

    #[test]
    fn test_class_simple_parse() -> Result<()> {
        let fake_class_data = FakeClassData::new();

        println!("Unparsed_data: {:?}", &fake_class_data.unparsed_data);

        pest::parses_to! {
            parser: PciIdsParser,
            input: &fake_class_data.unparsed_data,
            rule: Rule::class,
            tokens: [
                class(0, fake_class_data.unparsed_data.len(), [
                    class_id(2, 4),
                    class_name(5, fake_class_data.unparsed_data.len())
                ])
            ]
        };

        let mut parsed_data = PciIdsParser::parse(Rule::class, &fake_class_data.unparsed_data)?;

        let class_pair = parsed_data.next().ok_or(anyhow!("No class line."))?;
        let mut class_inners = match class_pair.as_rule() {
            Rule::class => Ok(class_pair.into_inner()),
            x => Err(anyhow!(format!(
                "Class line didn't parse as such. Parsed as {:?}",
                x
            ))),
        }?;
        let end = parsed_data.next();
        assert!(end.is_none(), "Something found after class line.");

        let class_id_pair = class_inners.next().ok_or(anyhow!("No class id."))?;
        assert_eq!(
            class_id_pair.as_str(),
            fake_class_data.expected_id_hex_string,
            "Class id doesn't match."
        );
        let class_name_pair = class_inners.next().ok_or(anyhow!("No class name."))?;
        assert_eq!(
            class_name_pair.as_str(),
            fake_class_data.expected_name,
            "Class name doesn't match."
        );
        let end = class_inners.next();
        assert!(end.is_none(), "Something found after class name.");
        Ok(())
    }

    #[test]
    fn test_class_simple_add() -> Result<()> {
        let fake_class_data = FakeClassData::new();

        println!("Unparsed_data: {:?}", &fake_class_data.unparsed_data);

        pest::parses_to! {
            parser: PciIdsParser,
            input: &fake_class_data.unparsed_data,
            rule: Rule::class,
            tokens: [
                class(0, fake_class_data.unparsed_data.len(), [
                    class_id(2, 4),
                    class_name(5, fake_class_data.unparsed_data.len())
                ])
            ]
        };

        let mut parsed_class = PciIdsParser::parse(Rule::class, &fake_class_data.unparsed_data)?;
        println!("parsed_class: {:#?}", &parsed_class);
        let class_pair = parsed_class.next().context("No parsed class.")?;
        println!("class_pair: {:#?}", &class_pair);

        let mut pci_data = PciIdData::new();
        pci_data.add_class_from_class_pairs(&mut class_pair.into_inner())?;

        let class = &pci_data.get_class(&fake_class_data.expected_id)?;
        fake_class_data.check(class)?;

        Ok(())
    }

    #[test]
    fn test_class_complex_add() -> Result<()> {
        let fake_prog_if_data = FakeProgIfaceData::new();
        let fake_subclass_data = FakeSubclassData::new_with_prog_ifs(vec![fake_prog_if_data]);
        let fake_class_data = FakeClassData::new_with_subclasses(vec![fake_subclass_data]);

        println!("Unparsed_data: {:?}", &fake_class_data.unparsed_data);

        let class_end = fake_class_data.simple_unparsed_data.len();
        let subclass_start = class_end + 1;
        println!("{}", subclass_start);
        let subclass_end =
            subclass_start + fake_class_data.subclasses[0].simple_unparsed_data.len();
        println!("{}", subclass_end);
        let prog_if_start = subclass_end + 1;

        pest::parses_to! {
            parser: PciIdsParser,
            input: &fake_class_data.unparsed_data,
            rule: Rule::class,
            tokens: [
                class(0, fake_class_data.unparsed_data.len(), [
                    class_id(2, 4),
                    class_name(5, class_end),
                    subclass(subclass_start + 0, fake_class_data.unparsed_data.len(), [
                        subclass_id(subclass_start + 1, subclass_start + 3),
                        subclass_name(subclass_start + 4, subclass_end),
                        prog_if(prog_if_start, fake_class_data.unparsed_data.len(), [
                            prog_if_id(prog_if_start+2, prog_if_start+4),
                            prog_if_name(prog_if_start+5, fake_class_data.unparsed_data.len()),
                        ])
                    ])
                ])
            ]
        };

        let mut parsed_class = PciIdsParser::parse(Rule::class, &fake_class_data.unparsed_data)?;
        println!("parsed_class: {:#?}", &parsed_class);
        let class_pair = parsed_class.next().context("No parsed class.")?;
        println!("class_pair: {:#?}", &class_pair);

        let mut pci_data = PciIdData::new();
        pci_data.add_class_from_class_pairs(&mut class_pair.into_inner())?;
        println!("pci_data: {:#?}", pci_data);

        let class = &pci_data.get_class(&fake_class_data.expected_id)?;
        fake_class_data.check(&class)?;

        let subclass = &class.get_subclass(&fake_class_data.subclasses[0].expected_id)?;
        fake_class_data.subclasses[0].check(subclass)?;

        let prog_if =
            &subclass.get_prog_interface(&fake_class_data.subclasses[0].prog_ifs[0].expected_id)?;
        fake_class_data.subclasses[0].prog_ifs[0].check(&prog_if)?;

        Ok(())
    }

    struct FakeSubclassData {
        expected_id: u8,
        expected_id_hex_string: String,
        expected_name: String,
        unparsed_data: String,
        simple_unparsed_data: String,
        prog_ifs: Vec<FakeProgIfaceData>,
    }

    impl FakeSubclassData {
        fn new_with_prog_ifs(prog_ifs: Vec<FakeProgIfaceData>) -> Self {
            let expected_id = rand::random::<_>();
            let expected_id_hex_string = as_max_len_hex_string(expected_id);
            let expected_name = format!("Fake subclass ({})", as_max_len_hex_string(expected_id));
            let simple_unparsed_data = format!("\t{} {}", expected_id_hex_string, expected_name);
            let full_unparsed_data = prog_ifs
                .iter()
                .map(|d| d.unparsed_data.clone())
                .fold(simple_unparsed_data.clone(), |acc, x| {
                    format!("{}\n{}", acc, x)
                });
            FakeSubclassData {
                expected_id: expected_id,
                expected_id_hex_string: expected_id_hex_string.clone(),
                expected_name: expected_name.clone(),
                unparsed_data: full_unparsed_data,
                simple_unparsed_data: simple_unparsed_data,
                prog_ifs: prog_ifs,
            }
        }

        fn new() -> Self {
            Self::new_with_prog_ifs(Vec::<_>::new())
        }

        fn check(&self, subclass: &PciSubclass) -> Result<()> {
            assert_eq!(subclass.id, self.expected_id);
            assert_eq!(subclass.name, self.expected_name);
            Ok(())
        }
    }

    #[test]
    fn test_subclass_simple_parse() -> Result<()> {
        let fake_subclass_data = FakeSubclassData::new();

        println!("Unparsed_data: {:?}", &fake_subclass_data.unparsed_data);

        pest::parses_to! {
            parser: PciIdsParser,
            input: &fake_subclass_data.unparsed_data,
            rule: Rule::subclass,
            tokens: [
                subclass(0, fake_subclass_data.unparsed_data.len(), [
                    subclass_id(1, 3),
                    subclass_name(4, fake_subclass_data.unparsed_data.len())
                ])
            ]
        };

        let mut parsed_data =
            PciIdsParser::parse(Rule::subclass, &fake_subclass_data.unparsed_data)?;

        let subclass_pair = parsed_data.next().ok_or(anyhow!("No subclass line."))?;
        let mut subclass_inners = match subclass_pair.as_rule() {
            Rule::subclass => Ok(subclass_pair.into_inner()),
            x => Err(anyhow!(format!(
                "Subclass line didn't parse as such. Parsed as {:?}",
                x
            ))),
        }?;
        let end = parsed_data.next();
        assert!(end.is_none(), "Something found after subclass line.");

        let subclass_id_pair = subclass_inners.next().ok_or(anyhow!("No subclass id."))?;
        assert_eq!(
            subclass_id_pair.as_str(),
            fake_subclass_data.expected_id_hex_string,
            "Subclass id doesn't match."
        );
        let subclass_name_pair = subclass_inners.next().ok_or(anyhow!("No subclass name."))?;
        assert_eq!(
            subclass_name_pair.as_str(),
            fake_subclass_data.expected_name,
            "Subclass name doesn't match."
        );
        let end = subclass_inners.next();
        assert!(end.is_none(), "Something found after subclass name.");
        Ok(())
    }

    #[test]
    fn test_subclass_simple_add() -> Result<()> {
        let fake_subclass_data = FakeSubclassData::new();

        println!("Unparsed_data: {:?}", &fake_subclass_data.unparsed_data);

        let mut parsed_subclass =
            PciIdsParser::parse(Rule::subclass, &fake_subclass_data.unparsed_data)?;
        println!("parsed_subclass: {:#?}", &parsed_subclass);
        let subclass_pair = parsed_subclass.next().context("No parsed class")?;
        println!("parsed_subclass: {:#?}", &subclass_pair);

        let mut class = PciClass::new(rand::random::<_>(), "Fake class");
        class.add_subclass_from_subclass_pairs(&mut subclass_pair.into_inner())?;
        println!("{:#?}", &class);

        let subclass = &class.get_subclass(&fake_subclass_data.expected_id)?;
        fake_subclass_data.check(&subclass)?;

        Ok(())
    }

    #[test]
    fn test_subclass_complex_add() -> Result<()> {
        let fake_prog_if_data = FakeProgIfaceData::new();
        let fake_subclass_data = FakeSubclassData::new_with_prog_ifs(vec![fake_prog_if_data]);

        println!(
            "Unparsed_data: {:?}",
            &fake_subclass_data.simple_unparsed_data
        );

        let unparsed_subclass_string_len = fake_subclass_data.simple_unparsed_data.len();

        println!("{}", unparsed_subclass_string_len);

        pest::parses_to! {
            parser: PciIdsParser,
            input: &fake_subclass_data.unparsed_data,
            rule: Rule::subclass,
            tokens: [
                subclass(0, fake_subclass_data.unparsed_data.len(), [
                    subclass_id(1, 3),
                    subclass_name(4, unparsed_subclass_string_len),
                    prog_if(unparsed_subclass_string_len+1, fake_subclass_data.unparsed_data.len(), [
                        prog_if_id(unparsed_subclass_string_len+3, unparsed_subclass_string_len+5),
                        prog_if_name(unparsed_subclass_string_len+6, fake_subclass_data.unparsed_data.len()),
                    ])
                ])
            ]
        };

        let mut parsed_subclass =
            PciIdsParser::parse(Rule::subclass, &fake_subclass_data.unparsed_data)?;
        println!("parsed_subclass: {:#?}", &parsed_subclass);
        let subclass_pair = parsed_subclass.next().context("No parsed subclass.")?;
        println!("subclass_pair: {:#?}", &subclass_pair);

        let mut class = PciClass::new(rand::random::<_>(), "Fake class");
        class.add_subclass_from_subclass_pairs(&mut subclass_pair.into_inner())?;
        println!("class: {:#?}", class);

        let subclass = &class.get_subclass(&fake_subclass_data.expected_id)?;
        fake_subclass_data.check(subclass)?;

        Ok(())
    }

    struct FakeProgIfaceData {
        expected_id: u8,
        expected_id_hex_string: String,
        expected_name: String,
        unparsed_data: String,
    }

    impl FakeProgIfaceData {
        fn new() -> Self {
            let expected_id = rand::random::<_>();
            let expected_id_hex_string = as_max_len_hex_string(expected_id);
            let expected_name = format!(
                "Fake programming interface ({})",
                as_max_len_hex_string(expected_id)
            );
            FakeProgIfaceData {
                expected_id: expected_id,
                expected_id_hex_string: expected_id_hex_string.clone(),
                expected_name: expected_name.clone(),
                unparsed_data: format!("\t\t{} {}", expected_id_hex_string, expected_name),
            }
        }

        fn check(&self, class: &PciProgInterface) -> Result<()> {
            assert_eq!(class.id, self.expected_id);
            assert_eq!(class.name, self.expected_name);
            Ok(())
        }
    }

    #[test]
    fn test_prog_if_simple_parse() -> Result<()> {
        let fake_prog_if_data = FakeProgIfaceData::new();

        println!("Unparsed_data: {:?}", &fake_prog_if_data.unparsed_data);

        pest::parses_to! {
            parser: PciIdsParser,
            input: &fake_prog_if_data.unparsed_data,
            rule: Rule::prog_if,
            tokens: [
                prog_if(0, fake_prog_if_data.unparsed_data.len(), [
                    prog_if_id(2, 4),
                    prog_if_name(5, fake_prog_if_data.unparsed_data.len())
                ])
            ]
        };

        let mut parsed_data = PciIdsParser::parse(Rule::prog_if, &fake_prog_if_data.unparsed_data)?;

        let prog_if_pair = parsed_data.next().ok_or(anyhow!("No prog_if line."))?;
        let mut prog_if_inners = match prog_if_pair.as_rule() {
            Rule::prog_if => Ok(prog_if_pair.into_inner()),
            x => Err(anyhow!(format!(
                "Subclass line didn't parse as such. Parsed as {:?}",
                x
            ))),
        }?;
        let end = parsed_data.next();
        assert!(end.is_none(), "Something found after prog_if line.");

        let prog_if_id_pair = prog_if_inners.next().ok_or(anyhow!("No prog_if id."))?;
        assert_eq!(
            prog_if_id_pair.as_str(),
            fake_prog_if_data.expected_id_hex_string,
            "Subclass id doesn't match."
        );
        let prog_if_name_pair = prog_if_inners.next().ok_or(anyhow!("No prog_if name."))?;
        assert_eq!(
            prog_if_name_pair.as_str(),
            fake_prog_if_data.expected_name,
            "Subclass name doesn't match."
        );
        let end = prog_if_inners.next();
        assert!(end.is_none(), "Something found after prog_if name.");
        Ok(())
    }

    #[test]
    fn test_prog_if_simple_add() -> Result<()> {
        let fake_prog_if_data = FakeProgIfaceData::new();

        println!("Unparsed_data: {:?}", &fake_prog_if_data.unparsed_data);

        let mut parsed_prog_if =
            PciIdsParser::parse(Rule::prog_if, &fake_prog_if_data.unparsed_data)?;
        println!("parsed_prog_if: {:#?}", &parsed_prog_if);
        let prog_if_pair = parsed_prog_if
            .next()
            .context("No parsed programming interface")?;
        println!("parsed_prog_if: {:#?}", &prog_if_pair);

        let mut subclass = PciSubclass::new(rand::random::<_>(), "Fake subclass");
        subclass.add_prog_if_from_prog_if_pairs(&mut prog_if_pair.into_inner())?;
        println!("{:#?}", &subclass);

        let prog_if = &subclass.get_prog_interface(&fake_prog_if_data.expected_id)?;
        fake_prog_if_data.check(&prog_if)?;

        Ok(())
    }

    #[test]
    fn test_full_parse() -> Result<()> {
        let vendors = vec![
            FakeVendorData::new_with_devices(vec![FakeDeviceData::new_with_subsystems(vec![
                FakeSubsystemData::new(),
                FakeSubsystemData::new(),
            ])]),
            FakeVendorData::new_with_devices(vec![FakeDeviceData::new()]),
            FakeVendorData::new(),
        ];

        let classes = vec![
            FakeClassData::new_with_subclasses(vec![FakeSubclassData::new_with_prog_ifs(vec![
                FakeProgIfaceData::new(),
                FakeProgIfaceData::new(),
            ])]),
            FakeClassData::new_with_subclasses(vec![FakeSubclassData::new()]),
            FakeClassData::new(),
        ];

        let vendor_data = vendors
            .iter()
            .map(|d| d.unparsed_data.clone())
            .fold(String::new(), |acc, x| format!("{}\n{}", acc, x));
        let class_data = classes
            .iter()
            .map(|d| d.unparsed_data.clone())
            .fold(String::new(), |acc, x| format!("{}\n{}", acc, x));
        let unparsed_data = vendor_data + &class_data;

        println!("Unparsed_data: {:?}", unparsed_data);

        let mut pci_data = PciIdData::new();
        pci_data.add_pci_ids_data(&mut unparsed_data.as_bytes())?;
        println!("{:#?}", &pci_data);

        for vendor in vendors {
            let pci_vendor = pci_data.get_vendor(&vendor.expected_id).context(format!(
                "Vendor didn't parse correctly: {}",
                vendor.simple_unparsed_data
            ))?;
            for device in vendor.devices {
                let pci_device = pci_vendor.get_device(&device.expected_id).context(format!(
                    "Device didn't parse correctly: {}",
                    device.simple_unparsed_data
                ))?;
                for subsystem in device.subsystems {
                    let _pci_subsystem = pci_device
                        .get_subsystem(&(
                            subsystem.expected_subvendor_id,
                            subsystem.expected_subdevice_id,
                        ))
                        .context(format!(
                            "Subsystem didn't parse correctly: {}",
                            subsystem.unparsed_data
                        ))?;
                }
            }
        }

        for class in classes {
            let pci_class = pci_data.get_class(&class.expected_id).context(format!(
                "Class didn't parse correctly: {}",
                class.simple_unparsed_data
            ))?;
            for subclass in class.subclasses {
                let pci_subclass =
                    pci_class
                        .get_subclass(&subclass.expected_id)
                        .context(format!(
                            "Subclass didn't parse correctly: {}",
                            subclass.simple_unparsed_data
                        ))?;
                for prog_if in subclass.prog_ifs {
                    let _pci_prog_interface = pci_subclass
                        .get_prog_interface(&prog_if.expected_id)
                        .context(format!(
                            "Programming interface didn't parse correctly: {}",
                            prog_if.unparsed_data
                        ))?;
                }
            }
        }

        Ok(())
    }

    #[bench]
    fn test_benchmark_complex_load(b: &mut test::Bencher) -> Result<()> {
        let mut s = String::new();

        let mut rng = rand::rngs::StdRng::seed_from_u64(0);

        s.push_str("# preamble comment\n");
        s.push('\n');
        s.push_str("# subpreamble comment\n");
        for i in 0..(u16::MAX / 5 - 1) {
            s.push_str("# vendor comment\n");
            s.push_str(format!("{:02$X}  Vendor {:02$X}\n", i, i, 4).as_str());
            let num_devices: u16 = rng.gen_range(0, 3);
            for j in 0..num_devices {
                let num_subsystems: u16 = rng.gen_range(0, 5);
                s.push_str("# device comment\n");
                s.push_str(format!("\t{:02$X}  Device {:02$X}\n", j, j, 4).as_str());
                for k in 0..num_subsystems {
                    s.push_str("# subsystem comment\n");
                    s.push_str(
                        format!(
                            "\t\t{:04$X} {:04$X}  Subsystem {:04$X}:{:04$X}\n",
                            k, k, k, k, 4
                        )
                        .as_str(),
                    );
                }
            }
        }
        s.push_str("FFFF  Invalid Vendor\n");
        s.push('\n');
        for i in 0..(u8::MAX - 1) {
            s.push_str("# class comment\n");
            s.push_str(format!("C {:02$X}  Class {:02$X}\n", i, i, 2).as_str());
            let num_subclasses: u8 = rng.gen_range(1, 3);
            for j in 0..num_subclasses {
                let num_prog_ifs: u8 = rng.gen_range(0, 5);
                s.push_str("# subclass comment\n");
                s.push_str(format!("\t{:02$X}  Subclass {:02$X}\n", j, j, 2).as_str());
                for k in 0..num_prog_ifs {
                    s.push_str("# Programming interface comment\n");
                    s.push_str(
                        format!("\t\t{:02$X}  Programming interfaces {:02$X}\n", k, k, 2).as_str(),
                    );
                }
            }
        }
        s.push_str("FFFF  Unassigned class\n");

        b.iter(|| {
            let mut pci_data = PciIdData::new();
            pci_data.add_pci_ids_data(&mut s.as_bytes()).unwrap()
        });

        Ok(())
    }
}