pcitool 0.1.0

Tool and library for portable access to PCI bus configuration registres.
Documentation
use std::{collections::HashMap, io, iter, path::PathBuf};

use thiserror::Error;

use crate::device::{address::ParseAddressError, Address, Device};

pub mod dump;
use dump::{Dump, DumpError};

pub mod linux_procfs;
use linux_procfs::LinuxProcfs;

pub mod linux_sysfs;
use linux_sysfs::LinuxSysfs;

#[derive(Debug, Error)]
pub enum AccessError {
    #[error("No addressed device {0}")]
    NoAddress(Address),
    #[error("address '{address}' parse problem: {source}")]
    ParseAddress {
        address: String,
        source: ParseAddressError,
    },
    #[error("This method unavailable on this platform")]
    Platform,
    #[error("{path}: {source}")]
    File { path: PathBuf, source: io::Error },
    #[error("unable to parse configuration space data")]
    ConfigurationSpace,
    #[error(transparent)]
    Dump(#[from] DumpError),
    #[error("linux-sysfs access {0}")]
    LinuxSysfs(#[from] linux_sysfs::LinuxSysfsError),
}

impl PartialEq for AccessError {
    fn eq(&self, other: &Self) -> bool {
        match (self, other) {
            (
                Self::File {
                    path: self_path,
                    source: self_source,
                },
                Self::File {
                    path: other_path,
                    source: other_source,
                },
            ) => self_path.eq(other_path) && self_source.kind().eq(&other_source.kind()),
            (s, o) => s.eq(o),
        }
    }
}

impl Eq for AccessError {}

pub type Result<T> = core::result::Result<T, AccessError>;
pub type Slots = HashMap<Address, String>;

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Access {
    Void(Void),
    Dump(Dump),
    LinuxSysfs(LinuxSysfs),
    LinuxProcfs(LinuxProcfs),
}

impl Access {
    pub fn init() -> Result<Self> {
        LinuxSysfs::default()
            .access()
            .or_else(|_| LinuxProcfs::init(LinuxProcfs::PATH).map(Into::into))
            .or_else(|_| Void::init().map(Into::into))
    }
    pub fn device(&self, addr: Address) -> Result<Device> {
        match self {
            Self::Void(a) => a.device(addr),
            Self::Dump(a) => a.device(addr),
            Self::LinuxSysfs(a) => a.device(addr),
            Self::LinuxProcfs(a) => a.device(addr),
        }
    }
    pub fn scan(&self) -> Box<dyn Iterator<Item = Result<Address>> + '_> {
        match self {
            Self::Void(a) => Box::new(a.scan()),
            Self::Dump(a) => Box::new(a.scan()),
            Self::LinuxSysfs(a) => Box::new(a.scan()),
            Self::LinuxProcfs(a) => Box::new(a.scan()),
        }
    }
    pub fn iter(&self) -> Box<dyn Iterator<Item = Result<Device>> + '_> {
        match self {
            Self::Void(a) => Box::new(a.iter()),
            Self::Dump(a) => Box::new(a.iter()),
            Self::LinuxSysfs(a) => Box::new(a.iter()),
            Self::LinuxProcfs(a) => Box::new(a.iter()),
        }
    }
    pub fn vital_product_data(&self, addr: Address) -> io::Result<Vec<u8>> {
        match self {
            Self::Void(a) => a.vital_product_data(addr),
            Self::Dump(a) => a.vital_product_data(addr),
            Self::LinuxSysfs(a) => a.vital_product_data(addr),
            Self::LinuxProcfs(a) => a.vital_product_data(addr),
        }
    }
}

impl Default for Access {
    fn default() -> Self {
        Self::Void(Void)
    }
}

impl From<Void> for Access {
    fn from(a: Void) -> Self {
        Self::Void(a)
    }
}

impl From<Dump> for Access {
    fn from(a: Dump) -> Self {
        Self::Dump(a)
    }
}

impl From<LinuxSysfs> for Access {
    fn from(a: LinuxSysfs) -> Self {
        Self::LinuxSysfs(a)
    }
}

impl From<LinuxProcfs> for Access {
    fn from(a: LinuxProcfs) -> Self {
        Self::LinuxProcfs(a)
    }
}

pub trait AccessMethod<'a> {
    type Scan: Iterator<Item = Result<Address>>;
    type Iter: Iterator<Item = Result<Device>>;
    fn device(&'a self, addr: Address) -> Result<Device> {
        self.iter()
            .find_map(|result| {
                result
                    .ok()
                    .filter(|Device { address, .. }| address == &addr)
            })
            .ok_or(AccessError::NoAddress(addr))
    }
    fn scan(&'a self) -> Self::Scan;
    fn iter(&'a self) -> Self::Iter;
    fn vital_product_data(&'a self, _: Address) -> io::Result<Vec<u8>> {
        Err(io::ErrorKind::Other.into())
    }
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Void;

impl Void {
    pub fn init() -> Result<Self> {
        Ok(Self)
    }
}

impl<'a> AccessMethod<'a> for Void {
    type Scan = iter::Empty<Result<Address>>;
    type Iter = iter::Empty<Result<Device>>;
    fn scan(&'a self) -> Self::Scan {
        iter::empty()
    }
    fn iter(&'a self) -> Self::Iter {
        iter::empty()
    }
}