haproxy_stats/
info.rs

1use core::{fmt, time::Duration};
2use std::io::{BufRead as _, Cursor, Error as IoError};
3
4use chrono::NaiveDate;
5use csv::{ByteRecord, Error as CsvError};
6use duration_str::deserialize_duration;
7use semver::Version;
8use serde::Deserialize;
9use serde_json::{Error as SerdeJsonError, Map, Value};
10
11use crate::formats::json;
12
13//
14#[derive(Deserialize, Debug, Clone)]
15pub struct Info {
16    #[serde(rename = "Name")]
17    pub name: Box<str>,
18    #[serde(rename = "Version")]
19    pub version: Version,
20    #[serde(rename = "Release_date")]
21    pub release_date: NaiveDate,
22    #[serde(rename = "Nbproc")]
23    pub nbproc: usize,
24    #[serde(rename = "Process_num")]
25    pub process_num: usize,
26    #[serde(rename = "Pid")]
27    pub pid: usize,
28    #[serde(rename = "Uptime")]
29    #[serde(deserialize_with = "deserialize_duration")]
30    pub uptime: Duration,
31    #[serde(rename = "Uptime_sec")]
32    pub uptime_sec: usize,
33    // TODO,
34}
35
36impl Info {
37    pub fn from_json_bytes(bytes: impl AsRef<[u8]>) -> Result<Self, InfoFromJsonBytesError> {
38        let bytes = bytes.as_ref();
39
40        let output = serde_json::from_slice::<JsonOutput>(bytes)
41            .map_err(InfoFromJsonBytesError::DeOutputFailed)?;
42
43        let map: Map<String, Value> = output
44            .0
45            .into_iter()
46            .map(|x| {
47                let v = match x.field.name.as_ref() {
48                    "Release_date" => {
49                        Value::from(x.value.as_str().unwrap_or_default().replacen('/', "-", 3))
50                    }
51                    _ => Value::from(&x.value),
52                };
53
54                (x.field.name.to_string(), v)
55            })
56            .collect();
57
58        serde_json::from_value::<Self>(Value::Object(map)).map_err(InfoFromJsonBytesError::DeFailed)
59    }
60
61    pub fn from_kv_bytes(bytes: impl AsRef<[u8]>) -> Result<Self, InfoFromKvBytesError> {
62        let bytes = bytes.as_ref();
63
64        let cursor = Cursor::new(bytes);
65
66        let list: Vec<(Box<str>, Box<str>)> = cursor
67            .lines()
68            .map(|x| match x {
69                Ok(s) => {
70                    let mut split = s.split(": ");
71                    let k: Box<str> = split.next().unwrap_or_default().into();
72                    let v: Box<str> = split.next().unwrap_or_default().into();
73
74                    let v = match k.as_ref() {
75                        "Release_date" => v.replacen('/', "-", 3).into(),
76                        _ => v,
77                    };
78
79                    Ok((k, v))
80                }
81                Err(err) => Err(InfoFromKvBytesError::LinesReadFailed(err)),
82            })
83            .collect::<Result<_, _>>()?;
84
85        let header = list.iter().map(|(x, _)| x.as_ref()).collect::<Vec<_>>();
86        let header_record = ByteRecord::from(header);
87
88        let row = list.iter().map(|(_, x)| x.as_ref()).collect::<Vec<_>>();
89        let row_record = ByteRecord::from(row);
90
91        row_record
92            .deserialize::<Self>(Some(&header_record))
93            .map_err(InfoFromKvBytesError::ValueDeFailed)
94    }
95}
96
97//
98#[derive(Debug)]
99pub enum InfoFromJsonBytesError {
100    DeOutputFailed(SerdeJsonError),
101    DeFailed(SerdeJsonError),
102}
103
104impl fmt::Display for InfoFromJsonBytesError {
105    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
106        write!(f, "{:?}", self)
107    }
108}
109
110impl std::error::Error for InfoFromJsonBytesError {}
111
112//
113#[derive(Debug)]
114pub enum InfoFromKvBytesError {
115    LinesReadFailed(IoError),
116    ValueDeFailed(CsvError),
117}
118
119impl fmt::Display for InfoFromKvBytesError {
120    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
121        write!(f, "{:?}", self)
122    }
123}
124
125impl std::error::Error for InfoFromKvBytesError {}
126
127//
128//
129//
130#[derive(Deserialize, Debug, Clone)]
131pub struct JsonOutput(pub Vec<JsonOutputItem>);
132
133#[derive(Deserialize, Debug, Clone)]
134pub struct JsonOutputItem {
135    pub field: json::Field,
136    #[serde(rename = "processNum")]
137    pub process_num: usize,
138    pub tags: json::Tags,
139    pub value: json::Value,
140}
141
142#[cfg(test)]
143mod tests {
144    use super::*;
145
146    #[test]
147    fn test_info_from_json_bytes() {
148        let bytes = include_bytes!("../tests/files/2_5_5_show_info.json");
149
150        let info = Info::from_json_bytes(bytes).unwrap();
151
152        println!("{:?}", info);
153
154        assert_eq!(info.name, "HAProxy".into());
155    }
156
157    #[test]
158    fn test_info_from_kv_bytes() {
159        let bytes = include_bytes!("../tests/files/2_5_5_show_info.txt");
160
161        let info = Info::from_kv_bytes(bytes).unwrap();
162
163        assert_eq!(info.name, "HAProxy".into());
164    }
165}