Skip to content

Commit f6557c7

Browse files
committed
Repurpose before, after filters to operate on date
Suggested-by: Vincent Prouillet <[email protected]>
1 parent 10d68b1 commit f6557c7

File tree

2 files changed

+77
-121
lines changed

2 files changed

+77
-121
lines changed

components/templates/src/filters.rs

Lines changed: 57 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -168,63 +168,39 @@ pub fn before<S: BuildHasher>(
168168
value: &Value,
169169
args: &HashMap<String, Value, S>,
170170
) -> TeraResult<Value> {
171-
let inclusive = args.get("inclusive").and_then(Value::as_bool).unwrap_or(false);
172-
filter_by_date(
173-
value,
174-
args,
175-
"before",
176-
if inclusive { |d: &str, t: &str| d <= t } else { |d: &str, t: &str| d < t },
177-
)
178-
}
171+
let date_str = try_get_value!("before", "value", String, value);
172+
let threshold_str = match args.get("date") {
173+
Some(val) => try_get_value!("before", "date", String, val),
174+
None => return Err(TeraError::msg("Filter `before` expected an arg called `date`")),
175+
};
176+
177+
let item_date = parse_filter_date(&date_str)
178+
.ok_or_else(|| TeraError::msg(format!("Filter `before`: invalid date '{}'", date_str)))?;
179+
let threshold_date = parse_filter_date(&threshold_str).ok_or_else(|| {
180+
TeraError::msg(format!("Filter `before`: invalid date '{}'", threshold_str))
181+
})?;
179182

180-
pub fn after<S: BuildHasher>(value: &Value, args: &HashMap<String, Value, S>) -> TeraResult<Value> {
181183
let inclusive = args.get("inclusive").and_then(Value::as_bool).unwrap_or(false);
182-
filter_by_date(
183-
value,
184-
args,
185-
"after",
186-
if inclusive { |d: &str, t: &str| d >= t } else { |d: &str, t: &str| d > t },
187-
)
184+
let result = if inclusive { item_date <= threshold_date } else { item_date < threshold_date };
185+
Ok(to_value(result).unwrap())
188186
}
189187

