lm_sensors/
bus.rs

1//! Bus connections.
2
3// This is a workaround because of a bug applying this lint/allow to `enum Kind` only.
4#![allow(clippy::cast_possible_truncation, clippy::as_conversions)]
5
6#[cfg(test)]
7mod tests;
8
9use core::ffi::CStr;
10use core::ffi::{c_int, c_short};
11use core::fmt;
12use std::io;
13
14use sensors_sys::{
15    SENSORS_BUS_NR_ANY, SENSORS_BUS_NR_IGNORE, SENSORS_BUS_TYPE_ACPI, SENSORS_BUS_TYPE_ANY,
16    SENSORS_BUS_TYPE_HID, SENSORS_BUS_TYPE_I2C, SENSORS_BUS_TYPE_ISA, SENSORS_BUS_TYPE_MDIO,
17    SENSORS_BUS_TYPE_PCI, SENSORS_BUS_TYPE_SCSI, SENSORS_BUS_TYPE_SPI, SENSORS_BUS_TYPE_VIRTUAL,
18    sensors_bus_id, sensors_get_adapter_name,
19};
20
21use crate::errors::{Error, Result};
22use crate::utils::API_ACCESS_LOCK;
23
24/// Bus connection of some [`Kind`], *e.g.,* PCI.
25///
26/// [`Kind`]: crate::bus::Kind
27#[derive(Debug, Clone, Copy, PartialEq, Eq)]
28pub struct Bus(pub(crate) sensors_bus_id);
29
30impl Bus {
31    /// Return the adapter name of this bus.
32    ///
33    /// If it could not be found, it returns an error.
34    pub fn name(&self) -> Result<&str> {
35        self.raw_name()?.to_str().map_err(Into::into)
36    }
37
38    /// Return the bus type.
39    #[must_use]
40    pub fn kind(&self) -> Option<Kind> {
41        Kind::from_raw(self.raw_kind())
42    }
43
44    /// Return the bus number.
45    #[must_use]
46    pub fn number(&self) -> Number {
47        self.raw_number().into()
48    }
49
50    /// Return the adapter name of this bus *(type, number)* pair, as used
51    /// within [`sensors_chip_name`].
52    /// If it could not be found, it returns an error.
53    ///
54    /// See: [`sensors_get_adapter_name`].
55    pub fn raw_name(&self) -> Result<&CStr> {
56        let name = {
57            let _guard = API_ACCESS_LOCK.lock();
58            // Safety: this is assumed to be safe.
59            unsafe { sensors_get_adapter_name(&self.0) }
60        };
61
62        (!name.is_null())
63            // Safety: sensors_get_adapter_name() returned a null-terminated string.
64            .then(|| unsafe { CStr::from_ptr(name) })
65            .ok_or_else(|| {
66                let err = io::ErrorKind::NotFound.into();
67                Error::from_io("sensors_get_adapter_name", err)
68            })
69    }
70
71    /// Return one of `SENSORS_BUS_TYPE_*` values,
72    /// *e.g.*, [`SENSORS_BUS_TYPE_ANY`].
73    #[must_use]
74    pub fn raw_kind(&self) -> c_short {
75        self.0.type_
76    }
77
78    /// Return a number, or one of `SENSORS_BUS_NR_*` values,
79    /// *e.g.*, [`SENSORS_BUS_NR_ANY`].
80    #[must_use]
81    pub fn raw_number(&self) -> c_short {
82        self.0.nr
83    }
84
85    /// Set the bus type.
86    pub fn set_kind(&mut self, kind: Kind) {
87        self.set_raw_kind(c_short::from(kind));
88    }
89
90    /// Set the bus number.
91    pub fn set_number(&mut self, number: Number) {
92        self.set_raw_number(number.into());
93    }
94
95    /// Set the bus type to one of `SENSORS_BUS_TYPE_*` values,
96    /// *e.g.*, [`SENSORS_BUS_TYPE_PCI`].
97    pub fn set_raw_kind(&mut self, kind: c_short) {
98        self.0.type_ = kind;
99    }
100
101    /// Set the bus number to one of `SENSORS_BUS_NR_*` values
102    /// (*e.g.*, [`SENSORS_BUS_NR_ANY`]), or to a specific number.
103    pub fn set_raw_number(&mut self, number: c_short) {
104        self.0.nr = number;
105    }
106}
107
108impl AsMut<sensors_bus_id> for Bus {
109    fn as_mut(&mut self) -> &mut sensors_bus_id {
110        &mut self.0
111    }
112}
113
114impl AsRef<sensors_bus_id> for Bus {
115    fn as_ref(&self) -> &sensors_bus_id {
116        &self.0
117    }
118}
119
120impl fmt::Display for Bus {
121    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
122        if let Ok(name) = self.raw_name() {
123            write!(f, "{}", name.to_string_lossy())
124        } else {
125            write!(f, "\u{fffd}")
126        }
127    }
128}
129
130/// Type of a [`Bus`].
131#[repr(i16)]
132#[non_exhaustive]
133#[derive(
134    Debug,
135    Clone,
136    Copy,
137    PartialEq,
138    Eq,
139    PartialOrd,
140    Ord,
141    num_enum::TryFromPrimitive,
142    num_enum::IntoPrimitive,
143)]
144pub enum Kind {
145    /// Any type of bus.
146    Any = SENSORS_BUS_TYPE_ANY as c_short,
147    /// Inter-Integrated Circuit.
148    I2C = SENSORS_BUS_TYPE_I2C as c_short,
149    /// Industry Standard Architecture.
150    ISA = SENSORS_BUS_TYPE_ISA as c_short,
151    /// Peripheral Component Interconnect.
152    PCI = SENSORS_BUS_TYPE_PCI as c_short,
153    /// Serial Peripheral Interface.
154    SPI = SENSORS_BUS_TYPE_SPI as c_short,
155    /// Virtual bus.
156    Virtual = SENSORS_BUS_TYPE_VIRTUAL as c_short,
157    /// Advanced Configuration and Power Interface.
158    ACPI = SENSORS_BUS_TYPE_ACPI as c_short,
159    /// Human Interface Device.
160    HID = SENSORS_BUS_TYPE_HID as c_short,
161    /// Management Data Input/Output.
162    MDIO = SENSORS_BUS_TYPE_MDIO as c_short,
163    /// Small Computer System Interface.
164    SCSI = SENSORS_BUS_TYPE_SCSI as c_short,
165}
166
167impl Kind {
168    /// Return an instance from one of the `SENSORS_BUS_TYPE_*` values,
169    /// *e.g.,* [`SENSORS_BUS_TYPE_PCI`].
170    #[must_use]
171    pub fn from_raw(kind: c_short) -> Option<Self> {
172        Self::try_from(kind).ok()
173    }
174
175    /// Return one of the `SENSORS_BUS_TYPE_*` values
176    /// (*e.g.,* [`SENSORS_BUS_TYPE_PCI`]) equivalent to this instance.
177    #[must_use]
178    pub fn as_raw(self) -> c_short {
179        self.into()
180    }
181}
182
183impl Default for Kind {
184    fn default() -> Self {
185        Self::Any
186    }
187}
188
189impl fmt::Display for Kind {
190    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
191        match *self {
192            Self::Any => write!(f, "Any"),
193            Self::I2C => write!(f, "Inter-Integrated Circuit (I2C)"),
194            Self::ISA => write!(f, "Industry Standard Architecture (ISA)"),
195            Self::PCI => write!(f, "Peripheral Component Interconnect (PCI)"),
196            Self::SPI => write!(f, "Serial Peripheral Interface (SPI)"),
197            Self::Virtual => write!(f, "Virtual"),
198            Self::ACPI => write!(f, "Advanced Configuration and Power Interface (ACPI)"),
199            Self::HID => write!(f, "Human Interface Device (HID)"),
200            Self::MDIO => write!(f, "Management Data Input/Output (MDIO)"),
201            Self::SCSI => write!(f, "Small Computer System Interface (SCSI)"),
202        }
203    }
204}
205
206/// Number of a [`Bus`].
207#[non_exhaustive]
208#[derive(Debug, Clone, Copy, PartialEq, Eq)]
209pub enum Number {
210    /// A bus of any number.
211    Any,
212    /// Ignored bus number.
213    Ignore,
214    /// Bus of a specific number.
215    Number(c_short),
216}
217
218impl Default for Number {
219    fn default() -> Self {
220        Self::Any
221    }
222}
223
224impl From<c_short> for Number {
225    fn from(other: c_short) -> Self {
226        match c_int::from(other) {
227            SENSORS_BUS_NR_ANY => Number::Any,
228            SENSORS_BUS_NR_IGNORE => Number::Ignore,
229            _ => Number::Number(other),
230        }
231    }
232}
233
234impl From<Number> for c_short {
235    fn from(other: Number) -> Self {
236        #[expect(clippy::cast_possible_truncation, clippy::as_conversions)]
237        match other {
238            Number::Any => SENSORS_BUS_NR_ANY as Self,
239            Number::Ignore => SENSORS_BUS_NR_IGNORE as Self,
240            Number::Number(n) => n,
241        }
242    }
243}
244
245impl fmt::Display for Number {
246    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
247        match *self {
248            Number::Any => write!(f, "Any"),
249            Number::Ignore => write!(f, "Ignore"),
250            Number::Number(n) => write!(f, "{n}"),
251        }
252    }
253}