1use nom::{
2 branch::alt,
3 bytes::complete::{tag, take_until1},
4 character::complete::{i64 as parse_i64, newline, not_line_ending, space1, u64 as parse_u64},
5 combinator::value,
6 multi::separated_list1,
7 number::complete::double,
8 sequence::terminated,
9 IResult, Parser,
10};
11
12use crate::errors::RRDCachedClientError;
13
14pub fn parse_response_line(input: &str) -> Result<(i64, &str), RRDCachedClientError> {
15 let parse_result: IResult<&str, (i64, &str)> = (
16 terminated(parse_i64, space1),
17 terminated(not_line_ending, newline),
18 )
19 .parse(input);
20
21 match parse_result {
22 Ok((_, (code, message))) => Ok((code, message)),
23 Err(_) => Err(RRDCachedClientError::Parsing("parse error".to_string())),
24 }
25}
26
27pub fn parse_queue_line(input: &str) -> Result<(&str, usize), RRDCachedClientError> {
28 let parse_result: IResult<&str, (u64, &str)> = (
29 terminated(parse_u64, space1),
30 terminated(not_line_ending, newline),
31 )
32 .parse(input);
33
34 match parse_result {
35 Ok((_, (code, message))) => Ok((message, code as usize)),
36 Err(_) => Err(RRDCachedClientError::Parsing("parse error".to_string())),
37 }
38}
39
40pub fn parse_stats_line(input: &str) -> Result<(&str, i64), RRDCachedClientError> {
41 let parse_result: IResult<&str, (&str, &str, &str, i64)> = (
43 take_until1(":"),
44 tag(":"),
45 space1,
46 terminated(parse_i64, newline),
47 )
48 .parse(input);
49
50 match parse_result {
51 Ok((_, (name, _, _, value))) => Ok((name, value)),
52 Err(_) => Err(RRDCachedClientError::Parsing("parse error".to_string())),
53 }
54}
55
56pub fn parse_timestamp(input: &str) -> Result<usize, RRDCachedClientError> {
57 let parse_result: IResult<&str, u64> = parse_u64(input);
58 match parse_result {
59 Ok((_, timestamp)) => Ok(timestamp as usize),
60 Err(_) => Err(RRDCachedClientError::Parsing("parse error".to_string())),
61 }
62}
63
64pub fn parse_fetch_header_line(input: &str) -> Result<(String, String), RRDCachedClientError> {
65 let parse_result: IResult<&str, (&str, &str, &str, &str)> = (
66 take_until1(":"),
67 tag(":"),
68 space1,
69 terminated(not_line_ending, newline),
70 )
71 .parse(input);
72
73 match parse_result {
74 Ok((_, (name, _tag, _space, value))) => Ok((name.to_string(), value.to_string())),
75 Err(_) => Err(RRDCachedClientError::Parsing("parse error".to_string())),
76 }
77}
78
79pub fn parse_fetch_line(input: &str) -> IResult<&str, (usize, Vec<f64>)> {
80 (
81 parse_u64,
82 tag(":"),
83 space1,
84 separated_list1(space1, alt((double, value(f64::NAN, tag("-nan"))))),
85 newline,
86 )
87 .parse(input)
88 .map(|(i, (timestamp, _, _, values, _))| (i, (timestamp as usize, values)))
89}
90
91#[cfg(test)]
92mod tests {
93 use super::*;
94
95 #[test]
96 fn test_parse_response_line() {
97 let input = "1234 hello world\n";
98 let result = parse_response_line(input);
99 assert_eq!(result.unwrap(), (1234, "hello world"));
100
101 let input = "1234 hello world";
102 let result = parse_response_line(input);
103 assert!(result.is_err());
104
105 let input = "0 PONG\n";
106 let result = parse_response_line(input);
107 assert_eq!(result.unwrap(), (0, "PONG"));
108
109 let input = "-20 errors, a lot of errors\n";
110 let result = parse_response_line(input);
111 assert_eq!(result.unwrap(), (-20, "errors, a lot of errors"));
112
113 let input = "";
114 let result = parse_response_line(input);
115 assert!(result.is_err());
116
117 let input = "1234";
118 let result = parse_response_line(input);
119 assert!(result.is_err());
120 }
121
122 #[test]
123 fn test_parse_queue_line() {
124 let input = "12 test.rrd\n";
125 let result = parse_queue_line(input);
126 assert_eq!(result.unwrap(), ("test.rrd", 12));
127
128 let input = "-0 test/test.rrd";
129 let result = parse_queue_line(input);
130 assert!(result.is_err());
131 }
132
133 #[test]
134 fn test_parse_stats_line() {
135 let input = "uptime: 1234\n";
136 let result = parse_stats_line(input);
137 assert_eq!(result.unwrap(), ("uptime", 1234));
138
139 let input = "uptime: 1234";
140 let result = parse_stats_line(input);
141 assert!(result.is_err());
142
143 let input = "upti:me:\n 1234\n";
144 let result = parse_stats_line(input);
145 assert!(result.is_err());
146
147 let input = " upti:me: 1234\n";
148 let result = parse_stats_line(input);
149 assert!(result.is_err());
150 }
151
152 #[test]
153 fn test_parse_timestamp() {
154 let input = "1234";
155 let result = parse_timestamp(input);
156 assert_eq!(result.unwrap(), 1234);
157
158 let input = "abcd\n";
159 let result = parse_timestamp(input);
160 assert!(result.is_err());
161 }
162 #[test]
175 fn test_parse_fetch_header_line() {
176 let input = "FlushVersion: 1\n";
177 let result = parse_fetch_header_line(input);
178 assert_eq!(
179 result.unwrap(),
180 ("FlushVersion".to_string(), "1".to_string())
181 );
182
183 let input = "FlushVersion: 1";
184 let result = parse_fetch_header_line(input);
185 assert!(result.is_err());
186
187 let input = "DSName: ds1 ds2\n";
188 let result = parse_fetch_header_line(input);
189 assert_eq!(
190 result.unwrap(),
191 ("DSName".to_string(), "ds1 ds2".to_string())
192 );
193
194 let input = "0 PONG\n";
195 let result = parse_fetch_header_line(input);
196 assert!(result.is_err());
197 }
198
199 #[test]
200 fn test_parse_fetch_line() {
201 let input = "1708800040: nan nan\n";
202 let result = parse_fetch_line(input).unwrap().1;
203 assert_eq!(result.0, 1708800040);
204 assert_eq!(result.1.len(), 2);
205 assert!(result.1.iter().all(|f| f.is_nan()));
206
207 let input = "1708800040: 4.2 100000\n";
208 let result = parse_fetch_line(input);
209 assert_eq!(result.unwrap().1, (1708800040, vec![4.2, 100000.0]));
210
211 let input = "1708800040: nan nan";
212 let result = parse_fetch_line(input);
213 assert!(result.is_err());
214
215 let input = "End: 1708886440";
216 let result = parse_fetch_line(input);
217 assert!(result.is_err());
218 }
219
220 #[test]
221 fn test_minus_nan_handling() {
222 let input = "1708800040: -nan -nan\n";
224 let result = parse_fetch_line(input).unwrap().1;
225 assert_eq!(result.0, 1708800040);
226 assert_eq!(result.1.len(), 2);
227 assert!(result.1.iter().all(|f| f.is_nan()));
228 }
229}