1use crate::{ProcError, ProcResult};
9use std::collections::HashMap;
10
11#[cfg(feature = "serde1")]
12use serde::{Deserialize, Serialize};
13
14#[derive(Debug, Clone)]
18#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
19pub struct PressureRecord {
20 pub avg10: f32,
25 pub avg60: f32,
30 pub avg300: f32,
35 pub total: u64,
37}
38
39#[derive(Debug, Clone)]
41#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
42pub struct CpuPressure {
43 pub some: PressureRecord,
45 pub full: PressureRecord,
50}
51
52impl super::FromBufRead for CpuPressure {
53 fn from_buf_read<R: std::io::BufRead>(mut r: R) -> ProcResult<Self> {
54 let (some, full) = get_pressure(r)?;
55 Ok(CpuPressure { some, full })
56 }
57}
58
59#[derive(Debug, Clone)]
61#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
62pub struct MemoryPressure {
63 pub some: PressureRecord,
65 pub full: PressureRecord,
68}
69
70impl super::FromBufRead for MemoryPressure {
71 fn from_buf_read<R: std::io::BufRead>(r: R) -> ProcResult<Self> {
72 let (some, full) = get_pressure(r)?;
73 Ok(MemoryPressure { some, full })
74 }
75}
76
77#[derive(Debug, Clone)]
79#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
80pub struct IoPressure {
81 pub some: PressureRecord,
83 pub full: PressureRecord,
86}
87
88impl super::FromBufRead for IoPressure {
89 fn from_buf_read<R: std::io::BufRead>(r: R) -> ProcResult<Self> {
90 let (some, full) = get_pressure(r)?;
91 Ok(IoPressure { some, full })
92 }
93}
94
95fn get_f32(map: &HashMap<&str, &str>, value: &str) -> ProcResult<f32> {
96 map.get(value).map_or_else(
97 || Err(ProcError::Incomplete(None)),
98 |v| v.parse::<f32>().map_err(|_| ProcError::Incomplete(None)),
99 )
100}
101
102fn get_total(map: &HashMap<&str, &str>) -> ProcResult<u64> {
103 map.get("total").map_or_else(
104 || Err(ProcError::Incomplete(None)),
105 |v| v.parse::<u64>().map_err(|_| ProcError::Incomplete(None)),
106 )
107}
108
109pub fn parse_pressure_record(line: &str) -> ProcResult<PressureRecord> {
117 let mut parsed = HashMap::new();
118
119 if !line.starts_with("some") && !line.starts_with("full") {
120 return Err(ProcError::Incomplete(None));
121 }
122
123 let values = &line[5..];
124
125 for kv_str in values.split_whitespace() {
126 let kv_split = kv_str.split('=');
127 let vec: Vec<&str> = kv_split.collect();
128 if vec.len() == 2 {
129 parsed.insert(vec[0], vec[1]);
130 }
131 }
132
133 Ok(PressureRecord {
134 avg10: get_f32(&parsed, "avg10")?,
135 avg60: get_f32(&parsed, "avg60")?,
136 avg300: get_f32(&parsed, "avg300")?,
137 total: get_total(&parsed)?,
138 })
139}
140
141pub fn get_pressure<R: std::io::BufRead>(mut r: R) -> ProcResult<(PressureRecord, PressureRecord)> {
151 let mut some = String::new();
152 r.read_line(&mut some)?;
153 let mut full = String::new();
154 r.read_line(&mut full)?;
155 Ok((parse_pressure_record(&some)?, parse_pressure_record(&full)?))
156}
157
158#[cfg(test)]
159mod test {
160 use super::*;
161 use std::f32::EPSILON;
162
163 #[test]
164 fn test_parse_pressure_record() {
165 let record = parse_pressure_record("full avg10=2.10 avg60=0.12 avg300=0.00 total=391926").unwrap();
166
167 assert!(record.avg10 - 2.10 < EPSILON);
168 assert!(record.avg60 - 0.12 < EPSILON);
169 assert!(record.avg300 - 0.00 < EPSILON);
170 assert_eq!(record.total, 391_926);
171 }
172
173 #[test]
174 fn test_parse_pressure_record_errs() {
175 assert!(parse_pressure_record("avg10=2.10 avg60=0.12 avg300=0.00 total=391926").is_err());
176 assert!(parse_pressure_record("some avg10=2.10 avg300=0.00 total=391926").is_err());
177 assert!(parse_pressure_record("some avg10=2.10 avg60=0.00 avg300=0.00").is_err());
178 }
179}