templater: add wrapper that turns template back to string property

I have no idea whether or not any template expressions are intentionally
allowed as a label, but it makes sense to write something like
'label("phase-" phase, ...)' (if we had a phase keyword.) So I decided to
add .into_plain_text() instead of stricter .try_into_string().
This commit is contained in:
Yuya Nishihara 2023-01-26 20:20:43 +09:00
parent a42ce8773c
commit 1911734acc
3 changed files with 77 additions and 11 deletions

View file

@ -22,12 +22,12 @@ use pest::iterators::{Pair, Pairs};
use pest::Parser;
use pest_derive::Parser;
use crate::formatter::PlainTextFormatter;
use crate::templater::{
BranchProperty, CommitOrChangeId, ConditionalTemplate, DynamicLabelTemplate,
FormattablePropertyTemplate, GitHeadProperty, GitRefsProperty, IdWithHighlightedPrefix,
IsWorkingCopyProperty, LabelTemplate, ListTemplate, Literal, TagProperty, Template,
TemplateFunction, TemplateProperty, TemplatePropertyFn, WorkingCopiesProperty,
IsWorkingCopyProperty, LabelTemplate, ListTemplate, Literal, PlainTextFormattedProperty,
TagProperty, Template, TemplateFunction, TemplateProperty, TemplatePropertyFn,
WorkingCopiesProperty,
};
use crate::{cli_util, time_util};
@ -98,6 +98,13 @@ impl<'a, I: 'a> Property<'a, I> {
}
}
fn into_plain_text(self) -> Box<dyn TemplateProperty<I, Output = String> + 'a> {
match self {
Property::String(property) => property,
_ => Box::new(PlainTextFormattedProperty::new(self.into_template())),
}
}
fn into_template(self) -> Box<dyn Template<I> + 'a> {
fn wrap<'a, I: 'a, O: Template<()> + 'a>(
property: Box<dyn TemplateProperty<I, Output = O> + 'a>,
@ -141,6 +148,13 @@ impl<'a, C: 'a> Expression<'a, C> {
}
}
fn into_plain_text(self) -> Box<dyn TemplateProperty<C, Output = String> + 'a> {
match self {
Expression::Property(PropertyAndLabels(property, _)) => property.into_plain_text(),
Expression::Template(template) => Box::new(PlainTextFormattedProperty::new(template)),
}
}
fn into_template(self) -> Box<dyn Template<C> + 'a> {
match self {
Expression::Property(property_labels) => property_labels.into_template(),
@ -330,8 +344,8 @@ fn parse_commit_term<'a>(
match name.as_str() {
"label" => {
let label_pair = args.next().unwrap();
let label_template =
parse_commit_template_rule(repo, workspace_id, label_pair).into_template();
let label_property = parse_commit_template_rule(repo, workspace_id, label_pair)
.into_plain_text();
let arg_template = match args.next() {
None => panic!("label() requires two arguments"),
Some(pair) => pair,
@ -342,11 +356,8 @@ fn parse_commit_term<'a>(
let content = parse_commit_template_rule(repo, workspace_id, arg_template)
.into_template();
let get_labels = move |commit: &Commit| -> Vec<String> {
let mut buf = vec![];
let mut formatter = PlainTextFormatter::new(&mut buf);
label_template.format(commit, &mut formatter).unwrap();
String::from_utf8(buf)
.unwrap()
label_property
.extract(commit)
.split_whitespace()
.map(ToString::to_string)
.collect()

View file

@ -21,7 +21,7 @@ use jujutsu_lib::commit::Commit;
use jujutsu_lib::op_store::WorkspaceId;
use jujutsu_lib::repo::RepoRef;
use crate::formatter::Formatter;
use crate::formatter::{Formatter, PlainTextFormatter};
use crate::time_util;
pub trait Template<C> {
@ -204,6 +204,30 @@ where
}
}
/// Adapter to turn template back to string property.
pub struct PlainTextFormattedProperty<T> {
template: T,
}
impl<T> PlainTextFormattedProperty<T> {
pub fn new(template: T) -> Self {
PlainTextFormattedProperty { template }
}
}
impl<C, T: Template<C>> TemplateProperty<C> for PlainTextFormattedProperty<T> {
type Output = String;
fn extract(&self, context: &C) -> Self::Output {
let mut output = vec![];
self.template
.format(context, &mut PlainTextFormatter::new(&mut output))
.expect("write() to PlainTextFormatter should never fail");
// TODO: Use from_utf8_lossy() if we added template that embeds file content
String::from_utf8(output).expect("template output should be utf-8 bytes")
}
}
pub struct WorkingCopiesProperty<'a> {
pub repo: RepoRef<'a>,
}

View file

@ -116,6 +116,37 @@ fn test_templater_string_method() {
insta::assert_snapshot!(render(r#""foo\nbar".first_line()"#), @"foo");
}
#[test]
fn test_templater_label_function() {
let test_env = TestEnvironment::default();
test_env.jj_cmd_success(test_env.env_root(), &["init", "repo", "--git"]);
let repo_path = test_env.env_root().join("repo");
let render = |template| {
test_env.jj_cmd_success(
&repo_path,
&[
"log",
"--color=always",
"--no-graph",
"-r@-",
"-T",
template,
],
)
};
// Literal
insta::assert_snapshot!(render(r#"label("error", "text")"#), @"text");
// Evaluated property
insta::assert_snapshot!(
render(r#"label("error".first_line(), "text")"#), @"text");
// Template
insta::assert_snapshot!(
render(r#"label(if(empty, "error", "warning"), "text")"#), @"text");
}
fn get_template_output(
test_env: &TestEnvironment,
repo_path: &Path,