vector/sources/nginx_metrics/
parser.rs

1use std::convert::TryFrom;
2
3use nom::{
4    bytes::complete::{tag, take_while_m_n},
5    combinator::{all_consuming, map_res},
6    error::ErrorKind,
7    sequence::{preceded, terminated},
8    Parser,
9};
10use snafu::Snafu;
11
12#[derive(Debug, Snafu, PartialEq, Eq)]
13pub enum ParseError {
14    #[snafu(display("failed to parse NginxStubStatus, kind: `{:?}`", kind))]
15    NginxStubStatusParseError { kind: ErrorKind },
16}
17
18#[derive(Debug, Clone, Copy, PartialEq, Eq)]
19pub struct NginxStubStatus {
20    pub active: usize,
21    pub accepts: usize,
22    pub handled: usize,
23    pub requests: usize,
24    pub reading: usize,
25    pub writing: usize,
26    pub waiting: usize,
27}
28
29fn get_usize(input: &str) -> nom::IResult<&str, usize, nom::error::Error<&str>> {
30    map_res(
31        take_while_m_n(1, 20, |c: char| c.is_ascii_digit()),
32        |s: &str| s.parse::<usize>(),
33    )
34    .parse(input)
35}
36
37impl<'a> TryFrom<&'a str> for NginxStubStatus {
38    type Error = ParseError;
39
40    // The `ngx_http_stub_status_module` response:
41    // https://github.com/nginx/nginx/blob/master/src/http/modules/ngx_http_stub_status_module.c#L137-L145
42    fn try_from(input: &'a str) -> Result<Self, Self::Error> {
43        // `usize::MAX` eq `18446744073709551615` (20 characters)
44        match all_consuming((
45            preceded(tag("Active connections: "), get_usize),
46            preceded(tag(" \nserver accepts handled requests\n "), get_usize),
47            preceded(tag(" "), get_usize),
48            preceded(tag(" "), get_usize),
49            preceded(tag(" \nReading: "), get_usize),
50            preceded(tag(" Writing: "), get_usize),
51            terminated(preceded(tag(" Waiting: "), get_usize), tag(" \n")),
52        ))
53        .parse(input)
54        {
55            Ok((_, (active, accepts, handled, requests, reading, writing, waiting))) => {
56                Ok(NginxStubStatus {
57                    active,
58                    accepts,
59                    handled,
60                    requests,
61                    reading,
62                    writing,
63                    waiting,
64                })
65            }
66            Err(error) => match error {
67                nom::Err::Error(error) => {
68                    Err(ParseError::NginxStubStatusParseError { kind: error.code })
69                }
70                nom::Err::Incomplete(_) | nom::Err::Failure(_) => unreachable!(),
71            },
72        }
73    }
74}
75
76#[cfg(test)]
77mod tests {
78    use super::*;
79
80    #[test]
81    fn nginx_stub_status_try_from() {
82        let data = "Active connections: 291 \n\
83                    server accepts handled requests\n \
84                    16630948 16630948 31070465 \n\
85                    Reading: 6 Writing: 179 Waiting: 106 \n";
86
87        assert_eq!(
88            NginxStubStatus::try_from(data).expect("valid data"),
89            NginxStubStatus {
90                active: 291,
91                accepts: 16630948,
92                handled: 16630948,
93                requests: 31070465,
94                reading: 6,
95                writing: 179,
96                waiting: 106
97            }
98        );
99    }
100}