templater: process property composition by parse_<type>_method()

This prepares for adding method arguments support. Since a method argument
should be evaluated in the surrounding scope, its type will be
'TemplateProperty<I>', not 'TemplateProperty<J>' for the receiver type 'J'.
Then, the receiver of type 'TemplateProperty<I, Output = J>', and arguments
of type 'TemplateProperty<I, Output = Pn>' will be composed to an input of
type 'TemplateProperty<I, Output = (J, Pn...)>'.

wrap_fn() is removed since it's only useful for nullary methods, and we
no longer need Box<dyn> to abstract the return value.
This commit is contained in:
Yuya Nishihara 2023-02-03 20:28:44 +09:00
parent bdf809c6c3
commit db172aa7c9

View file

@ -187,29 +187,6 @@ enum Property<'a, I> {
} }
impl<'a, I: 'a> Property<'a, I> { impl<'a, I: 'a> Property<'a, I> {
fn after<C: 'a>(self, first: Box<dyn TemplateProperty<C, Output = I> + 'a>) -> Property<'a, C> {
fn chain<'a, C: 'a, I: 'a, O: 'a>(
first: Box<dyn TemplateProperty<C, Output = I> + 'a>,
second: Box<dyn TemplateProperty<I, Output = O> + 'a>,
) -> Box<dyn TemplateProperty<C, Output = O> + 'a> {
Box::new(TemplateFunction::new(first, move |value| {
second.extract(&value)
}))
}
match self {
Property::String(property) => Property::String(chain(first, property)),
Property::Boolean(property) => Property::Boolean(chain(first, property)),
Property::CommitOrChangeId(property) => {
Property::CommitOrChangeId(chain(first, property))
}
Property::IdWithHighlightedPrefix(property) => {
Property::IdWithHighlightedPrefix(chain(first, property))
}
Property::Signature(property) => Property::Signature(chain(first, property)),
Property::Timestamp(property) => Property::Timestamp(chain(first, property)),
}
}
fn try_into_boolean(self) -> Option<Box<dyn TemplateProperty<I, Output = bool> + 'a>> { fn try_into_boolean(self) -> Option<Box<dyn TemplateProperty<I, Output = bool> + 'a>> {
match self { match self {
Property::String(property) => { Property::String(property) => {
@ -306,10 +283,10 @@ fn parse_method_chain<'a, I: 'a>(
}; };
labels.push(name.as_str().to_owned()); labels.push(name.as_str().to_owned());
property = match property { property = match property {
Property::String(property) => parse_string_method(name, args_pair)?.after(property), Property::String(property) => parse_string_method(property, name, args_pair)?,
Property::Boolean(property) => parse_boolean_method(name, args_pair)?.after(property), Property::Boolean(property) => parse_boolean_method(property, name, args_pair)?,
Property::CommitOrChangeId(property) => { Property::CommitOrChangeId(property) => {
parse_commit_or_change_id_method(name, args_pair)?.after(property) parse_commit_or_change_id_method(property, name, args_pair)?
} }
Property::IdWithHighlightedPrefix(_property) => { Property::IdWithHighlightedPrefix(_property) => {
return Err(TemplateParseError::no_such_method( return Err(TemplateParseError::no_such_method(
@ -317,61 +294,65 @@ fn parse_method_chain<'a, I: 'a>(
&name, &name,
)); ));
} }
Property::Signature(property) => { Property::Signature(property) => parse_signature_method(property, name, args_pair)?,
parse_signature_method(name, args_pair)?.after(property) Property::Timestamp(property) => parse_timestamp_method(property, name, args_pair)?,
}
Property::Timestamp(property) => {
parse_timestamp_method(name, args_pair)?.after(property)
}
}; };
} }
Ok(PropertyAndLabels(property, labels)) Ok(PropertyAndLabels(property, labels))
} }
fn parse_string_method<'a>( fn chain_properties<'a, I: 'a, J: 'a, O: 'a>(
first: impl TemplateProperty<I, Output = J> + 'a,
second: impl TemplateProperty<J, Output = O> + 'a,
) -> Box<dyn TemplateProperty<I, Output = O> + 'a> {
Box::new(TemplateFunction::new(first, move |value| {
second.extract(&value)
}))
}
fn parse_string_method<'a, I: 'a>(
self_property: impl TemplateProperty<I, Output = String> + 'a,
name: Pair<Rule>, name: Pair<Rule>,
_args_pair: Pair<Rule>, _args_pair: Pair<Rule>,
) -> TemplateParseResult<Property<'a, String>> { ) -> TemplateParseResult<Property<'a, I>> {
fn wrap_fn<'a, O>(
f: impl Fn(&String) -> O + 'a,
) -> Box<dyn TemplateProperty<String, Output = O> + 'a> {
Box::new(TemplatePropertyFn(f))
}
// TODO: validate arguments // TODO: validate arguments
let property = match name.as_str() { let property = match name.as_str() {
"first_line" => Property::String(wrap_fn(|s| { "first_line" => Property::String(chain_properties(
s.lines().next().unwrap_or_default().to_string() self_property,
})), TemplatePropertyFn(|s: &String| s.lines().next().unwrap_or_default().to_string()),
)),
_ => return Err(TemplateParseError::no_such_method("String", &name)), _ => return Err(TemplateParseError::no_such_method("String", &name)),
}; };
Ok(property) Ok(property)
} }
fn parse_boolean_method<'a>( fn parse_boolean_method<'a, I: 'a>(
_self_property: impl TemplateProperty<I, Output = bool> + 'a,
name: Pair<Rule>, name: Pair<Rule>,
_args_pair: Pair<Rule>, _args_pair: Pair<Rule>,
) -> TemplateParseResult<Property<'a, bool>> { ) -> TemplateParseResult<Property<'a, I>> {
Err(TemplateParseError::no_such_method("Boolean", &name)) Err(TemplateParseError::no_such_method("Boolean", &name))
} }
fn parse_commit_or_change_id_method<'a>( fn parse_commit_or_change_id_method<'a, I: 'a>(
self_property: impl TemplateProperty<I, Output = CommitOrChangeId<'a>> + 'a,
name: Pair<Rule>, name: Pair<Rule>,
_args_pair: Pair<Rule>, _args_pair: Pair<Rule>,
) -> TemplateParseResult<Property<'a, CommitOrChangeId<'a>>> { ) -> TemplateParseResult<Property<'a, I>> {
fn wrap_fn<'a, O>(
f: impl Fn(&CommitOrChangeId<'a>) -> O + 'a,
) -> Box<dyn TemplateProperty<CommitOrChangeId<'a>, Output = O> + 'a> {
Box::new(TemplatePropertyFn(f))
}
// TODO: validate arguments // TODO: validate arguments
let property = match name.as_str() { let property = match name.as_str() {
"short" => Property::String(wrap_fn(|id| id.short())), "short" => Property::String(chain_properties(
"shortest_prefix_and_brackets" => { self_property,
Property::String(wrap_fn(|id| id.shortest_prefix_and_brackets())) TemplatePropertyFn(|id: &CommitOrChangeId| id.short()),
} )),
"shortest_styled_prefix" => { "shortest_prefix_and_brackets" => Property::String(chain_properties(
Property::IdWithHighlightedPrefix(wrap_fn(|id| id.shortest_styled_prefix())) self_property,
} TemplatePropertyFn(|id: &CommitOrChangeId| id.shortest_prefix_and_brackets()),
)),
"shortest_styled_prefix" => Property::IdWithHighlightedPrefix(chain_properties(
self_property,
TemplatePropertyFn(|id: &CommitOrChangeId| id.shortest_styled_prefix()),
)),
_ => { _ => {
return Err(TemplateParseError::no_such_method( return Err(TemplateParseError::no_such_method(
"CommitOrChangeId", "CommitOrChangeId",
@ -382,37 +363,41 @@ fn parse_commit_or_change_id_method<'a>(
Ok(property) Ok(property)
} }
fn parse_signature_method<'a>( fn parse_signature_method<'a, I: 'a>(
self_property: impl TemplateProperty<I, Output = Signature> + 'a,
name: Pair<Rule>, name: Pair<Rule>,
_args_pair: Pair<Rule>, _args_pair: Pair<Rule>,
) -> TemplateParseResult<Property<'a, Signature>> { ) -> TemplateParseResult<Property<'a, I>> {
fn wrap_fn<'a, O>(
f: impl Fn(&Signature) -> O + 'a,
) -> Box<dyn TemplateProperty<Signature, Output = O> + 'a> {
Box::new(TemplatePropertyFn(f))
}
// TODO: validate arguments // TODO: validate arguments
let property = match name.as_str() { let property = match name.as_str() {
"name" => Property::String(wrap_fn(|signature| signature.name.clone())), "name" => Property::String(chain_properties(
"email" => Property::String(wrap_fn(|signature| signature.email.clone())), self_property,
"timestamp" => Property::Timestamp(wrap_fn(|signature| signature.timestamp.clone())), TemplatePropertyFn(|signature: &Signature| signature.name.clone()),
)),
"email" => Property::String(chain_properties(
self_property,
TemplatePropertyFn(|signature: &Signature| signature.email.clone()),
)),
"timestamp" => Property::Timestamp(chain_properties(
self_property,
TemplatePropertyFn(|signature: &Signature| signature.timestamp.clone()),
)),
_ => return Err(TemplateParseError::no_such_method("Signature", &name)), _ => return Err(TemplateParseError::no_such_method("Signature", &name)),
}; };
Ok(property) Ok(property)
} }
fn parse_timestamp_method<'a>( fn parse_timestamp_method<'a, I: 'a>(
self_property: impl TemplateProperty<I, Output = Timestamp> + 'a,
name: Pair<Rule>, name: Pair<Rule>,
_args_pair: Pair<Rule>, _args_pair: Pair<Rule>,
) -> TemplateParseResult<Property<'a, Timestamp>> { ) -> TemplateParseResult<Property<'a, I>> {
fn wrap_fn<'a, O>(
f: impl Fn(&Timestamp) -> O + 'a,
) -> Box<dyn TemplateProperty<Timestamp, Output = O> + 'a> {
Box::new(TemplatePropertyFn(f))
}
// TODO: validate arguments // TODO: validate arguments
let property = match name.as_str() { let property = match name.as_str() {
"ago" => Property::String(wrap_fn(time_util::format_timestamp_relative_to_now)), "ago" => Property::String(chain_properties(
self_property,
TemplatePropertyFn(time_util::format_timestamp_relative_to_now),
)),
_ => return Err(TemplateParseError::no_such_method("Timestamp", &name)), _ => return Err(TemplateParseError::no_such_method("Timestamp", &name)),
}; };
Ok(property) Ok(property)