ev3dev_lang_rust/
driver.rs

1//! Helper struct that manages attributes.
2//! It creates an `Attribute` instance if it does not exists or uses a cached one.
3
4use std::cell::RefCell;
5use std::collections::HashMap;
6use std::fmt::{self, Debug};
7use std::fs;
8use std::path::Path;
9use std::string::String;
10
11use crate::utils::OrErr;
12use crate::{Attribute, Ev3Error, Ev3Result, Port};
13
14/// The driver path `/sys/class/`.
15#[cfg(not(feature = "override-driver-path"))]
16pub const DRIVER_PATH: &str = "/sys/class/";
17
18/// The driver path that was set with the env variable `EV3DEV_DRIVER_PATH` (default value: `/sys/class/`).
19#[cfg(feature = "override-driver-path")]
20pub const DRIVER_PATH: &str = get_driver_path();
21
22#[cfg(feature = "override-driver-path")]
23const fn get_driver_path() -> &'static str {
24    let path = std::option_env!("EV3DEV_DRIVER_PATH");
25    if let Some(path) = path {
26        path
27    } else {
28        "/sys/class/"
29    }
30}
31
32/// Helper struct that manages attributes.
33/// It creates an `Attribute` instance if it does not exists or uses a cached one.
34#[derive(Clone)]
35pub struct Driver {
36    class_name: String,
37    name: String,
38    attributes: RefCell<HashMap<String, Attribute>>,
39}
40
41impl Driver {
42    /// Returns a new `Driver`.
43    /// All attributes created by this driver will use the path `/sys/class/{class_name}/{name}`.
44    pub fn new(class_name: &str, name: &str) -> Driver {
45        Driver {
46            class_name: class_name.to_owned(),
47            name: name.to_owned(),
48            attributes: RefCell::new(HashMap::new()),
49        }
50    }
51
52    /// Returns the name of the device with the given `class_name`, `driver_name` and at the given `port`.
53    ///
54    /// Returns `Ev3Error::NotFound` if no such device exists.
55    pub fn find_name_by_port_and_driver(
56        class_name: &str,
57        port: &dyn Port,
58        driver_name_vec: &[&str],
59    ) -> Ev3Result<String> {
60        let port_address = port.address();
61
62        let paths = fs::read_dir(Path::new(DRIVER_PATH).join(class_name))?;
63
64        for path in paths {
65            let file_name = path?.file_name();
66            let name = file_name.to_str().or_err()?;
67
68            let address = Attribute::from_sys_class(class_name, name, "address")?;
69
70            if address.get::<String>()?.contains(&port_address) {
71                let driver = Attribute::from_sys_class(class_name, name, "driver_name")?;
72                let driver_name = driver.get::<String>()?;
73                if driver_name_vec.iter().any(|n| &driver_name == n) {
74                    return Ok(name.to_owned());
75                }
76            }
77        }
78
79        Err(Ev3Error::NotConnected {
80            device: format!("{driver_name_vec:?}"),
81            port: Some(port_address),
82        })
83    }
84
85    /// Returns the name of the device with the given `class_name`.
86    ///
87    /// Returns `Ev3Error::NotFound` if no such device exists.
88    /// Returns `Ev3Error::MultipleMatches` if more then one matching device exists.
89    pub fn find_name_by_driver(class_name: &str, driver_name_vec: &[&str]) -> Ev3Result<String> {
90        let mut names = Driver::find_names_by_driver(class_name, driver_name_vec)?;
91
92        match names.len() {
93            0 => Err(Ev3Error::NotConnected {
94                device: format!("{driver_name_vec:?}"),
95                port: None,
96            }),
97            1 => Ok(names
98                .pop()
99                .expect("Name vector should contains exactly one element")),
100            _ => Err(Ev3Error::MultipleMatches {
101                device: format!("{driver_name_vec:?}"),
102                ports: names,
103            }),
104        }
105    }
106
107    /// Returns the names of the devices with the given `class_name`.
108    pub fn find_names_by_driver(
109        class_name: &str,
110        driver_name_vec: &[&str],
111    ) -> Ev3Result<Vec<String>> {
112        let paths = fs::read_dir(Path::new(DRIVER_PATH).join(class_name))?;
113
114        let mut found_names = Vec::new();
115        for path in paths {
116            let file_name = path?.file_name();
117            let name = file_name.to_str().or_err()?;
118
119            let driver = Attribute::from_sys_class(class_name, name, "driver_name")?;
120
121            let driver_name = driver.get::<String>()?;
122            if driver_name_vec.iter().any(|n| &driver_name == n) {
123                found_names.push(name.to_owned());
124            }
125        }
126
127        Ok(found_names)
128    }
129
130    /// Return the `Attribute` wrapper for the given `attribute_name`.
131    /// Creates a new one if it does not exist.
132    pub fn get_attribute(&self, attribute_name: &str) -> Attribute {
133        let mut attributes = self.attributes.borrow_mut();
134
135        if !attributes.contains_key(attribute_name) {
136            if let Ok(v) = Attribute::from_sys_class(
137                self.class_name.as_ref(),
138                self.name.as_ref(),
139                attribute_name,
140            ) {
141                attributes.insert(attribute_name.to_owned(), v);
142            };
143        };
144
145        attributes
146            .get(attribute_name)
147            .expect("Internal error in the attribute map")
148            .clone()
149    }
150}
151
152impl Debug for Driver {
153    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
154        write!(
155            f,
156            "Driver {{ class_name: {}, name: {} }}",
157            self.class_name, self.name
158        )
159    }
160}