agent: PCI path type

Introduce a Rust type to represent a "PCI path" - that is a way of
locating a PCI device from a given root by listing the slots of all
the bridges leading to it and finally the slot of the device itself.

It's implemented as a vector of the previously added pci::Slot type,
and includes the necessary validation and conversions to/from strings.

Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
This commit is contained in:
David Gibson 2020-10-30 16:22:46 +11:00
parent b22259ad9b
commit 7464d055a7

View File

@ -4,6 +4,7 @@
//
use std::convert::TryInto;
use std::fmt;
use std::ops::Deref;
use std::str::FromStr;
use anyhow::anyhow;
@ -48,9 +49,50 @@ impl fmt::Display for Slot {
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Path(Vec<Slot>);
impl Path {
pub fn new(slots: Vec<Slot>) -> anyhow::Result<Self> {
if slots.is_empty() {
return Err(anyhow!("PCI path must have at least one element"));
}
Ok(Path(slots))
}
}
// Let Path be treated as a slice of Slots
impl Deref for Path {
type Target = [Slot];
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl fmt::Display for Path {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
let sslots: Vec<String> = self
.0
.iter()
.map(std::string::ToString::to_string)
.collect();
write!(f, "{}", sslots.join("/"))
}
}
impl FromStr for Path {
type Err = anyhow::Error;
fn from_str(s: &str) -> anyhow::Result<Self> {
let rslots: anyhow::Result<Vec<Slot>> = s.split('/').map(Slot::from_str).collect();
Path::new(rslots?)
}
}
#[cfg(test)]
mod tests {
use crate::pci::Slot;
use crate::pci::{Path, Slot};
use std::str::FromStr;
#[test]
@ -85,4 +127,42 @@ mod tests {
let slot = Slot::from_str("");
assert!(slot.is_err());
}
#[test]
fn test_path() {
let slot3 = Slot::new(0x03).unwrap();
let slot4 = Slot::new(0x04).unwrap();
let slot5 = Slot::new(0x05).unwrap();
// Valid paths
let pcipath = Path::new(vec![slot3]).unwrap();
assert_eq!(format!("{}", pcipath), "03");
let pcipath2 = Path::from_str("03").unwrap();
assert_eq!(pcipath, pcipath2);
assert_eq!(pcipath.len(), 1);
assert_eq!(pcipath[0], slot3);
let pcipath = Path::new(vec![slot3, slot4]).unwrap();
assert_eq!(format!("{}", pcipath), "03/04");
let pcipath2 = Path::from_str("03/04").unwrap();
assert_eq!(pcipath, pcipath2);
assert_eq!(pcipath.len(), 2);
assert_eq!(pcipath[0], slot3);
assert_eq!(pcipath[1], slot4);
let pcipath = Path::new(vec![slot3, slot4, slot5]).unwrap();
assert_eq!(format!("{}", pcipath), "03/04/05");
let pcipath2 = Path::from_str("03/04/05").unwrap();
assert_eq!(pcipath, pcipath2);
assert_eq!(pcipath.len(), 3);
assert_eq!(pcipath[0], slot3);
assert_eq!(pcipath[1], slot4);
assert_eq!(pcipath[2], slot5);
// Bad paths
assert!(Path::new(vec!()).is_err());
assert!(Path::from_str("20").is_err());
assert!(Path::from_str("//").is_err());
assert!(Path::from_str("xyz").is_err());
}
}