1#![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#[derive(Debug, Clone, Copy, PartialEq, Eq)]
28pub struct Bus(pub(crate) sensors_bus_id);
29
30impl Bus {
31 pub fn name(&self) -> Result<&str> {
35 self.raw_name()?.to_str().map_err(Into::into)
36 }
37
38 #[must_use]
40 pub fn kind(&self) -> Option<Kind> {
41 Kind::from_raw(self.raw_kind())
42 }
43
44 #[must_use]
46 pub fn number(&self) -> Number {
47 self.raw_number().into()
48 }
49
50 pub fn raw_name(&self) -> Result<&CStr> {
56 let name = {
57 let _guard = API_ACCESS_LOCK.lock();
58 unsafe { sensors_get_adapter_name(&self.0) }
60 };
61
62 (!name.is_null())
63 .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 #[must_use]
74 pub fn raw_kind(&self) -> c_short {
75 self.0.type_
76 }
77
78 #[must_use]
81 pub fn raw_number(&self) -> c_short {
82 self.0.nr
83 }
84
85 pub fn set_kind(&mut self, kind: Kind) {
87 self.set_raw_kind(c_short::from(kind));
88 }
89
90 pub fn set_number(&mut self, number: Number) {
92 self.set_raw_number(number.into());
93 }
94
95 pub fn set_raw_kind(&mut self, kind: c_short) {
98 self.0.type_ = kind;
99 }
100
101 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#[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 = SENSORS_BUS_TYPE_ANY as c_short,
147 I2C = SENSORS_BUS_TYPE_I2C as c_short,
149 ISA = SENSORS_BUS_TYPE_ISA as c_short,
151 PCI = SENSORS_BUS_TYPE_PCI as c_short,
153 SPI = SENSORS_BUS_TYPE_SPI as c_short,
155 Virtual = SENSORS_BUS_TYPE_VIRTUAL as c_short,
157 ACPI = SENSORS_BUS_TYPE_ACPI as c_short,
159 HID = SENSORS_BUS_TYPE_HID as c_short,
161 MDIO = SENSORS_BUS_TYPE_MDIO as c_short,
163 SCSI = SENSORS_BUS_TYPE_SCSI as c_short,
165}
166
167impl Kind {
168 #[must_use]
171 pub fn from_raw(kind: c_short) -> Option<Self> {
172 Self::try_from(kind).ok()
173 }
174
175 #[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#[non_exhaustive]
208#[derive(Debug, Clone, Copy, PartialEq, Eq)]
209pub enum Number {
210 Any,
212 Ignore,
214 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}