Skip to content

Commit d8492a6

Browse files
committed
Support date filtering
1 parent ab866ab commit d8492a6

File tree

5 files changed

+71
-21
lines changed

5 files changed

+71
-21
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ type = "Run" and title ~ "Park"
6767
- `title`: Activity title
6868
- `elevation`: Elevation (in meters)
6969
- `time`: Time (in seconds, 3600 = 1 hour)
70+
- `date`: Date (YYYY-MM-DD)
7071

7172
### Operators
7273

src/expr/evaluator.rs

Lines changed: 26 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
use std::collections::HashMap;
22

3-
use super::parser::{Expr, Parser};
3+
use chrono::NaiveDate;
4+
5+
use super::{parser::{Expr, Parser}, lexer::TokenKind};
46

57
pub type Vars = HashMap<String, Evalue>;
68

@@ -11,18 +13,21 @@ pub enum Evalue {
1113
String(String),
1214
Number(f64),
1315
Bool(bool),
16+
Date(NaiveDate),
1417
}
1518
impl Evalue {
1619
fn to_bool(&self) -> bool {
1720
match self {
1821
Evalue::String(v) => v != "" && v != "0",
1922
Evalue::Number(n) => *n != 0.0,
2023
Evalue::Bool(b) => *b,
24+
Evalue::Date(_) => true,
2125
}
2226
}
2327

2428
fn to_string(&self) -> String {
2529
match self {
30+
Evalue::Date(d) => d.to_string(),
2631
Evalue::String(v) => v.clone(),
2732
Evalue::Number(n) => format!("{}", *n),
2833
Evalue::Bool(b) => match b {
@@ -49,7 +54,7 @@ impl Evaluator {
4954

5055
pub fn evaluate(&self, expr: &Expr, vars: &Vars) -> Result<bool, String> {
5156
match self.evaluate_expr(&expr, vars)? {
52-
Evalue::String(_) | Evalue::Number(_) => {
57+
Evalue::Date(_) | Evalue::String(_) | Evalue::Number(_) => {
5358
Err(format!("expression must evluate to a boolean, got: {:?}", expr).to_string())
5459
}
5560
Evalue::Bool(b) => Ok(b),
@@ -58,27 +63,28 @@ impl Evaluator {
5863

5964
fn evaluate_expr(&self, expr: &super::parser::Expr, vars: &Vars) -> Result<Evalue, String> {
6065
match expr {
61-
super::parser::Expr::Boolean(b) => Ok(Evalue::Bool(*b)),
62-
super::parser::Expr::String(s) => Ok(Evalue::String(s.clone())),
63-
super::parser::Expr::Binary(lexpr, op, rexpr) => {
66+
Expr::Boolean(b) => Ok(Evalue::Bool(*b)),
67+
Expr::String(s) => Ok(Evalue::String(s.clone())),
68+
Expr::Date(s) => Ok(Evalue::Date(s.clone())),
69+
Expr::Binary(lexpr, op, rexpr) => {
6470
let lval = self.evaluate_expr(lexpr, vars)?;
6571
let rval = self.evaluate_expr(rexpr, vars)?;
6672
let eval = match op {
67-
super::lexer::TokenKind::GreaterThan => Ok(lval > rval),
68-
super::lexer::TokenKind::GreaterThanEqual => Ok(lval >= rval),
69-
super::lexer::TokenKind::LessThanEqual => Ok(lval <= rval),
70-
super::lexer::TokenKind::LessThan => Ok(lval < rval),
71-
super::lexer::TokenKind::Equal => Ok(lval == rval),
72-
super::lexer::TokenKind::FuzzyEqual => Ok(lval.to_string().contains(rval.to_string().as_str())),
73-
super::lexer::TokenKind::NotEqual => Ok(lval != rval),
74-
super::lexer::TokenKind::NotFuzzyEqual => Ok(!lval.to_string().contains(rval.to_string().as_str())),
75-
super::lexer::TokenKind::Or => Ok(lval.to_bool() || rval.to_bool()),
76-
super::lexer::TokenKind::And => Ok(lval.to_bool() && rval.to_bool()),
73+
TokenKind::GreaterThan => Ok(lval > rval),
74+
TokenKind::GreaterThanEqual => Ok(lval >= rval),
75+
TokenKind::LessThanEqual => Ok(lval <= rval),
76+
TokenKind::LessThan => Ok(lval < rval),
77+
TokenKind::Equal => Ok(lval == rval),
78+
TokenKind::FuzzyEqual => Ok(lval.to_string().contains(rval.to_string().as_str())),
79+
TokenKind::NotEqual => Ok(lval != rval),
80+
TokenKind::NotFuzzyEqual => Ok(!lval.to_string().contains(rval.to_string().as_str())),
81+
TokenKind::Or => Ok(lval.to_bool() || rval.to_bool()),
82+
TokenKind::And => Ok(lval.to_bool() && rval.to_bool()),
7783
_ => Err(format!("unknown operator: {:?}", op)),
7884
}?;
7985
Ok(Evalue::Bool(eval))
8086
}
81-
super::parser::Expr::Number(n) => Ok(Evalue::Number(*n)),
87+
Expr::Number(n) => Ok(Evalue::Number(*n)),
8288
super::parser::Expr::Variable(v) => match vars.get(v) {
8389
Some(v) => Ok(v.clone()),
8490
None => Err(format!("Unknown variable `{}`", v)),
@@ -124,5 +130,9 @@ mod test {
124130
assert_eq!(true, result.unwrap());
125131
let result = Evaluator::new().parse_and_evaluate("type != 'Run'", &map);
126132
assert_eq!(false, result.unwrap());
133+
let result = Evaluator::new().parse_and_evaluate("2024-01-06 > 2020-01-06", &map);
134+
assert_eq!(true, result.unwrap());
135+
let result = Evaluator::new().parse_and_evaluate("2024-01-06 < 2020-01-06", &map);
136+
assert_eq!(false, result.unwrap());
127137
}
128138
}

src/expr/lexer.rs

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
pub enum TokenKind {
88
True,
99
String,
10+
Date,
1011
False,
1112
Number,
1213
Contains,
@@ -63,7 +64,7 @@ impl Lexer<'_> {
6364
'\0' => self.spawn_token(TokenKind::Eol, self.pos),
6465
_ => {
6566
if is_number(c) {
66-
return self.parse_number();
67+
return self.parse_number_or_date();
6768
}
6869

6970
if is_name(c) {
@@ -114,15 +115,26 @@ impl Lexer<'_> {
114115
}
115116
}
116117

117-
fn parse_number(&mut self) -> Token {
118+
fn parse_number_or_date(&mut self) -> Token {
119+
if self.peek(4) == '-' {
120+
return self.parse_date();
121+
}
118122
let start = self.pos;
119-
while is_number(self.current()) {
123+
while is_number(self.current()) || self.current() == '-' {
120124
self.advance()
121125
}
122126

123127
self.spawn_token(TokenKind::Number, start)
124128
}
125129

130+
fn parse_date(&mut self) -> Token {
131+
let start = self.pos;
132+
while is_number(self.current()) || self.current() == '-' {
133+
self.advance()
134+
}
135+
self.spawn_token(TokenKind::Date, start)
136+
}
137+
126138
fn parse_name(&mut self) -> Token {
127139
let mut length = 0;
128140
while is_name(self.peek(length)) {
@@ -252,6 +264,11 @@ mod test {
252264
assert_eq!(TokenKind::Unkown, Lexer::new("' ").next().kind);
253265
}
254266

267+
#[test]
268+
pub fn lex_date() {
269+
assert_eq!(TokenKind::Date, Lexer::new("2024-01-01").next().kind);
270+
}
271+
255272
#[test]
256273
pub fn lex_expression() {
257274
let mut l = Lexer::new("distance > 10m");

src/expr/parser.rs

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
use std::str::FromStr;
2+
3+
use chrono::NaiveDate;
4+
15
use super::lexer::{Lexer, Token, TokenKind};
26

37
#[derive(PartialEq, Debug, Clone)]
@@ -7,6 +11,7 @@ pub enum Expr {
711
Variable(String),
812
Boolean(bool),
913
String(String),
14+
Date(NaiveDate),
1015
}
1116

1217
pub struct Parser<'a> {
@@ -40,7 +45,14 @@ impl Parser<'_> {
4045
let value = self.lexer.token_value(&token);
4146
Ok(Expr::Variable(value.to_string()))
4247
}
43-
_ => Err(format!("unknown left token: {:?} at {}", token.kind, token.start)),
48+
TokenKind::Date => match NaiveDate::from_str(self.lexer.token_value(&token)) {
49+
Ok(d) => Ok(Expr::Date(d)),
50+
Err(_) => Err("Could not parse date".to_string()),
51+
},
52+
_ => Err(format!(
53+
"unknown left token: {:?} at {}",
54+
token.kind, token.start
55+
)),
4456
}?;
4557

4658
let mut next_t = self.lexer.next();
@@ -128,5 +140,10 @@ mod test {
128140
),
129141
Parser::new("variable > 20 and 10 < 30").parse().unwrap()
130142
);
143+
assert_eq!(Expr::Number(10.0), Parser::new("10").parse().unwrap());
144+
assert_eq!(
145+
Expr::Date(NaiveDate::from_ymd_opt(2024, 1, 1).unwrap()),
146+
Parser::new("2024-01-01").parse().unwrap(),
147+
);
131148
}
132149
}

src/store/activity.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use std::{cmp::Ordering, fmt::Display};
22

3-
use chrono::NaiveDateTime;
3+
use chrono::{NaiveDateTime, NaiveDate};
44
use crossterm::event::KeyCode;
55
use geo_types::LineString;
66
use serde::{Deserialize, Serialize};
@@ -216,6 +216,11 @@ impl Activities {
216216
("title".to_string(), Evalue::String(a.title.clone())),
217217
("elevation".to_string(), Evalue::Number(a.total_elevation_gain)),
218218
("time".to_string(), Evalue::Number(a.moving_time as f64)),
219+
("date".to_string(), Evalue::Date(
220+
a.start_date.unwrap_or(
221+
NaiveDateTime::default()
222+
).try_into().unwrap()
223+
)),
219224
])) {
220225
Ok(v) => v,
221226
Err(_) => false,

0 commit comments

Comments
 (0)