templater: add enum that holds property or template

This allows us to merge parse_boolean_commit_property() into
parse_commit_term(). We'll probably need similar type coercion methods
for the other basic types.

I considered adding something like Property::Template(), which could be
reused for map operation (e.g. 'revset().map(commit_id).join(" ")'.)
However, a mapped commit template would be different from the top-level
commit template regarding the lifetime of the context.
"Expression::<Commit>::Template()" takes "for<'b> &'b Commit" as an argument,
whereas a mapped template property would capture Commit object somewhere.
This commit is contained in:
Yuya Nishihara 2023-01-29 08:54:53 +09:00
parent 8d7ba8ddd3
commit 5eee7186ba

View file

@ -119,6 +119,20 @@ impl<'a, C: 'a> PropertyAndLabels<'a, C> {
} }
} }
enum Expression<'a, C> {
Property(PropertyAndLabels<'a, C>),
Template(Box<dyn Template<C> + 'a>),
}
impl<'a, C: 'a> Expression<'a, C> {
fn into_template(self) -> Box<dyn Template<C> + 'a> {
match self {
Expression::Property(property_labels) => property_labels.into_template(),
Expression::Template(template) => template,
}
}
}
fn parse_method_chain<'a, I: 'a>( fn parse_method_chain<'a, I: 'a>(
pair: Pair<Rule>, pair: Pair<Rule>,
input_property: PropertyAndLabels<'a, I>, input_property: PropertyAndLabels<'a, I>,
@ -274,7 +288,7 @@ fn parse_commit_term<'a>(
repo: RepoRef<'a>, repo: RepoRef<'a>,
workspace_id: &WorkspaceId, workspace_id: &WorkspaceId,
pair: Pair<Rule>, pair: Pair<Rule>,
) -> Box<dyn Template<Commit> + 'a> { ) -> Expression<'a, Commit> {
assert_eq!(pair.as_rule(), Rule::term); assert_eq!(pair.as_rule(), Rule::term);
let mut inner = pair.into_inner(); let mut inner = pair.into_inner();
let expr = inner.next().unwrap(); let expr = inner.next().unwrap();
@ -284,11 +298,11 @@ fn parse_commit_term<'a>(
Rule::literal => { Rule::literal => {
let text = parse_string_literal(expr); let text = parse_string_literal(expr);
let term = PropertyAndLabels(Property::String(Box::new(Literal(text))), vec![]); let term = PropertyAndLabels(Property::String(Box::new(Literal(text))), vec![]);
parse_method_chain(maybe_method, term).into_template() Expression::Property(parse_method_chain(maybe_method, term))
} }
Rule::identifier => { Rule::identifier => {
let term = parse_commit_keyword(repo, workspace_id, expr); let term = parse_commit_keyword(repo, workspace_id, expr);
parse_method_chain(maybe_method, term).into_template() Expression::Property(parse_method_chain(maybe_method, term))
} }
Rule::function => { Rule::function => {
let (name, mut args) = { let (name, mut args) = {
@ -302,7 +316,8 @@ fn parse_commit_term<'a>(
match name.as_str() { match name.as_str() {
"label" => { "label" => {
let label_pair = args.next().unwrap(); let label_pair = args.next().unwrap();
let label_template = parse_commit_template_rule(repo, workspace_id, label_pair); let label_template =
parse_commit_template_rule(repo, workspace_id, label_pair).into_template();
let arg_template = match args.next() { let arg_template = match args.next() {
None => panic!("label() requires two arguments"), None => panic!("label() requires two arguments"),
Some(pair) => pair, Some(pair) => pair,
@ -310,8 +325,8 @@ fn parse_commit_term<'a>(
if args.next().is_some() { if args.next().is_some() {
panic!("label() accepts only two arguments") panic!("label() accepts only two arguments")
} }
let content: Box<dyn Template<Commit> + 'a> = let content = parse_commit_template_rule(repo, workspace_id, arg_template)
parse_commit_template_rule(repo, workspace_id, arg_template); .into_template();
let get_labels = move |commit: &Commit| -> Vec<String> { let get_labels = move |commit: &Commit| -> Vec<String> {
let mut buf = vec![]; let mut buf = vec![];
let mut formatter = PlainTextFormatter::new(&mut buf); let mut formatter = PlainTextFormatter::new(&mut buf);
@ -322,7 +337,8 @@ fn parse_commit_term<'a>(
.map(ToString::to_string) .map(ToString::to_string)
.collect() .collect()
}; };
Box::new(DynamicLabelTemplate::new(content, get_labels)) let template = Box::new(DynamicLabelTemplate::new(content, get_labels));
Expression::Template(template)
} }
"if" => { "if" => {
let condition_pair = args.next().unwrap(); let condition_pair = args.next().unwrap();
@ -332,19 +348,22 @@ fn parse_commit_term<'a>(
let true_template = match args.next() { let true_template = match args.next() {
None => panic!("if() requires at least two arguments"), None => panic!("if() requires at least two arguments"),
Some(pair) => parse_commit_template_rule(repo, workspace_id, pair), Some(pair) => {
parse_commit_template_rule(repo, workspace_id, pair).into_template()
}
}; };
let false_template = args let false_template = args.next().map(|pair| {
.next() parse_commit_template_rule(repo, workspace_id, pair).into_template()
.map(|pair| parse_commit_template_rule(repo, workspace_id, pair)); });
if args.next().is_some() { if args.next().is_some() {
panic!("if() accepts at most three arguments") panic!("if() accepts at most three arguments")
} }
Box::new(ConditionalTemplate::new( let template = Box::new(ConditionalTemplate::new(
condition, condition,
true_template, true_template,
false_template, false_template,
)) ));
Expression::Template(template)
} }
name => panic!("function {name} not implemented"), name => panic!("function {name} not implemented"),
} }
@ -358,16 +377,17 @@ fn parse_commit_template_rule<'a>(
repo: RepoRef<'a>, repo: RepoRef<'a>,
workspace_id: &WorkspaceId, workspace_id: &WorkspaceId,
pair: Pair<Rule>, pair: Pair<Rule>,
) -> Box<dyn Template<Commit> + 'a> { ) -> Expression<'a, Commit> {
assert_eq!(pair.as_rule(), Rule::template); assert_eq!(pair.as_rule(), Rule::template);
let inner = pair.into_inner(); let inner = pair.into_inner();
let mut templates = inner let mut expressions = inner
.map(|term| parse_commit_term(repo, workspace_id, term)) .map(|term| parse_commit_term(repo, workspace_id, term))
.collect_vec(); .collect_vec();
if templates.len() == 1 { if expressions.len() == 1 {
templates.pop().unwrap() expressions.pop().unwrap()
} else { } else {
Box::new(ListTemplate(templates)) let templates = expressions.into_iter().map(|x| x.into_template()).collect();
Expression::Template(Box::new(ListTemplate(templates)))
} }
} }
@ -381,6 +401,6 @@ pub fn parse_commit_template<'a>(
if first_pair.as_rule() == Rule::EOI { if first_pair.as_rule() == Rule::EOI {
Box::new(Literal(String::new())) Box::new(Literal(String::new()))
} else { } else {
parse_commit_template_rule(repo, workspace_id, first_pair) parse_commit_template_rule(repo, workspace_id, first_pair).into_template()
} }
} }