1use nom::branch::alt;
18use nom::bytes::complete::{tag, tag_no_case};
19use nom::character::complete::{alphanumeric1, digit0, multispace0};
20use nom::combinator::peek;
21use nom::sequence::tuple;
22use nom::IResult;
23
24use crate::repr;
25
26#[test]
27fn test_parse_duration() {
28 let input = "1 h 5 m 42 second";
29 let (remain, ttl) = parse_duration(input).unwrap();
30 assert_eq!(remain, "");
31 assert_eq!(ttl, (3600 + 5 * 60 + 42) * 1000);
32}
33
34#[test]
35fn test_parse_fixed() {
36 let input = "timestamp < now() - INTERVAL '5m 42s'";
37 let (remain, ttl) = parse_fixed(input).unwrap();
38 assert_eq!(remain, "");
39 assert_eq!(ttl, (5 * 60 + 42) * 1000);
40}
41
42pub fn parse_fixed(input: &str) -> IResult<&str, i64> {
43 let (r, _) = tuple((
44 multispace0,
45 tag_no_case("timestamp"),
46 multispace0,
47 tag("<"),
48 multispace0,
49 tag_no_case("now()"),
50 multispace0,
51 tag("-"),
52 multispace0,
53 tag_no_case("interval"),
54 multispace0,
55 ))(input)?;
56 tuple((tag("'"), parse_duration, tag("'")))(r).map(|(r, (_, ttl, _))| (r, ttl))
57}
58
59pub fn parse_duration(input: &str) -> IResult<&str, i64> {
61 let mut intervals = vec![];
62 let mut remain = input;
63 while peek(parse_quality)(remain).is_ok() {
64 let (r, number) = parse_quality(remain)?;
65 let (r, unit) = parse_time_unit(r)?;
66 intervals.push((number, unit));
67 remain = r;
68 }
69 let mut total = 0;
70 for (number, unit) in intervals {
71 let number = match unit {
72 TimeUnit::Second => number,
73 TimeUnit::Minute => number * 60,
74 TimeUnit::Hour => number * 60 * 60,
75 };
76 total += number;
77 }
78 total *= 1000;
79 Ok((remain, total))
80}
81
82enum Expr {
83 Col(String),
84 Now,
85 Duration(repr::Duration),
86 Binary {
87 left: Box<Expr>,
88 op: String,
89 right: Box<Expr>,
90 },
91}
92
93fn parse_expr(input: &str) -> IResult<&str, Expr> {
94 parse_expr_bp(input, 0)
95}
96
97fn parse_expr_bp(input: &str, min_bp: u8) -> IResult<&str, Expr> {
99 let (mut input, mut lhs): (&str, Expr) = parse_item(input)?;
100 loop {
101 let (r, op) = parse_op(input)?;
102 let (_, (l_bp, r_bp)) = infix_binding_power(op)?;
103 if l_bp < min_bp {
104 return Ok((input, lhs));
105 }
106 let (r, rhs) = parse_expr_bp(r, r_bp)?;
107 input = r;
108 lhs = Expr::Binary {
109 left: Box::new(lhs),
110 op: op.to_string(),
111 right: Box::new(rhs),
112 };
113 }
114}
115
116fn parse_op(input: &str) -> IResult<&str, &str> {
117 alt((parse_add_sub, parse_cmp))(input)
118}
119
120fn parse_item(input: &str) -> IResult<&str, Expr> {
121 if let Ok((r, name)) = parse_col_name(input) {
122 Ok((r, Expr::Col(name.to_string())))
123 } else if let Ok((r, _now)) = parse_now(input) {
124 Ok((r, Expr::Now))
125 } else if let Ok((_r, _num)) = parse_quality(input) {
126 todo!()
127 } else {
128 todo!()
129 }
130}
131
132fn infix_binding_power(op: &str) -> IResult<&str, (u8, u8)> {
133 let ret = match op {
134 "<" | ">" | "<=" | ">=" => (1, 2),
135 "+" | "-" => (3, 4),
136 _ => {
137 return Err(nom::Err::Error(nom::error::Error::new(
138 op,
139 nom::error::ErrorKind::Fail,
140 )))
141 }
142 };
143 Ok((op, ret))
144}
145
146fn parse_col_name(input: &str) -> IResult<&str, &str> {
147 tuple((multispace0, alphanumeric1, multispace0))(input).map(|(r, (_, name, _))| (r, name))
148}
149
150fn parse_now(input: &str) -> IResult<&str, &str> {
151 tag_no_case("now()")(input)
152}
153
154fn parse_add_sub(input: &str) -> IResult<&str, &str> {
155 tuple((multispace0, alt((tag("+"), tag("-"))), multispace0))(input)
156 .map(|(r, (_, op, _))| (r, op))
157}
158
159fn parse_cmp(input: &str) -> IResult<&str, &str> {
160 tuple((
161 multispace0,
162 alt((tag("<="), tag(">="), tag("<"), tag(">"))),
163 multispace0,
164 ))(input)
165 .map(|(r, (_, op, _))| (r, op))
166}
167
168fn parse_quality(input: &str) -> IResult<&str, repr::Duration> {
170 tuple((
171 multispace0,
172 alt((tag("+"), tag("-"), tag(""))),
173 digit0,
174 multispace0,
175 ))(input)
176 .map(|(r, (_, sign, name, _))| (r, sign, name))
177 .and_then(|(r, sign, name)| {
178 let num = name.parse::<repr::Duration>().map_err(|_| {
179 nom::Err::Error(nom::error::Error::new(input, nom::error::ErrorKind::Digit))
180 })?;
181 let num = match sign {
182 "+" => num,
183 "-" => -num,
184 _ => num,
185 };
186 Ok((r, num))
187 })
188}
189
190#[derive(Debug, Clone)]
191enum TimeUnit {
192 Second,
193 Minute,
194 Hour,
195}
196
197#[derive(Debug, Clone)]
198enum DateUnit {
199 Day,
200 Month,
201 Year,
202}
203
204fn parse_time_unit(input: &str) -> IResult<&str, TimeUnit> {
205 fn to_second(input: &str) -> IResult<&str, TimeUnit> {
206 alt((
207 tag_no_case("second"),
208 tag_no_case("seconds"),
209 tag_no_case("S"),
210 ))(input)
211 .map(move |(r, _)| (r, TimeUnit::Second))
212 }
213 fn to_minute(input: &str) -> IResult<&str, TimeUnit> {
214 alt((
215 tag_no_case("minute"),
216 tag_no_case("minutes"),
217 tag_no_case("m"),
218 ))(input)
219 .map(move |(r, _)| (r, TimeUnit::Minute))
220 }
221 fn to_hour(input: &str) -> IResult<&str, TimeUnit> {
222 alt((tag_no_case("hour"), tag_no_case("hours"), tag_no_case("h")))(input)
223 .map(move |(r, _)| (r, TimeUnit::Hour))
224 }
225
226 tuple((
227 multispace0,
228 alt((
229 to_second, to_minute,
230 to_hour, )),
242 multispace0,
243 ))(input)
244 .map(|(r, (_, unit, _))| (r, unit))
245}