Skip to content

Commit

Permalink
templates: Timestamp: add after, before methods
Browse files Browse the repository at this point in the history
This allows for more fine-grained control of timestamp formatting, for
example:

```
[template-aliases]
'format_timestamp(timestamp)' = '''
if(timestamp.before("1 week ago"),
  timestamp.format("%b %d %Y %H:%M"),
  timestamp.ago()
)
'''
```

Closes #3782.
  • Loading branch information
bnjmnt4n committed Oct 16, 2024
1 parent 11648ee commit 0f605b8
Show file tree
Hide file tree
Showing 4 changed files with 63 additions and 0 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,9 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
* New `coalesce(revsets...)` revset which returns commits in the first revset
in the `revsets` list that does not evaluate to `none()`.

* Timestamp objects in templates now have `after(date) -> Boolean` and
`before(date) -> Boolean` methods for comparing timestamps to other dates.

### Fixed bugs

* Error on `trunk()` revset resolution is now handled gracefully.
Expand Down
20 changes: 20 additions & 0 deletions cli/src/template_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ use itertools::Itertools as _;
use jj_lib::backend::Signature;
use jj_lib::backend::Timestamp;
use jj_lib::dsl_util::AliasExpandError as _;
use jj_lib::time_util::DatePattern;

use crate::template_parser;
use crate::template_parser::BinaryOp;
Expand Down Expand Up @@ -900,6 +901,25 @@ fn builtin_timestamp_methods<'a, L: TemplateLanguage<'a> + ?Sized>(
Ok(L::wrap_timestamp(out_property))
},
);
map.insert(
"after",
|_language, _diagnostics, _build_ctx, self_property, function| {
let [date_pattern_node] = function.expect_exact_arguments()?;
let now = chrono::Local::now();
let date_pattern = template_parser::expect_string_literal_with(
date_pattern_node,
|date_pattern, span| {
DatePattern::from_str_kind(date_pattern, function.name, now).map_err(|err| {
TemplateParseError::expression("Invalid date pattern", span)
.with_source(err)
})
},
)?;
let out_property = self_property.map(move |timestamp| date_pattern.matches(&timestamp));
Ok(L::wrap_boolean(out_property))
},
);
map.insert("before", map["after"]);
map
}

Expand Down
38 changes: 38 additions & 0 deletions cli/tests/test_commit_template.rs
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,44 @@ fn test_log_author_timestamp_local() {
"###);
}

#[test]
fn test_log_author_timestamp_after_before() {
let test_env = TestEnvironment::default();
test_env.jj_cmd_ok(test_env.env_root(), &["git", "init", "repo"]);
let repo_path = test_env.env_root().join("repo");

test_env.jj_cmd_ok(&repo_path, &["describe", "-m", "first"]);

let template = r#"
separate(" ",
author.timestamp(),
":",
if(author.timestamp().after("1969"), "(after 1969)", "(before 1969)"),
if(author.timestamp().before("1975"), "(before 1975)", "(after 1975)"),
if(author.timestamp().before("now"), "(before now)", "(after now)")
) ++ "\n""#;
let stdout = test_env.jj_cmd_success(&repo_path, &["log", "--no-graph", "-T", template]);
insta::assert_snapshot!(stdout, @r#"
2001-02-03 04:05:08.000 +07:00 : (after 1969) (after 1975) (before now)
1970-01-01 00:00:00.000 +00:00 : (after 1969) (before 1975) (before now)
"#);

// Should display error with invalid date.
let template = r#"author.timestamp().after("invalid date")"#;
let stderr = test_env.jj_cmd_failure(&repo_path, &["log", "-r@", "--no-graph", "-T", template]);
insta::assert_snapshot!(stderr, @r#"
Error: Failed to parse template: Invalid date pattern
Caused by:
1: --> 1:26
|
1 | author.timestamp().after("invalid date")
| ^------------^
|
= Invalid date pattern
2: expected week day or month name
"#);
}

#[test]
fn test_mine_is_true_when_author_is_user() {
let test_env = TestEnvironment::default();
Expand Down
2 changes: 2 additions & 0 deletions docs/templates.md
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,8 @@ The following methods are defined.
format string](https://docs.rs/chrono/latest/chrono/format/strftime/).
* `.utc() -> Timestamp`: Convert timestamp into UTC timezone.
* `.local() -> Timestamp`: Convert timestamp into local timezone.
* `.after(date: String) -> Boolean`: True if the timestamp is exactly at or after the given date.
* `.before(date: String) -> Boolean`: True if the timestamp is before, but not including, the given date.

### TimestampRange type

Expand Down

0 comments on commit 0f605b8

Please sign in to comment.