From 7464d055a7f1c1d50b4eb4fe25340656c3b05943 Mon Sep 17 00:00:00 2001 From: David Gibson Date: Fri, 30 Oct 2020 16:22:46 +1100 Subject: [PATCH] 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 --- src/agent/src/pci.rs | 82 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 81 insertions(+), 1 deletion(-) diff --git a/src/agent/src/pci.rs b/src/agent/src/pci.rs index 7f85e53949..176d90c678 100644 --- a/src/agent/src/pci.rs +++ b/src/agent/src/pci.rs @@ -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); + +impl Path { + pub fn new(slots: Vec) -> anyhow::Result { + 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 = 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 { + let rslots: anyhow::Result> = 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()); + } }