lm_sensors/
feature.rs

1//! Sensor or actuator (*a.k.a.,* feature) controlled by a chip.
2
3#[cfg(test)]
4mod tests;
5
6use alloc::ffi::CString;
7use core::ffi::CStr;
8use core::ffi::{c_int, c_uint};
9use core::fmt;
10use std::io;
11
12use sensors_sys::sensors_feature_type::{
13    SENSORS_FEATURE_BEEP_ENABLE, SENSORS_FEATURE_CURR, SENSORS_FEATURE_ENERGY, SENSORS_FEATURE_FAN,
14    SENSORS_FEATURE_HUMIDITY, SENSORS_FEATURE_IN, SENSORS_FEATURE_INTRUSION, SENSORS_FEATURE_POWER,
15    SENSORS_FEATURE_TEMP, SENSORS_FEATURE_UNKNOWN, SENSORS_FEATURE_VID,
16};
17use sensors_sys::{
18    sensors_feature, sensors_get_features, sensors_get_label, sensors_get_subfeature,
19};
20
21use crate::chip::ChipRef;
22use crate::errors::{Error, Result};
23use crate::sub_feature::SubFeatureRef;
24use crate::utils::API_ACCESS_LOCK;
25
26/// Shared reference to a feature of some [`Kind`] (*e.g.,* temperature),
27/// provided by a [`Chip`].
28///
29/// [`Kind`]: crate::feature::Kind
30#[derive(Debug, Clone, Copy, Eq)]
31pub struct FeatureRef<'sensors> {
32    pub(crate) chip: ChipRef<'sensors>,
33    pub(crate) raw: &'sensors sensors_feature,
34}
35
36impl<'sensors> FeatureRef<'sensors> {
37    /// Returns a shared reference to the raw data structure [`sensors_feature`].
38    #[must_use]
39    pub fn raw_ref(self) -> &'sensors sensors_feature {
40        self.raw
41    }
42
43    /// Return the chip controlling this feature.
44    #[must_use]
45    pub fn chip(self) -> ChipRef<'sensors> {
46        self.chip
47    }
48
49    /// Return the name of this feature, if it is valid UTF-8.
50    #[must_use]
51    pub fn name(self) -> Option<Result<&'sensors str>> {
52        self.raw_name()
53            .map(|name| name.to_str().map_err(Into::into))
54    }
55
56    /// Return the label of this feature, if it is valid UTF-8.
57    pub fn label(self) -> Result<String> {
58        self.raw_label()?.into_string().map_err(Into::into)
59    }
60
61    /// Return the number of this feature.
62    #[must_use]
63    pub fn number(self) -> c_int {
64        self.raw.number
65    }
66
67    /// Return the type of this feature, if it is a valid [`Kind`].
68    #[must_use]
69    pub fn kind(self) -> Option<Kind> {
70        Kind::from_raw(self.raw_kind())
71    }
72
73    /// Return the sub-feature of the given type for a given main feature,
74    /// if it exists, or an error otherwise.
75    pub fn sub_feature_by_kind(self, kind: crate::value::Kind) -> Result<SubFeatureRef<'sensors>> {
76        self.sub_feature_by_raw_kind(c_uint::from(kind))
77    }
78
79    /// Return an iterator which yields all sub-features belonging
80    /// to this feature.
81    pub fn sub_feature_iter(self) -> crate::sub_feature::Iter<'sensors> {
82        crate::sub_feature::Iter {
83            feature: self,
84            state: 0,
85        }
86    }
87
88    /// Return the raw name of this feature, if available.
89    #[must_use]
90    pub fn raw_name(self) -> Option<&'sensors CStr> {
91        // Safety: if `name` is not null, then it is assumed to be a null-terminated string.
92        (!self.raw.name.is_null()).then(|| unsafe { CStr::from_ptr(self.raw.name) })
93    }
94
95    /// Return the raw label of this feature.
96    ///
97    /// If no label exists for this feature, its name is returned.
98    ///
99    /// See: [`sensors_get_label`].
100    pub fn raw_label(self) -> Result<CString> {
101        let label = {
102            let _guard = API_ACCESS_LOCK.lock();
103            // Safety: sensors_get_label() is assumed to be safe.
104            unsafe { sensors_get_label(self.chip.raw_ref(), self.raw) }
105        };
106
107        if label.is_null() {
108            let err = io::ErrorKind::InvalidInput.into();
109            Err(Error::from_io("sensors_get_label()", err))
110        } else {
111            // Safety: sensors_get_label() initialized `label`.
112            Ok(unsafe {
113                let result = CString::from(CStr::from_ptr(label));
114                libc::free(label.cast());
115                result
116            })
117        }
118    }
119
120    /// Return the raw type of this feature, which is one of `SENSORS_FEATURE_*`,
121    /// *e.g.*, [`SENSORS_FEATURE_TEMP`].
122    ///
123    /// [`SENSORS_FEATURE_TEMP`]: sensors_feature_type::SENSORS_FEATURE_TEMP
124    #[must_use]
125    pub fn raw_kind(self) -> c_uint {
126        self.raw.type_
127    }
128
129    /// Return the sub-feature of the given type for a given main feature,
130    /// if it exists, or an error otherwise.
131    ///
132    /// `kind` is one of `SENSORS_SUBFEATURE_*`.
133    ///
134    /// See: [`sensors_get_subfeature`].
135    pub fn sub_feature_by_raw_kind(self, kind: c_uint) -> Result<SubFeatureRef<'sensors>> {
136        let sub_feature = {
137            let _guard = API_ACCESS_LOCK.lock();
138            // Safety: sensors_get_subfeature() is assumed to be safe.
139            unsafe { sensors_get_subfeature(self.chip.raw_ref(), self.raw, kind).as_ref() }
140        };
141
142        sub_feature
143            .map(|raw| SubFeatureRef { feature: self, raw })
144            .ok_or_else(|| {
145                let err = io::ErrorKind::NotFound.into();
146                Error::from_io("sensors_get_subfeature", err)
147            })
148    }
149}
150
151impl PartialEq for FeatureRef<'_> {
152    fn eq(&self, other: &Self) -> bool {
153        self.chip == other.chip
154            && self.number() == other.number()
155            && self.raw_kind() == other.raw_kind()
156            && self.raw_name() == other.raw_name()
157    }
158}
159
160impl fmt::Display for FeatureRef<'_> {
161    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
162        if let Ok(label) = self.raw_label() {
163            write!(f, "{}", label.to_string_lossy())
164        } else {
165            write!(f, "\u{fffd}")
166        }
167    }
168}
169
170/// Type of a sensor or actuator (*a.k.a.,* feature) controlled by a chip.
171#[allow(missing_docs)] // Enum variant names are self-explanatory.
172#[repr(u32)]
173#[non_exhaustive]
174#[derive(
175    Debug,
176    Clone,
177    Copy,
178    PartialEq,
179    Eq,
180    PartialOrd,
181    Ord,
182    num_enum::TryFromPrimitive,
183    num_enum::IntoPrimitive,
184)]
185pub enum Kind {
186    Voltage = SENSORS_FEATURE_IN,
187    Fan = SENSORS_FEATURE_FAN,
188    Temperature = SENSORS_FEATURE_TEMP,
189    Power = SENSORS_FEATURE_POWER,
190    Energy = SENSORS_FEATURE_ENERGY,
191    Current = SENSORS_FEATURE_CURR,
192    Humidity = SENSORS_FEATURE_HUMIDITY,
193    //MaximumMain = SENSORS_FEATURE_MAX_MAIN,
194    VoltageID = SENSORS_FEATURE_VID,
195    Intrusion = SENSORS_FEATURE_INTRUSION,
196    //MaximumOther = SENSORS_FEATURE_MAX_OTHER,
197    BeepEnable = SENSORS_FEATURE_BEEP_ENABLE,
198    // Maximum = SENSORS_FEATURE_MAX,
199    Unknown = SENSORS_FEATURE_UNKNOWN,
200}
201
202impl Kind {
203    /// Return an instance from one of the `SENSORS_FEATURE_*` values,
204    /// *e.g.,* [`SENSORS_FEATURE_TEMP`].
205    #[must_use]
206    pub fn from_raw(kind: c_uint) -> Option<Self> {
207        Self::try_from(kind).ok()
208    }
209
210    /// Return one of the `SENSORS_FEATURE_*` values
211    /// (*e.g.,* [`SENSORS_FEATURE_TEMP`]) equivalent to this instance.
212    #[must_use]
213    pub fn as_raw(self) -> c_uint {
214        self.into()
215    }
216}
217
218impl Default for Kind {
219    fn default() -> Self {
220        Self::Unknown
221    }
222}
223
224impl fmt::Display for Kind {
225    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
226        match *self {
227            Self::Voltage => write!(f, "Voltage"),
228            Self::Fan => write!(f, "Fan"),
229            Self::Temperature => write!(f, "Temperature"),
230            Self::Power => write!(f, "Power"),
231            Self::Energy => write!(f, "Energy"),
232            Self::Current => write!(f, "Current"),
233            Self::Humidity => write!(f, "Humidity"),
234            Self::VoltageID => write!(f, "VoltageID"),
235            Self::Intrusion => write!(f, "Intrusion"),
236            Self::BeepEnable => write!(f, "BeepEnable"),
237            Self::Unknown => write!(f, "Unknown"),
238        }
239    }
240}
241
242/// Iterator over available features of a chip. Yields [`FeatureRef`]s.
243#[derive(Debug)]
244#[must_use]
245pub struct Iter<'sensors> {
246    pub(crate) chip: ChipRef<'sensors>,
247    pub(crate) state: c_int,
248}
249
250impl<'sensors> Iterator for Iter<'sensors> {
251    type Item = FeatureRef<'sensors>;
252
253    /// See: [`sensors_get_features`].
254    fn next(&mut self) -> Option<Self::Item> {
255        let feature = {
256            let _guard = API_ACCESS_LOCK.lock();
257            // Safety: sensors_get_features() is assumed to be safe.
258            unsafe { sensors_get_features(self.chip.raw_ref(), &mut self.state).as_ref() }
259        };
260
261        feature.map(|raw| FeatureRef {
262            chip: self.chip,
263            raw,
264        })
265    }
266}