sysinfo/common/component.rs
1// Take a look at the license at the top of the repository in the LICENSE file.
2
3use crate::{ComponentInner, ComponentsInner};
4
5/// Interacting with components.
6///
7/// ⚠️ Not implemented in NetBSD.
8///
9/// ```no_run
10/// use sysinfo::Components;
11///
12/// let components = Components::new_with_refreshed_list();
13/// for component in &components {
14/// println!("{component:?}");
15/// }
16/// ```
17pub struct Components {
18 pub(crate) inner: ComponentsInner,
19}
20
21impl Default for Components {
22 fn default() -> Self {
23 Self::new()
24 }
25}
26
27impl From<Components> for Vec<Component> {
28 fn from(components: Components) -> Self {
29 components.inner.into_vec()
30 }
31}
32
33impl From<Vec<Component>> for Components {
34 fn from(components: Vec<Component>) -> Self {
35 Self {
36 inner: ComponentsInner::from_vec(components),
37 }
38 }
39}
40
41impl std::ops::Deref for Components {
42 type Target = [Component];
43
44 fn deref(&self) -> &Self::Target {
45 self.list()
46 }
47}
48
49impl std::ops::DerefMut for Components {
50 fn deref_mut(&mut self) -> &mut Self::Target {
51 self.list_mut()
52 }
53}
54
55impl<'a> IntoIterator for &'a Components {
56 type Item = &'a Component;
57 type IntoIter = std::slice::Iter<'a, Component>;
58
59 fn into_iter(self) -> Self::IntoIter {
60 self.list().iter()
61 }
62}
63
64impl<'a> IntoIterator for &'a mut Components {
65 type Item = &'a mut Component;
66 type IntoIter = std::slice::IterMut<'a, Component>;
67
68 fn into_iter(self) -> Self::IntoIter {
69 self.list_mut().iter_mut()
70 }
71}
72
73impl Components {
74 /// Creates a new empty [`Components`][crate::Components] type.
75 ///
76 /// If you want it to be filled directly, take a look at
77 /// [`Components::new_with_refreshed_list`].
78 ///
79 /// ```no_run
80 /// use sysinfo::Components;
81 ///
82 /// let mut components = Components::new();
83 /// components.refresh(false);
84 /// for component in &components {
85 /// println!("{component:?}");
86 /// }
87 /// ```
88 pub fn new() -> Self {
89 Self {
90 inner: ComponentsInner::new(),
91 }
92 }
93
94 /// Creates a new [`Components`][crate::Components] type with the components list
95 /// loaded.
96 ///
97 /// ```no_run
98 /// use sysinfo::Components;
99 ///
100 /// let mut components = Components::new_with_refreshed_list();
101 /// for component in components.list() {
102 /// println!("{component:?}");
103 /// }
104 /// ```
105 pub fn new_with_refreshed_list() -> Self {
106 let mut components = Self::new();
107 components.refresh(true);
108 components
109 }
110
111 /// Returns the components list.
112 ///
113 /// ```no_run
114 /// use sysinfo::Components;
115 ///
116 /// let components = Components::new_with_refreshed_list();
117 /// for component in components.list() {
118 /// println!("{component:?}");
119 /// }
120 /// ```
121 pub fn list(&self) -> &[Component] {
122 self.inner.list()
123 }
124
125 /// Returns the components list.
126 ///
127 /// ```no_run
128 /// use sysinfo::Components;
129 ///
130 /// let mut components = Components::new_with_refreshed_list();
131 /// for component in components.list_mut() {
132 /// component.refresh();
133 /// println!("{component:?}");
134 /// }
135 /// ```
136 pub fn list_mut(&mut self) -> &mut [Component] {
137 self.inner.list_mut()
138 }
139
140 /// Refreshes the components list.
141 ///
142 /// ```no_run
143 /// use sysinfo::Components;
144 ///
145 /// let mut components = Components::new_with_refreshed_list();
146 /// // We wait some time...?
147 /// components.refresh(false);
148 /// ```
149 pub fn refresh(&mut self, remove_not_listed_components: bool) {
150 self.inner.refresh();
151 if remove_not_listed_components {
152 // Remove interfaces which are gone.
153 self.inner.components.retain_mut(|c| {
154 if !c.inner.updated {
155 return false;
156 }
157 c.inner.updated = false;
158 true
159 });
160 }
161 }
162}
163
164/// Getting a component temperature information.
165///
166/// ```no_run
167/// use sysinfo::Components;
168///
169/// let components = Components::new_with_refreshed_list();
170/// for component in &components {
171/// if let Some(temperature) = component.temperature() {
172/// println!("{} {temperature}°C", component.label());
173/// } else {
174/// println!("{} (unknown temperature)", component.label());
175/// }
176/// }
177/// ```
178pub struct Component {
179 pub(crate) inner: ComponentInner,
180}
181
182impl Component {
183 /// Returns the temperature of the component (in celsius degree).
184 ///
185 /// ## Linux
186 ///
187 /// Returns `f32::NAN` if it failed to retrieve it.
188 ///
189 /// ```no_run
190 /// use sysinfo::Components;
191 ///
192 /// let components = Components::new_with_refreshed_list();
193 /// for component in &components {
194 /// if let Some(temperature) = component.temperature() {
195 /// println!("{temperature}°C");
196 /// }
197 /// }
198 /// ```
199 pub fn temperature(&self) -> Option<f32> {
200 self.inner.temperature()
201 }
202
203 /// Returns the maximum temperature of the component (in celsius degree).
204 ///
205 /// Note: if `temperature` is higher than the current `max`,
206 /// `max` value will be updated on refresh.
207 ///
208 /// ## Linux
209 ///
210 /// May be computed by `sysinfo` from kernel.
211 /// Returns `f32::NAN` if it failed to retrieve it.
212 ///
213 /// ```no_run
214 /// use sysinfo::Components;
215 ///
216 /// let components = Components::new_with_refreshed_list();
217 /// for component in &components {
218 /// if let Some(max) = component.max() {
219 /// println!("{max}°C");
220 /// }
221 /// }
222 /// ```
223 pub fn max(&self) -> Option<f32> {
224 self.inner.max()
225 }
226
227 /// Returns the highest temperature before the component halts (in celsius degree).
228 ///
229 /// ## Linux
230 ///
231 /// Critical threshold defined by chip or kernel.
232 ///
233 /// ```no_run
234 /// use sysinfo::Components;
235 ///
236 /// let components = Components::new_with_refreshed_list();
237 /// for component in &components {
238 /// if let Some(critical) = component.critical() {
239 /// println!("{critical}°C");
240 /// }
241 /// }
242 /// ```
243 pub fn critical(&self) -> Option<f32> {
244 self.inner.critical()
245 }
246
247 /// Returns the label of the component.
248 ///
249 /// ## Linux
250 ///
251 /// Since components information is retrieved thanks to `hwmon`,
252 /// the labels are generated as follows.
253 /// Note: it may change and it was inspired by `sensors` own formatting.
254 ///
255 /// | name | label | device_model | id_sensor | Computed label by `sysinfo` |
256 /// |---------|--------|------------|----------|----------------------|
257 /// | ✓ | ✓ | ✓ | ✓ | `"{name} {label} {device_model}"` |
258 /// | ✓ | ✓ | ✗ | ✓ | `"{name} {label}"` |
259 /// | ✓ | ✗ | ✓ | ✓ | `"{name} {device_model}"` |
260 /// | ✓ | ✗ | ✗ | ✓ | `"{name} temp{id}"` |
261 ///
262 /// ```no_run
263 /// use sysinfo::Components;
264 ///
265 /// let components = Components::new_with_refreshed_list();
266 /// for component in &components {
267 /// println!("{}", component.label());
268 /// }
269 /// ```
270 pub fn label(&self) -> &str {
271 self.inner.label()
272 }
273
274 /// Returns the identifier of the component.
275 ///
276 /// Note: The identifier should be reasonably unique but is provided by the kernel.
277 /// It could change if the hardware changes or after a reboot.
278 ///
279 /// | OS | Computed ID by `sysinfo` | Example |
280 /// |----|--------------------------|----------|
281 /// | Linux/hwmon | hwmon file concatenated with the temp index. | ` hwmon0_1` if the temperature data comes from the `hwmon0/temp1_input` file. |
282 /// | Linux/thermal | thermal file name | `thermal_zone0` |
283 /// | FreeBSD | `cpu_` concatenated with the core index. | `cpu_1` for the first core. |
284 /// | macOS/arm | Serial ID reported by the HID driver. | |
285 /// | macOS/x86 | Technical ID sent to the OS (see below) | `TXCX` |
286 /// | Windows | `Computer` (same as the label) | `Computer` |
287 /// | appstore | Components are not available | None |
288 /// | unknown | Components are not available | None |
289 ///
290 /// For macOS on X86 the following identifiers are possible:
291 /// - `TXCX` or `TXCx` for PECI CPU (depending on if run on iMac or MacBook)
292 /// - `TC0P` for CPU Proximity
293 /// - `TG0P` for GPU
294 /// - `TB0T` for Battery
295 ///
296 /// ```no_run
297 /// use sysinfo::Components;
298 ///
299 /// let components = Components::new_with_refreshed_list();
300 /// for component in &components {
301 /// if let Some(id) = component.id() {
302 /// println!("{id}");
303 /// }
304 /// }
305 /// ```
306 pub fn id(&self) -> Option<&str> {
307 self.inner.id()
308 }
309
310 /// Refreshes component.
311 ///
312 /// ```no_run
313 /// use sysinfo::Components;
314 ///
315 /// let mut components = Components::new_with_refreshed_list();
316 /// for component in components.iter_mut() {
317 /// component.refresh();
318 /// }
319 /// ```
320 pub fn refresh(&mut self) {
321 self.inner.refresh()
322 }
323}
324
325#[cfg(test)]
326mod tests {
327 use crate::*;
328
329 #[test]
330 fn test_components_mac_m1() {
331 let mut components = Components::new();
332 components.refresh(false);
333 components.refresh(false);
334 }
335}