lspci/
filter.rs

1// Copyright 2020 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4use crate::device;
5use anyhow::anyhow;
6use std::{fmt, str};
7
8/// Provides a way to filter Devices to those specified by the user's preference.
9#[derive(Copy, Clone, Debug, Default, PartialEq)]
10pub struct Filter {
11    pub bus: u8,
12    pub dev: Option<u8>,
13    pub func: Option<u8>,
14}
15
16impl Filter {
17    pub fn matches(&self, device: &device::Device<'_>) -> bool {
18        if device.device.bus_id != self.bus {
19            return false;
20        }
21
22        if let Some(dev) = &self.dev {
23            if device.device.device_id != *dev {
24                return false;
25            }
26        }
27
28        if let Some(func) = &self.func {
29            if device.device.function_id != *func {
30                return false;
31            }
32        }
33
34        true
35    }
36}
37
38impl std::fmt::Display for Filter {
39    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
40        write!(f, "{:02x}:", self.bus)?;
41        if let Some(dev) = self.dev {
42            write!(f, "{:02x}:", dev)?;
43        } else {
44            write!(f, "**:")?;
45        }
46        if let Some(func) = self.func {
47            write!(f, "{:1x}:", func)
48        } else {
49            write!(f, "*")
50        }
51    }
52}
53
54impl str::FromStr for Filter {
55    type Err = anyhow::Error;
56
57    fn from_str(s: &str) -> Result<Self, Self::Err> {
58        // The expected format is BB:DD.F. The filter is permitted to omit
59        // trailing sections as long as the sections provided are complete.
60        //
61        // ex: BB is fine, as is BB:DD, but BB:D is not.
62        let vs: Vec<char> = s.chars().collect();
63        if s.len() == 1 || s.len() == 4 || s.len() == 6 {
64            return Err(anyhow!("filter is incomplete."));
65        }
66
67        if s.len() >= 3 && vs[2] != ':' {
68            return Err(anyhow!(format!("expected ':', but found '{}' in position {}", vs[2], 2)));
69        }
70
71        if s.len() >= 6 && vs[5] != '.' {
72            return Err(anyhow!(format!("expected '.', but found '{}' in position {}", vs[5], 5)));
73        }
74
75        if s.len() > 7 {
76            return Err(anyhow!("filter is too long."));
77        }
78
79        let bus = u8::from_str_radix(&s[0..2], 16)?;
80        let dev = if s.len() >= 5 { Some(u8::from_str_radix(&s[3..5], 16)?) } else { None };
81        let func = if s.len() == 7 { Some(u8::from_str_radix(&s[6..7], 16)?) } else { None };
82
83        Ok(Filter { bus, dev, func })
84    }
85}