1#[cfg(test)]
4mod tests;
5
6use alloc::ffi::CString;
7use core::ffi::CStr;
8use core::ffi::{c_int, c_uint};
9use core::marker::PhantomData;
10use core::mem::MaybeUninit;
11use core::{fmt, mem, ptr};
12use std::ffi::OsStr;
13use std::io;
14use std::path::Path;
15
16use sensors_sys::{
17 SENSORS_CHIP_NAME_ADDR_ANY, sensors_chip_name, sensors_do_chip_sets, sensors_free_chip_name,
18 sensors_get_detected_chips, sensors_parse_chip_name, sensors_snprintf_chip_name,
19};
20
21use crate::Bus;
22use crate::errors::{Error, Result};
23use crate::utils::API_ACCESS_LOCK;
24
25#[derive(Debug, PartialEq, Eq)]
27pub struct Chip<'sensors> {
28 pub(crate) raw: sensors_chip_name,
29 pub(crate) _phantom: &'sensors PhantomData<crate::LMSensors>,
30}
31
32impl<'sensors> Chip<'sensors> {
33 pub(crate) fn new(name: &str) -> Result<Self> {
35 let c_name = CString::new(name)?;
38 let mut result = MaybeUninit::zeroed();
39
40 let r = {
41 let _guard = API_ACCESS_LOCK.lock();
42 unsafe { sensors_parse_chip_name(c_name.as_ptr(), result.as_mut_ptr()) }
44 };
45
46 if r == 0 {
47 Ok(Self {
48 raw: unsafe { result.assume_init() },
50 _phantom: &PhantomData,
51 })
52 } else {
53 Err(Error::from_lm_sensors("sensors_parse_chip_name()", r))
54 }
55 }
56
57 #[must_use]
62 pub unsafe fn into_raw_parts(self) -> sensors_chip_name {
63 let raw = self.raw;
64 mem::forget(self);
65 raw
66 }
67
68 #[must_use]
70 pub fn raw_ref(&self) -> &sensors_chip_name {
71 &self.raw
72 }
73
74 #[must_use]
79 pub unsafe fn raw_mut(&mut self) -> &mut sensors_chip_name {
80 &mut self.raw
81 }
82
83 #[must_use]
85 pub fn as_ref(&'sensors self) -> ChipRef<'sensors> {
86 ChipRef(&self.raw)
87 }
88
89 pub fn set_bus(&mut self, new_bus: &Bus) {
93 self.raw.bus = new_bus.0;
94 }
95
96 pub fn feature_iter(&'sensors self) -> crate::feature::Iter<'sensors> {
99 crate::feature::Iter {
100 chip: ChipRef(&self.raw),
101 state: 0,
102 }
103 }
104
105 pub fn name(&self) -> Result<String> {
107 self.as_ref().name()
108 }
109
110 #[must_use]
112 pub fn prefix(&self) -> Option<Result<&str>> {
113 self.as_ref().prefix()
114 }
115
116 #[cfg(unix)]
118 #[must_use]
119 pub fn path(&self) -> Option<&Path> {
120 self.as_ref().path()
121 }
122
123 #[must_use]
125 pub fn address(&self) -> Option<c_int> {
126 self.as_ref().address()
127 }
128
129 pub fn do_chip_sets(&self) -> Result<()> {
133 self.as_ref().do_chip_sets()
134 }
135
136 #[must_use]
138 pub fn bus(&self) -> Bus {
139 Bus(self.raw.bus)
140 }
141
142 pub fn raw_name(&self) -> Result<CString> {
146 self.as_ref().raw_name()
147 }
148
149 #[must_use]
151 pub fn raw_prefix(&self) -> Option<&CStr> {
152 self.as_ref().raw_prefix()
153 }
154
155 #[must_use]
157 pub fn raw_path(&self) -> Option<&CStr> {
158 self.as_ref().raw_path()
159 }
160
161 #[must_use]
164 pub fn raw_address(&self) -> c_int {
165 self.raw.addr
166 }
167}
168
169impl Drop for Chip<'_> {
170 fn drop(&mut self) {
172 let _guard = API_ACCESS_LOCK.lock();
173 unsafe { sensors_free_chip_name(&mut self.raw) }
175 }
176}
177
178impl PartialEq<ChipRef<'_>> for Chip<'_> {
179 fn eq(&self, other: &ChipRef<'_>) -> bool {
180 self.as_ref() == *other
181 }
182}
183
184impl PartialEq<Chip<'_>> for ChipRef<'_> {
185 fn eq(&self, other: &Chip<'_>) -> bool {
186 *self == other.as_ref()
187 }
188}
189
190impl fmt::Display for Chip<'_> {
191 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
192 if let Ok(name) = self.raw_name() {
193 write!(f, "{}", name.to_string_lossy())
194 } else {
195 write!(f, "\u{fffd}")
196 }
197 }
198}
199
200#[derive(Debug, Clone, Copy, Eq)]
202pub struct ChipRef<'sensors>(pub(crate) &'sensors sensors_chip_name);
203
204impl<'sensors> ChipRef<'sensors> {
205 #[must_use]
207 pub fn raw_ref(self) -> &'sensors sensors_chip_name {
208 self.0
209 }
210
211 pub fn feature_iter(self) -> crate::feature::Iter<'sensors> {
214 crate::feature::Iter {
215 chip: self,
216 state: 0,
217 }
218 }
219
220 pub fn name(self) -> Result<String> {
222 self.raw_name()?.into_string().map_err(Into::into)
223 }
224
225 #[must_use]
227 pub fn prefix(self) -> Option<Result<&'sensors str>> {
228 self.raw_prefix()
229 .map(|prefix| prefix.to_str().map_err(Into::into))
230 }
231
232 #[cfg(unix)]
234 #[must_use]
235 pub fn path(self) -> Option<&'sensors Path> {
236 use std::os::unix::ffi::OsStrExt;
237
238 self.raw_path()
239 .map(CStr::to_bytes)
240 .map(OsStr::from_bytes)
241 .map(Path::new)
242 }
243
244 #[must_use]
246 pub fn address(self) -> Option<c_int> {
247 let addr = self.raw_address();
248 (addr != SENSORS_CHIP_NAME_ADDR_ANY).then_some(addr)
249 }
250
251 pub fn do_chip_sets(self) -> Result<()> {
255 let r = {
256 let _guard = API_ACCESS_LOCK.lock();
257 unsafe { sensors_do_chip_sets(self.0) }
259 };
260
261 if r == 0 {
262 Ok(())
263 } else {
264 Err(Error::from_lm_sensors("sensors_do_chip_sets()", r))
265 }
266 }
267
268 #[must_use]
270 pub fn bus(self) -> Bus {
271 Bus(self.0.bus)
272 }
273
274 pub fn raw_name(self) -> Result<CString> {
278 let (r, mut buffer) = {
279 let _guard = API_ACCESS_LOCK.lock();
280
281 let result = unsafe { sensors_snprintf_chip_name(ptr::null_mut(), 0, self.0) };
283 if result < 0 {
284 (result, Vec::default())
285 } else {
286 #[expect(clippy::cast_sign_loss, clippy::as_conversions)]
287 let mut buffer = vec![0_u8; (result as c_uint as usize).saturating_add(1)];
288
289 let result = unsafe {
291 sensors_snprintf_chip_name(buffer.as_mut_ptr().cast(), buffer.len(), self.0)
292 };
293 (result, buffer)
294 }
295 };
296
297 if r < 0 {
298 Err(Error::from_lm_sensors("sensors_snprintf_chip_name()", r))
299 } else {
300 #[expect(clippy::cast_sign_loss, clippy::as_conversions)]
301 let len = r as c_uint as usize;
302
303 if len >= buffer.len() {
304 let err = io::ErrorKind::InvalidData.into();
306 Err(Error::from_io("sensors_snprintf_chip_name()", err))
307 } else {
308 buffer.resize_with(len, Default::default);
309 CString::new(buffer).map_err(Into::into)
310 }
311 }
312 }
313
314 #[must_use]
316 pub fn raw_prefix(self) -> Option<&'sensors CStr> {
317 (!self.0.prefix.is_null()).then(|| unsafe { CStr::from_ptr(self.0.prefix) })
319 }
320
321 #[must_use]
323 pub fn raw_path(self) -> Option<&'sensors CStr> {
324 (!self.0.path.is_null()).then(|| unsafe { CStr::from_ptr(self.0.path) })
326 }
327
328 #[must_use]
331 pub fn raw_address(self) -> c_int {
332 self.0.addr
333 }
334}
335
336impl PartialEq<ChipRef<'_>> for ChipRef<'_> {
337 fn eq(&self, other: &ChipRef<'_>) -> bool {
338 self.raw_address() == other.raw_address()
339 && self.bus() == other.bus()
340 && self.raw_prefix() == other.raw_prefix()
341 && self.raw_path() == other.raw_path()
342 }
343}
344
345impl fmt::Display for ChipRef<'_> {
346 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
347 if let Ok(name) = self.raw_name() {
348 write!(f, "{}", name.to_string_lossy())
349 } else {
350 write!(f, "\u{fffd}")
351 }
352 }
353}
354
355#[derive(Debug)]
357#[must_use]
358pub struct Iter<'sensors> {
359 pub(crate) state: c_int,
360 pub(crate) match_pattern: Option<ChipRef<'sensors>>,
361}
362
363impl<'sensors> Iterator for Iter<'sensors> {
364 type Item = ChipRef<'sensors>;
365
366 fn next(&mut self) -> Option<Self::Item> {
368 let match_pattern = self
369 .match_pattern
370 .map_or_else(ptr::null, |chip| chip.raw_ref());
371
372 let name = {
373 let _guard = API_ACCESS_LOCK.lock();
374 unsafe { sensors_get_detected_chips(match_pattern, &mut self.state).as_ref() }
376 };
377
378 name.map(ChipRef)
379 }
380}