190-
fn filter_by_date<S: BuildHasher>(
191-
value: &Value,
192-
args: &HashMap<String, Value, S>,
193-
dir: &str,
194-
compare: impl Fn(&str, &str) -> bool,
195-
) -> TeraResult<Value> {
196-
let date_str = match args.get("date") {
197-
Some(val) => try_get_value!(dir, "date", String, val),
198-
None => {
199-
return Err(TeraError::msg(format!("Filter `{}` expected an arg called `date`", dir)));
200-
}
188+
pub fn after<S: BuildHasher>(value: &Value, args: &HashMap<String, Value, S>) -> TeraResult<Value> {
189+
let date_str = try_get_value!("after", "value", String, value);
190+
let threshold_str = match args.get("date") {
191+
Some(val) => try_get_value!("after", "date", String, val),
192+
None => return Err(TeraError::msg("Filter `after` expected an arg called `date`")),
201193
};
202194

203-
let threshold_date = parse_filter_date(&date_str).ok_or_else(|| {
204-
TeraError::msg(format!(
205-
"Filter `{}`: invalid date format '{}'. Expected YYYY-MM-DD or RFC3339.",
206-
dir, date_str
207-
))
195+
let item_date = parse_filter_date(&date_str)
196+
.ok_or_else(|| TeraError::msg(format!("Filter `after`: invalid date '{}'", date_str)))?;
197+
let threshold_date = parse_filter_date(&threshold_str).ok_or_else(|| {
198+
TeraError::msg(format!("Filter `after`: invalid date '{}'", threshold_str))
208199
})?;
209200

210-
let items = value
211-
.as_array()
212-
.ok_or_else(|| TeraError::msg(format!("Filter `{}` expected an array", dir)))?;
213-
214-
let filtered: Vec<Value> = items
215-
.iter()
216-
.filter(|item| {
217-
item.as_object()
218-
.and_then(|obj| obj.get("date"))
219-
.and_then(Value::as_str)
220-
.and_then(parse_filter_date)
221-
.map(|item_date| compare(&item_date, &threshold_date))
222-
.unwrap_or(false)
223-
})
224-
.cloned()
225-
.collect();
226-
227-
Ok(to_value(filtered).unwrap())
201+
let inclusive = args.get("inclusive").and_then(Value::as_bool).unwrap_or(false);
202+
let result = if inclusive { item_date >= threshold_date } else { item_date > threshold_date };
203+
Ok(to_value(result).unwrap())
228204
}
229205

230206
fn parse_filter_date(date_str: &str) -> Option<String> {
@@ -442,75 +418,51 @@ mod tests {
442418

443419
#[test]
444420
fn before_filter() {
445-
use libs::serde_json::json;
446-
let pages = vec![
447-
json!({ "date": "2023-12-01T12:00:00Z" }),
448-
json!({ "date": "2024-01-01" }),
449-
json!({ "date": "2024-03-01" }),
450-
json!({ "date": "2024-05-01T12:00:00Z" }),
451-
json!({}),
452-
];
453-
let [dec2023, jan2024, mar2024, may2024, none] = &pages[..] else { unreachable!() };
454-
455421
let mut args = HashMap::new();
456422
args.insert("date".to_string(), to_value("2024-03-01").unwrap());
457423

458-
let result = super::before(&to_value(&pages).unwrap(), &args);
459-
assert!(result.is_ok());
460-
let filtered = result.unwrap().as_array().unwrap().clone();
461-
assert_eq!(filtered.len(), 2);
462-
assert!(filtered.contains(dec2023));
463-
assert!(filtered.contains(jan2024));
464-
assert!(!filtered.contains(mar2024));
465-
assert!(!filtered.contains(may2024));
466-
assert!(!filtered.contains(none));
424+
assert_eq!(
425+
super::before(&to_value("2024-01-01").unwrap(), &args).unwrap(),
426+
to_value(true).unwrap()
427+
);
428+
assert_eq!(
429+
super::before(&to_value("2024-05-01").unwrap(), &args).unwrap(),
430+
to_value(false).unwrap()
431+
);
432+
assert_eq!(
433+
super::before(&to_value("2024-03-01").unwrap(), &args).unwrap(),
434+
to_value(false).unwrap()
435+
);
467436

468437
args.insert("inclusive".to_string(), to_value(true).unwrap());
469-
let result = super::before(&to_value(&pages).unwrap(), &args);
470-
assert!(result.is_ok());
471-
let filtered = result.unwrap().as_array().unwrap().clone();
472-
assert_eq!(filtered.len(), 3);
473-
assert!(filtered.contains(dec2023));
474-
assert!(filtered.contains(jan2024));
475-
assert!(filtered.contains(mar2024));
476-
assert!(!filtered.contains(may2024));
477-
assert!(!filtered.contains(none));
438+
assert_eq!(
439+
super::before(&to_value("2024-03-01").unwrap(), &args).unwrap(),
440+
to_value(true).unwrap()
441+
);
478442
}
479443

480444
#[test]
481445
fn after_filter() {
482-
use libs::serde_json::json;
483-
let pages = vec![
484-
json!({ "date": "2023-12-01T12:00:00Z" }),
485-
json!({ "date": "2024-01-01" }),
486-
json!({ "date": "2024-03-01" }),
487-
json!({ "date": "2024-05-01T12:00:00Z" }),
488-
json!({}),
489-
];
490-
let [dec2023, jan2024, mar2024, may2024, none] = &pages[..] else { unreachable!() };
491-
492446
let mut args = HashMap::new();
493447
args.insert("date".to_string(), to_value("2024-03-01").unwrap());
494448

495-
let result = super::after(&to_value(&pages).unwrap(), &args);
496-
assert!(result.is_ok());
497-
let filtered = result.unwrap().as_array().unwrap().clone();
498-
assert_eq!(filtered.len(), 1);
499-
assert!(!filtered.contains(dec2023));
500-
assert!(!filtered.contains(jan2024));
501-
assert!(!filtered.contains(mar2024));
502-
assert!(filtered.contains(may2024));
503-
assert!(!filtered.contains(none));
449+
assert_eq!(
450+
super::after(&to_value("2024-01-01").unwrap(), &args).unwrap(),
451+
to_value(false).unwrap()
452+
);
453+
assert_eq!(
454+
super::after(&to_value("2024-05-01").unwrap(), &args).unwrap(),
455+
to_value(true).unwrap()
456+
);
457+
assert_eq!(
458+
super::after(&to_value("2024-03-01").unwrap(), &args).unwrap(),
459+
to_value(false).unwrap()
460+
);
504461

505462
args.insert("inclusive".to_string(), to_value(true).unwrap());
506-
let result = super::after(&to_value(&pages).unwrap(), &args);
507-
assert!(result.is_ok());
508-
let filtered = result.unwrap().as_array().unwrap().clone();
509-
assert_eq!(filtered.len(), 2);
510-
assert!(!filtered.contains(dec2023));
511-
assert!(!filtered.contains(jan2024));
512-
assert!(filtered.contains(mar2024));
513-
assert!(filtered.contains(may2024));
514-
assert!(!filtered.contains(none));
463+
assert_eq!(
464+
super::after(&to_value("2024-03-01").unwrap(), &args).unwrap(),
465+
to_value(true).unwrap()
466+
);
515467
}
516468
}

docs/content/documentation/templates/overview.md

Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -112,35 +112,39 @@ To format a number for a specific locale, you can use the `locale` argument and
112112
```
113113

114114
### before
115-
Filters an array of pages/sections to keep only items with a `date` field before the specified date. Items without a `date` field are excluded.
115+
Returns `true` if a date is before the specified date, `false` otherwise.
116116

117-
By default, the filter is exclusive (items matching the date are excluded). Set `inclusive=true` to include items matching the date.
117+
By default, the filter is exclusive (the boundary date returns `false`). Set `inclusive=true` to include the boundary date.
118118

119119
```jinja2
120-
{% for page in section.pages | before(date="2020-03-10") %}
121-
<h2>{{ page.title }}</h2>
120+
{% for page in section.pages %}
121+
{% if page.date | before(date="2020-03-10") %}
122+
<h2>{{ page.title }}</h2>
123+
{% endif %}
122124
{% endfor %}
123125
124-
{# Include pages from 2020-03-10 and earlier #}
125-
{% for page in section.pages | before(date="2020-03-10", inclusive=true) %}
126-
<h2>{{ page.title }}</h2>
127-
{% endfor %}
126+
{# Include the boundary date #}
127+
{% if page.date | before(date="2020-03-10", inclusive=true) %}
128+
...
129+
{% endif %}
128130
```
129131

130132
### after
131-
Filters an array of pages/sections to keep only items with a `date` field after the specified date. Items without a `date` field are excluded.
133+
Returns `true` if a date is after the specified date, `false` otherwise.
132134

133-
By default, the filter is exclusive (items matching the date are excluded). Set `inclusive=true` to include items matching the date.
135+
By default, the filter is exclusive (the boundary date returns `false`). Set `inclusive=true` to include the boundary date.
134136

135137
```jinja2
136-
{% for page in section.pages | after(date="2020-03-10") %}
137-
<h2>{{ page.title }}</h2>
138+
{% for page in section.pages %}
139+
{% if page.date | after(date="2020-03-10") %}
140+
<h2>{{ page.title }}</h2>
141+
{% endif %}
138142
{% endfor %}
139143
140-
{# Include pages from 2020-03-10 and later #}
141-
{% for page in section.pages | after(date="2020-03-10", inclusive=true) %}
142-
<h2>{{ page.title }}</h2>
143-
{% endfor %}
144+
{# Include the boundary date #}
145+
{% if page.date | after(date="2020-03-10", inclusive=true) %}
146+
...
147+
{% endif %}
144148
```
145149

146150
## Built-in functions

0 commit comments

Comments
 (0)