lm_sensors/
sub_feature.rs

1//! Data reported by a sensor or set for an actuator (*a.k.a.,* sub-feature)
2//! controlled by a chip.
3
4#[cfg(test)]
5mod tests;
6
7use core::ffi::CStr;
8use core::ffi::{c_int, c_uint};
9use core::fmt;
10use std::io;
11
12use bitflags::bitflags;
13use sensors_sys::{
14    SENSORS_COMPUTE_MAPPING, SENSORS_MODE_R, SENSORS_MODE_W, sensors_get_all_subfeatures,
15    sensors_get_value, sensors_set_value, sensors_subfeature,
16};
17
18use crate::errors::{Error, Result};
19use crate::feature::FeatureRef;
20use crate::utils::API_ACCESS_LOCK;
21use crate::value::{Kind, Value};
22
23/// Shared reference to a sub-feature of some [`Kind`] (*e.g.,* temperature input),
24/// provided by a [`Chip`].
25///
26/// [`Kind`]: crate::value::Kind
27#[derive(Debug, Clone, Copy, Eq)]
28pub struct SubFeatureRef<'sensors> {
29    pub(crate) feature: FeatureRef<'sensors>,
30    pub(crate) raw: &'sensors sensors_subfeature,
31}
32
33impl<'sensors> SubFeatureRef<'sensors> {
34    /// Returns a shared reference to the raw data structure [`sensors_subfeature`].
35    #[must_use]
36    pub fn raw_ref(self) -> &'sensors sensors_subfeature {
37        self.raw
38    }
39
40    /// Return the feature to which this sub-feature belongs.
41    #[must_use]
42    pub fn feature(self) -> FeatureRef<'sensors> {
43        self.feature
44    }
45
46    /// Return the name of this sub-feature, if available and valid UTF-8.
47    ///
48    /// This returns `None` if no name is available, and returns `Some(Err(_))`
49    /// if the available name is not 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 number of this sub-feature.
57    #[must_use]
58    pub fn number(self) -> c_int {
59        self.raw.number
60    }
61
62    /// Return the number of a main feature this sub-feature belongs to.
63    #[must_use]
64    pub fn mapping(self) -> c_int {
65        self.raw.mapping
66    }
67
68    /// Return the type of this sub-feature, if it is valid [`Kind`].
69    #[must_use]
70    pub fn kind(self) -> Option<Kind> {
71        Kind::from_raw(self.raw_kind())
72    }
73
74    /// Return the flags of this sub-feature, if it is valid [`Flags`].
75    #[must_use]
76    pub fn flags(self) -> Option<Flags> {
77        Flags::from_bits(self.raw_flags())
78    }
79
80    /// Return the value reported by this sub-feature, *e.g.,* sensor.
81    pub fn value(self) -> Result<Value> {
82        let value = self.raw_value()?;
83        Value::from_raw(self.raw_kind(), value)
84            .ok_or_else(|| Error::from_io("Value::from_raw", io::ErrorKind::InvalidData.into()))
85    }
86
87    /// Set the value associated with this sub-feature, *e.g.,* actuator.
88    pub fn set_value(self, new_value: &Value) -> Result<()> {
89        self.set_raw_value(new_value.raw_value())
90    }
91
92    /// Return the raw name of this sub-feature, if available.
93    #[must_use]
94    pub fn raw_name(self) -> Option<&'sensors CStr> {
95        // Safety: if `name` is not null, then it is assumed to be a null-terminated string.
96        (!self.raw.name.is_null()).then(|| unsafe { CStr::from_ptr(self.raw.name) })
97    }
98
99    /// Return the raw type of this sub-feature, which is one
100    /// of `SENSORS_SUBFEATURE_*`, *e.g.*, [`SENSORS_SUBFEATURE_TEMP_INPUT`].
101    ///
102    /// [`SENSORS_SUBFEATURE_TEMP_INPUT`]: sensors_subfeature_type::SENSORS_SUBFEATURE_TEMP_INPUT
103    #[must_use]
104    pub fn raw_kind(self) -> c_uint {
105        self.raw.type_
106    }
107
108    /// Return the raw flags of this sub-feature, which is a combination
109    /// of [`SENSORS_MODE_R`], [`SENSORS_MODE_W`] and [`SENSORS_COMPUTE_MAPPING`].
110    #[must_use]
111    pub fn raw_flags(self) -> c_uint {
112        self.raw.flags
113    }
114
115    /// Return the raw value reported by this sub-feature, *e.g.,* sensor.
116    ///
117    /// See: [`sensors_get_value`].
118    pub fn raw_value(self) -> Result<f64> {
119        let mut result = 0.0_f64;
120
121        let r = {
122            let _guard = API_ACCESS_LOCK.lock();
123            // Safety: `result` was properly initialized.
124            unsafe { sensors_get_value(self.feature.chip.raw_ref(), self.number(), &mut result) }
125        };
126
127        if r == 0 {
128            Ok(result)
129        } else {
130            Err(Error::from_lm_sensors("sensors_get_value()", r))
131        }
132    }
133
134    /// Set the raw value associated with this sub-feature, *e.g.,* actuator.
135    ///
136    /// See: [`sensors_set_value`].
137    pub fn set_raw_value(self, new_value: f64) -> Result<()> {
138        let chip = self.feature.chip.raw_ref();
139        let number = self.number();
140        let r = {
141            let _guard = API_ACCESS_LOCK.lock();
142            // Safety: sensors_set_value() is assumed to be safe.
143            unsafe { sensors_set_value(chip, number, new_value) }
144        };
145
146        if r == 0 {
147            Ok(())
148        } else {
149            Err(Error::from_lm_sensors("sensors_set_value()", r))
150        }
151    }
152}
153
154impl PartialEq for SubFeatureRef<'_> {
155    fn eq(&self, other: &Self) -> bool {
156        self.feature == other.feature
157            && self.number() == other.number()
158            && self.mapping() == other.mapping()
159            && self.raw_kind() == other.raw_kind()
160            && self.raw_flags() == other.raw_flags()
161            && self.raw_name() == other.raw_name()
162    }
163}
164
165impl fmt::Display for SubFeatureRef<'_> {
166    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
167        if let Some(name) = self.raw_name() {
168            write!(f, "{}", name.to_string_lossy())
169        } else {
170            Ok(())
171        }
172    }
173}
174
175bitflags! {
176    /// Flags of a sub-feature of a chip.
177    #[repr(transparent)]
178    pub struct Flags: c_uint {
179        /// Sub-feature is readable, *e.g.,* sensor data.
180        #[allow(clippy::as_conversions)]
181        const READABLE = SENSORS_MODE_R as c_uint;
182
183        /// Sub-feature is writable, *e.g.,* actuator value.
184        #[allow(clippy::as_conversions)]
185        const WRITABLE = SENSORS_MODE_W as c_uint;
186
187        /// Sub-feature value is affected by the computation rules of
188        /// the main feature.
189        #[allow(clippy::as_conversions)]
190        const COMPUTE_MAPPING = SENSORS_COMPUTE_MAPPING as c_uint;
191    }
192}
193
194/// Iterator over available sub-features of a chip. Yields [`SubFeatureRef`]s.
195#[derive(Debug)]
196#[must_use]
197pub struct Iter<'sensors> {
198    pub(crate) feature: FeatureRef<'sensors>,
199    pub(crate) state: c_int,
200}
201
202impl<'sensors> Iterator for Iter<'sensors> {
203    type Item = SubFeatureRef<'sensors>;
204
205    /// See: [`sensors_get_all_subfeatures`].
206    fn next(&mut self) -> Option<Self::Item> {
207        let subfeature = {
208            let _guard = API_ACCESS_LOCK.lock();
209            // Safety: sensors_get_all_subfeatures() is assumed to be safe.
210            unsafe {
211                sensors_get_all_subfeatures(
212                    self.feature.chip.raw_ref(),
213                    self.feature.raw,
214                    &mut self.state,
215                )
216                .as_ref()
217            }
218        };
219
220        subfeature.map(|raw| SubFeatureRef {
221            feature: self.feature,
222            raw,
223        })
224    }
225}