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,
44}
45
46impl super::FromBufRead for CpuPressure {
47 fn from_buf_read<R: std::io::BufRead>(mut r: R) -> ProcResult<Self> {
48 let mut some = String::new();
49 r.read_line(&mut some)?;
50
51 Ok(CpuPressure {
52 some: parse_pressure_record(&some)?,
53 })
54 }
55}
56
57#[derive(Debug, Clone)]
59#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
60pub struct MemoryPressure {
61 pub some: PressureRecord,
63 pub full: PressureRecord,
66}
67
68impl super::FromBufRead for MemoryPressure {
69 fn from_buf_read<R: std::io::BufRead>(r: R) -> ProcResult<Self> {
70 let (some, full) = get_pressure(r)?;
71 Ok(MemoryPressure { some, full })
72 }
73}
74
75#[derive(Debug, Clone)]
77#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
78pub struct IoPressure {
79 pub some: PressureRecord,
81 pub full: PressureRecord,
84}
85
86impl super::FromBufRead for IoPressure {
87 fn from_buf_read<R: std::io::BufRead>(r: R) -> ProcResult<Self> {
88 let (some, full) = get_pressure(r)?;
89 Ok(IoPressure { some, full })
90 }
91}
92
93fn get_f32(map: &HashMap<&str, &str>, value: &str) -> ProcResult<f32> {
94 map.get(value).map_or_else(
95 || Err(ProcError::Incomplete(None)),
96 |v| v.parse::<f32>().map_err(|_| ProcError::Incomplete(None)),
97 )
98}
99
100fn get_total(map: &HashMap<&str, &str>) -> ProcResult<u64> {
101 map.get("total").map_or_else(
102 || Err(ProcError::Incomplete(None)),
103 |v| v.parse::<u64>().map_err(|_| ProcError::Incomplete(None)),
104 )
105}
106
107fn parse_pressure_record(line: &str) -> ProcResult<PressureRecord> {
108 let mut parsed = HashMap::new();
109
110 if !line.starts_with("some") && !line.starts_with("full") {
111 return Err(ProcError::Incomplete(None));
112 }
113
114 let values = &line[5..];
115
116 for kv_str in values.split_whitespace() {
117 let kv_split = kv_str.split('=');
118 let vec: Vec<&str> = kv_split.collect();
119 if vec.len() == 2 {
120 parsed.insert(vec[0], vec[1]);
121 }
122 }
123
124 Ok(PressureRecord {
125 avg10: get_f32(&parsed, "avg10")?,
126 avg60: get_f32(&parsed, "avg60")?,
127 avg300: get_f32(&parsed, "avg300")?,
128 total: get_total(&parsed)?,
129 })
130}
131
132fn get_pressure<R: std::io::BufRead>(mut r: R) -> ProcResult<(PressureRecord, PressureRecord)> {
133 let mut some = String::new();
134 r.read_line(&mut some)?;
135 let mut full = String::new();
136 r.read_line(&mut full)?;
137 Ok((parse_pressure_record(&some)?, parse_pressure_record(&full)?))
138}
139
140#[cfg(test)]
141mod test {
142 use super::*;
143 use std::f32::EPSILON;
144
145 #[test]
146 fn test_parse_pressure_record() {
147 let record = parse_pressure_record("full avg10=2.10 avg60=0.12 avg300=0.00 total=391926").unwrap();
148
149 assert!(record.avg10 - 2.10 < EPSILON);
150 assert!(record.avg60 - 0.12 < EPSILON);
151 assert!(record.avg300 - 0.00 < EPSILON);
152 assert_eq!(record.total, 391_926);
153 }
154
155 #[test]
156 fn test_parse_pressure_record_errs() {
157 assert!(parse_pressure_record("avg10=2.10 avg60=0.12 avg300=0.00 total=391926").is_err());
158 assert!(parse_pressure_record("some avg10=2.10 avg300=0.00 total=391926").is_err());
159 assert!(parse_pressure_record("some avg10=2.10 avg60=0.00 avg300=0.00").is_err());
160 }
161}