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