forked from mirrors/jj
templater: introduce trait that abstracts property kind and keywords
This trait will provide ways to dispatch keyword/method nodes, and wrap TemplateProperty object with a dedicated "Property" enum. build_keyword() and context parameter "I"/"C" have been migrated to it.
This commit is contained in:
parent
92f9fe5a1b
commit
3361130df4
1 changed files with 116 additions and 77 deletions
|
@ -581,6 +581,18 @@ fn expand_aliases<'i>(
|
||||||
expand_node(node, state)
|
expand_node(node, state)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Callbacks to build language-specific evaluation objects from AST nodes.
|
||||||
|
trait TemplateLanguage<'a> {
|
||||||
|
type Context: 'a;
|
||||||
|
// TODO: type Property;
|
||||||
|
|
||||||
|
fn build_keyword(
|
||||||
|
&self,
|
||||||
|
name: &str,
|
||||||
|
span: pest::Span,
|
||||||
|
) -> TemplateParseResult<Property<'a, Self::Context>>;
|
||||||
|
}
|
||||||
|
|
||||||
enum Property<'a, I> {
|
enum Property<'a, I> {
|
||||||
String(Box<dyn TemplateProperty<I, Output = String> + 'a>),
|
String(Box<dyn TemplateProperty<I, Output = String> + 'a>),
|
||||||
Boolean(Box<dyn TemplateProperty<I, Output = bool> + 'a>),
|
Boolean(Box<dyn TemplateProperty<I, Output = bool> + 'a>),
|
||||||
|
@ -742,13 +754,13 @@ fn split_email(email: &str) -> (&str, Option<&str>) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_method_call<'a, I: 'a>(
|
fn build_method_call<'a, L: TemplateLanguage<'a>>(
|
||||||
|
language: &L,
|
||||||
method: &MethodCallNode,
|
method: &MethodCallNode,
|
||||||
build_keyword: &impl Fn(&str, pest::Span) -> TemplateParseResult<Property<'a, I>>,
|
) -> TemplateParseResult<Expression<'a, L::Context>> {
|
||||||
) -> TemplateParseResult<Expression<'a, I>> {
|
match build_expression(language, &method.object)? {
|
||||||
match build_expression(&method.object, build_keyword)? {
|
|
||||||
Expression::Property(property, mut labels) => {
|
Expression::Property(property, mut labels) => {
|
||||||
let property = build_core_method(property, &method.function, build_keyword)?;
|
let property = build_core_method(language, property, &method.function)?;
|
||||||
labels.push(method.function.name.to_owned());
|
labels.push(method.function.name.to_owned());
|
||||||
Ok(Expression::Property(property, labels))
|
Ok(Expression::Property(property, labels))
|
||||||
}
|
}
|
||||||
|
@ -768,36 +780,36 @@ fn chain_properties<'a, I: 'a, J: 'a, O: 'a>(
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_core_method<'a, I: 'a>(
|
fn build_core_method<'a, L: TemplateLanguage<'a>>(
|
||||||
property: Property<'a, I>,
|
language: &L,
|
||||||
|
property: Property<'a, L::Context>,
|
||||||
function: &FunctionCallNode,
|
function: &FunctionCallNode,
|
||||||
build_keyword: &impl Fn(&str, pest::Span) -> TemplateParseResult<Property<'a, I>>,
|
) -> TemplateParseResult<Property<'a, L::Context>> {
|
||||||
) -> TemplateParseResult<Property<'a, I>> {
|
|
||||||
match property {
|
match property {
|
||||||
Property::String(property) => build_string_method(property, function, build_keyword),
|
Property::String(property) => build_string_method(language, property, function),
|
||||||
Property::Boolean(property) => build_boolean_method(property, function, build_keyword),
|
Property::Boolean(property) => build_boolean_method(language, property, function),
|
||||||
Property::Integer(property) => build_integer_method(property, function, build_keyword),
|
Property::Integer(property) => build_integer_method(language, property, function),
|
||||||
Property::CommitOrChangeId(property) => {
|
Property::CommitOrChangeId(property) => {
|
||||||
build_commit_or_change_id_method(property, function, build_keyword)
|
build_commit_or_change_id_method(language, property, function)
|
||||||
}
|
}
|
||||||
Property::ShortestIdPrefix(property) => {
|
Property::ShortestIdPrefix(property) => {
|
||||||
build_shortest_id_prefix_method(property, function, build_keyword)
|
build_shortest_id_prefix_method(language, property, function)
|
||||||
}
|
}
|
||||||
Property::Signature(property) => build_signature_method(property, function, build_keyword),
|
Property::Signature(property) => build_signature_method(language, property, function),
|
||||||
Property::Timestamp(property) => build_timestamp_method(property, function, build_keyword),
|
Property::Timestamp(property) => build_timestamp_method(language, property, function),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_string_method<'a, I: 'a>(
|
fn build_string_method<'a, L: TemplateLanguage<'a>>(
|
||||||
self_property: impl TemplateProperty<I, Output = String> + 'a,
|
language: &L,
|
||||||
|
self_property: impl TemplateProperty<L::Context, Output = String> + 'a,
|
||||||
function: &FunctionCallNode,
|
function: &FunctionCallNode,
|
||||||
build_keyword: &impl Fn(&str, pest::Span) -> TemplateParseResult<Property<'a, I>>,
|
) -> TemplateParseResult<Property<'a, L::Context>> {
|
||||||
) -> TemplateParseResult<Property<'a, I>> {
|
|
||||||
let property = match function.name {
|
let property = match function.name {
|
||||||
"contains" => {
|
"contains" => {
|
||||||
let [needle_node] = expect_exact_arguments(function)?;
|
let [needle_node] = expect_exact_arguments(function)?;
|
||||||
// TODO: or .try_into_string() to disable implicit type cast?
|
// TODO: or .try_into_string() to disable implicit type cast?
|
||||||
let needle_property = build_expression(needle_node, build_keyword)?.into_plain_text();
|
let needle_property = build_expression(language, needle_node)?.into_plain_text();
|
||||||
Property::Boolean(chain_properties(
|
Property::Boolean(chain_properties(
|
||||||
(self_property, needle_property),
|
(self_property, needle_property),
|
||||||
TemplatePropertyFn(|(haystack, needle): &(String, String)| {
|
TemplatePropertyFn(|(haystack, needle): &(String, String)| {
|
||||||
|
@ -817,32 +829,32 @@ fn build_string_method<'a, I: 'a>(
|
||||||
Ok(property)
|
Ok(property)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_boolean_method<'a, I: 'a>(
|
fn build_boolean_method<'a, L: TemplateLanguage<'a>>(
|
||||||
_self_property: impl TemplateProperty<I, Output = bool> + 'a,
|
_language: &L,
|
||||||
|
_self_property: impl TemplateProperty<L::Context, Output = bool> + 'a,
|
||||||
function: &FunctionCallNode,
|
function: &FunctionCallNode,
|
||||||
_build_keyword: &impl Fn(&str, pest::Span) -> TemplateParseResult<Property<'a, I>>,
|
) -> TemplateParseResult<Property<'a, L::Context>> {
|
||||||
) -> TemplateParseResult<Property<'a, I>> {
|
|
||||||
Err(TemplateParseError::no_such_method("Boolean", function))
|
Err(TemplateParseError::no_such_method("Boolean", function))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_integer_method<'a, I: 'a>(
|
fn build_integer_method<'a, L: TemplateLanguage<'a>>(
|
||||||
_self_property: impl TemplateProperty<I, Output = i64> + 'a,
|
_language: &L,
|
||||||
|
_self_property: impl TemplateProperty<L::Context, Output = i64> + 'a,
|
||||||
function: &FunctionCallNode,
|
function: &FunctionCallNode,
|
||||||
_build_keyword: &impl Fn(&str, pest::Span) -> TemplateParseResult<Property<'a, I>>,
|
) -> TemplateParseResult<Property<'a, L::Context>> {
|
||||||
) -> TemplateParseResult<Property<'a, I>> {
|
|
||||||
Err(TemplateParseError::no_such_method("Integer", function))
|
Err(TemplateParseError::no_such_method("Integer", function))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_commit_or_change_id_method<'a, I: 'a>(
|
fn build_commit_or_change_id_method<'a, L: TemplateLanguage<'a>>(
|
||||||
self_property: impl TemplateProperty<I, Output = CommitOrChangeId<'a>> + 'a,
|
language: &L,
|
||||||
|
self_property: impl TemplateProperty<L::Context, Output = CommitOrChangeId<'a>> + 'a,
|
||||||
function: &FunctionCallNode,
|
function: &FunctionCallNode,
|
||||||
build_keyword: &impl Fn(&str, pest::Span) -> TemplateParseResult<Property<'a, I>>,
|
) -> TemplateParseResult<Property<'a, L::Context>> {
|
||||||
) -> TemplateParseResult<Property<'a, I>> {
|
|
||||||
let parse_optional_integer = |function| -> Result<Option<_>, TemplateParseError> {
|
let parse_optional_integer = |function| -> Result<Option<_>, TemplateParseError> {
|
||||||
let ([], [len_node]) = expect_arguments(function)?;
|
let ([], [len_node]) = expect_arguments(function)?;
|
||||||
len_node
|
len_node
|
||||||
.map(|node| {
|
.map(|node| {
|
||||||
build_expression(node, build_keyword).and_then(|p| {
|
build_expression(language, node).and_then(|p| {
|
||||||
p.try_into_integer().ok_or_else(|| {
|
p.try_into_integer().ok_or_else(|| {
|
||||||
TemplateParseError::invalid_argument_type("Integer", node.span)
|
TemplateParseError::invalid_argument_type("Integer", node.span)
|
||||||
})
|
})
|
||||||
|
@ -879,11 +891,11 @@ fn build_commit_or_change_id_method<'a, I: 'a>(
|
||||||
Ok(property)
|
Ok(property)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_shortest_id_prefix_method<'a, I: 'a>(
|
fn build_shortest_id_prefix_method<'a, L: TemplateLanguage<'a>>(
|
||||||
self_property: impl TemplateProperty<I, Output = ShortestIdPrefix> + 'a,
|
_language: &L,
|
||||||
|
self_property: impl TemplateProperty<L::Context, Output = ShortestIdPrefix> + 'a,
|
||||||
function: &FunctionCallNode,
|
function: &FunctionCallNode,
|
||||||
_build_keyword: &impl Fn(&str, pest::Span) -> TemplateParseResult<Property<'a, I>>,
|
) -> TemplateParseResult<Property<'a, L::Context>> {
|
||||||
) -> TemplateParseResult<Property<'a, I>> {
|
|
||||||
let property = match function.name {
|
let property = match function.name {
|
||||||
"prefix" => {
|
"prefix" => {
|
||||||
expect_no_arguments(function)?;
|
expect_no_arguments(function)?;
|
||||||
|
@ -909,11 +921,11 @@ fn build_shortest_id_prefix_method<'a, I: 'a>(
|
||||||
Ok(property)
|
Ok(property)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_signature_method<'a, I: 'a>(
|
fn build_signature_method<'a, L: TemplateLanguage<'a>>(
|
||||||
self_property: impl TemplateProperty<I, Output = Signature> + 'a,
|
_language: &L,
|
||||||
|
self_property: impl TemplateProperty<L::Context, Output = Signature> + 'a,
|
||||||
function: &FunctionCallNode,
|
function: &FunctionCallNode,
|
||||||
_build_keyword: &impl Fn(&str, pest::Span) -> TemplateParseResult<Property<'a, I>>,
|
) -> TemplateParseResult<Property<'a, L::Context>> {
|
||||||
) -> TemplateParseResult<Property<'a, I>> {
|
|
||||||
let property = match function.name {
|
let property = match function.name {
|
||||||
"name" => {
|
"name" => {
|
||||||
expect_no_arguments(function)?;
|
expect_no_arguments(function)?;
|
||||||
|
@ -951,11 +963,11 @@ fn build_signature_method<'a, I: 'a>(
|
||||||
Ok(property)
|
Ok(property)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_timestamp_method<'a, I: 'a>(
|
fn build_timestamp_method<'a, L: TemplateLanguage<'a>>(
|
||||||
self_property: impl TemplateProperty<I, Output = Timestamp> + 'a,
|
_language: &L,
|
||||||
|
self_property: impl TemplateProperty<L::Context, Output = Timestamp> + 'a,
|
||||||
function: &FunctionCallNode,
|
function: &FunctionCallNode,
|
||||||
_build_keyword: &impl Fn(&str, pest::Span) -> TemplateParseResult<Property<'a, I>>,
|
) -> TemplateParseResult<Property<'a, L::Context>> {
|
||||||
) -> TemplateParseResult<Property<'a, I>> {
|
|
||||||
let property = match function.name {
|
let property = match function.name {
|
||||||
"ago" => {
|
"ago" => {
|
||||||
expect_no_arguments(function)?;
|
expect_no_arguments(function)?;
|
||||||
|
@ -969,15 +981,15 @@ fn build_timestamp_method<'a, I: 'a>(
|
||||||
Ok(property)
|
Ok(property)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_global_function<'a, C: 'a>(
|
fn build_global_function<'a, L: TemplateLanguage<'a>>(
|
||||||
|
language: &L,
|
||||||
function: &FunctionCallNode,
|
function: &FunctionCallNode,
|
||||||
build_keyword: &impl Fn(&str, pest::Span) -> TemplateParseResult<Property<'a, C>>,
|
) -> TemplateParseResult<Expression<'a, L::Context>> {
|
||||||
) -> TemplateParseResult<Expression<'a, C>> {
|
|
||||||
let expression = match function.name {
|
let expression = match function.name {
|
||||||
"label" => {
|
"label" => {
|
||||||
let [label_node, content_node] = expect_exact_arguments(function)?;
|
let [label_node, content_node] = expect_exact_arguments(function)?;
|
||||||
let label_property = build_expression(label_node, build_keyword)?.into_plain_text();
|
let label_property = build_expression(language, label_node)?.into_plain_text();
|
||||||
let content = build_expression(content_node, build_keyword)?.into_template();
|
let content = build_expression(language, content_node)?.into_template();
|
||||||
let labels = TemplateFunction::new(label_property, |s| {
|
let labels = TemplateFunction::new(label_property, |s| {
|
||||||
s.split_whitespace().map(ToString::to_string).collect()
|
s.split_whitespace().map(ToString::to_string).collect()
|
||||||
});
|
});
|
||||||
|
@ -986,14 +998,14 @@ fn build_global_function<'a, C: 'a>(
|
||||||
}
|
}
|
||||||
"if" => {
|
"if" => {
|
||||||
let ([condition_node, true_node], [false_node]) = expect_arguments(function)?;
|
let ([condition_node, true_node], [false_node]) = expect_arguments(function)?;
|
||||||
let condition = build_expression(condition_node, build_keyword)?
|
let condition = build_expression(language, condition_node)?
|
||||||
.try_into_boolean()
|
.try_into_boolean()
|
||||||
.ok_or_else(|| {
|
.ok_or_else(|| {
|
||||||
TemplateParseError::invalid_argument_type("Boolean", condition_node.span)
|
TemplateParseError::invalid_argument_type("Boolean", condition_node.span)
|
||||||
})?;
|
})?;
|
||||||
let true_template = build_expression(true_node, build_keyword)?.into_template();
|
let true_template = build_expression(language, true_node)?.into_template();
|
||||||
let false_template = false_node
|
let false_template = false_node
|
||||||
.map(|node| build_expression(node, build_keyword))
|
.map(|node| build_expression(language, node))
|
||||||
.transpose()?
|
.transpose()?
|
||||||
.map(|x| x.into_template());
|
.map(|x| x.into_template());
|
||||||
let template = Box::new(ConditionalTemplate::new(
|
let template = Box::new(ConditionalTemplate::new(
|
||||||
|
@ -1005,10 +1017,10 @@ fn build_global_function<'a, C: 'a>(
|
||||||
}
|
}
|
||||||
"separate" => {
|
"separate" => {
|
||||||
let ([separator_node], content_nodes) = expect_some_arguments(function)?;
|
let ([separator_node], content_nodes) = expect_some_arguments(function)?;
|
||||||
let separator = build_expression(separator_node, build_keyword)?.into_template();
|
let separator = build_expression(language, separator_node)?.into_template();
|
||||||
let contents = content_nodes
|
let contents = content_nodes
|
||||||
.iter()
|
.iter()
|
||||||
.map(|node| build_expression(node, build_keyword).map(|x| x.into_template()))
|
.map(|node| build_expression(language, node).map(|x| x.into_template()))
|
||||||
.try_collect()?;
|
.try_collect()?;
|
||||||
let template = Box::new(SeparateTemplate::new(separator, contents));
|
let template = Box::new(SeparateTemplate::new(separator, contents));
|
||||||
Expression::Template(template)
|
Expression::Template(template)
|
||||||
|
@ -1019,8 +1031,7 @@ fn build_global_function<'a, C: 'a>(
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_commit_keyword<'a>(
|
fn build_commit_keyword<'a>(
|
||||||
repo: &'a dyn Repo,
|
language: &CommitTemplateLanguage<'a, '_>,
|
||||||
workspace_id: &WorkspaceId,
|
|
||||||
name: &str,
|
name: &str,
|
||||||
span: pest::Span,
|
span: pest::Span,
|
||||||
) -> TemplateParseResult<Property<'a, Commit>> {
|
) -> TemplateParseResult<Property<'a, Commit>> {
|
||||||
|
@ -1029,6 +1040,7 @@ fn build_commit_keyword<'a>(
|
||||||
) -> Box<dyn TemplateProperty<Commit, Output = O> + 'a> {
|
) -> Box<dyn TemplateProperty<Commit, Output = O> + 'a> {
|
||||||
Box::new(TemplatePropertyFn(f))
|
Box::new(TemplatePropertyFn(f))
|
||||||
}
|
}
|
||||||
|
let repo = language.repo;
|
||||||
let property = match name {
|
let property = match name {
|
||||||
"description" => Property::String(wrap_fn(|commit| {
|
"description" => Property::String(wrap_fn(|commit| {
|
||||||
cli_util::complete_newline(commit.description())
|
cli_util::complete_newline(commit.description())
|
||||||
|
@ -1043,7 +1055,7 @@ fn build_commit_keyword<'a>(
|
||||||
"committer" => Property::Signature(wrap_fn(|commit| commit.committer().clone())),
|
"committer" => Property::Signature(wrap_fn(|commit| commit.committer().clone())),
|
||||||
"working_copies" => Property::String(Box::new(WorkingCopiesProperty { repo })),
|
"working_copies" => Property::String(Box::new(WorkingCopiesProperty { repo })),
|
||||||
"current_working_copy" => {
|
"current_working_copy" => {
|
||||||
let workspace_id = workspace_id.clone();
|
let workspace_id = language.workspace_id.clone();
|
||||||
Property::Boolean(wrap_fn(move |commit| {
|
Property::Boolean(wrap_fn(move |commit| {
|
||||||
Some(commit.id()) == repo.view().get_wc_commit_id(&workspace_id)
|
Some(commit.id()) == repo.view().get_wc_commit_id(&workspace_id)
|
||||||
}))
|
}))
|
||||||
|
@ -1067,13 +1079,13 @@ fn build_commit_keyword<'a>(
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Builds template evaluation tree from AST nodes.
|
/// Builds template evaluation tree from AST nodes.
|
||||||
fn build_expression<'a, C: 'a>(
|
fn build_expression<'a, L: TemplateLanguage<'a>>(
|
||||||
|
language: &L,
|
||||||
node: &ExpressionNode,
|
node: &ExpressionNode,
|
||||||
build_keyword: &impl Fn(&str, pest::Span) -> TemplateParseResult<Property<'a, C>>,
|
) -> TemplateParseResult<Expression<'a, L::Context>> {
|
||||||
) -> TemplateParseResult<Expression<'a, C>> {
|
|
||||||
match &node.kind {
|
match &node.kind {
|
||||||
ExpressionKind::Identifier(name) => {
|
ExpressionKind::Identifier(name) => {
|
||||||
let property = build_keyword(name, node.span)?;
|
let property = language.build_keyword(name, node.span)?;
|
||||||
let labels = vec![(*name).to_owned()];
|
let labels = vec![(*name).to_owned()];
|
||||||
Ok(Expression::Property(property, labels))
|
Ok(Expression::Property(property, labels))
|
||||||
}
|
}
|
||||||
|
@ -1088,30 +1100,45 @@ fn build_expression<'a, C: 'a>(
|
||||||
ExpressionKind::List(nodes) => {
|
ExpressionKind::List(nodes) => {
|
||||||
let templates = nodes
|
let templates = nodes
|
||||||
.iter()
|
.iter()
|
||||||
.map(|node| build_expression(node, build_keyword).map(|x| x.into_template()))
|
.map(|node| build_expression(language, node).map(|x| x.into_template()))
|
||||||
.try_collect()?;
|
.try_collect()?;
|
||||||
Ok(Expression::Template(Box::new(ListTemplate(templates))))
|
Ok(Expression::Template(Box::new(ListTemplate(templates))))
|
||||||
}
|
}
|
||||||
ExpressionKind::FunctionCall(function) => build_global_function(function, build_keyword),
|
ExpressionKind::FunctionCall(function) => build_global_function(language, function),
|
||||||
ExpressionKind::MethodCall(method) => build_method_call(method, build_keyword),
|
ExpressionKind::MethodCall(method) => build_method_call(language, method),
|
||||||
ExpressionKind::AliasExpanded(id, subst) => build_expression(subst, build_keyword)
|
ExpressionKind::AliasExpanded(id, subst) => {
|
||||||
.map_err(|e| e.within_alias_expansion(*id, node.span)),
|
build_expression(language, subst).map_err(|e| e.within_alias_expansion(*id, node.span))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct CommitTemplateLanguage<'a, 'b> {
|
||||||
|
repo: &'a dyn Repo,
|
||||||
|
workspace_id: &'b WorkspaceId,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> TemplateLanguage<'a> for CommitTemplateLanguage<'a, '_> {
|
||||||
|
type Context = Commit;
|
||||||
|
|
||||||
|
fn build_keyword(
|
||||||
|
&self,
|
||||||
|
name: &str,
|
||||||
|
span: pest::Span,
|
||||||
|
) -> TemplateParseResult<Property<'a, Self::Context>> {
|
||||||
|
build_commit_keyword(self, name, span)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: We'll probably need a trait that abstracts the Property enum and
|
|
||||||
// keyword/method parsing functions per the top-level context.
|
|
||||||
pub fn parse_commit_template<'a>(
|
pub fn parse_commit_template<'a>(
|
||||||
repo: &'a dyn Repo,
|
repo: &'a dyn Repo,
|
||||||
workspace_id: &WorkspaceId,
|
workspace_id: &WorkspaceId,
|
||||||
template_text: &str,
|
template_text: &str,
|
||||||
aliases_map: &TemplateAliasesMap,
|
aliases_map: &TemplateAliasesMap,
|
||||||
) -> TemplateParseResult<Box<dyn Template<Commit> + 'a>> {
|
) -> TemplateParseResult<Box<dyn Template<Commit> + 'a>> {
|
||||||
|
let language = CommitTemplateLanguage { repo, workspace_id };
|
||||||
let node = parse_template(template_text)?;
|
let node = parse_template(template_text)?;
|
||||||
let node = expand_aliases(node, aliases_map)?;
|
let node = expand_aliases(node, aliases_map)?;
|
||||||
let expression = build_expression(&node, &|name, span| {
|
let expression = build_expression(&language, &node)?;
|
||||||
build_commit_keyword(repo, workspace_id, name, span)
|
|
||||||
})?;
|
|
||||||
Ok(expression.into_template())
|
Ok(expression.into_template())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1119,6 +1146,20 @@ pub fn parse_commit_template<'a>(
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
struct MinimalTemplateLanguage;
|
||||||
|
|
||||||
|
impl TemplateLanguage<'static> for MinimalTemplateLanguage {
|
||||||
|
type Context = ();
|
||||||
|
|
||||||
|
fn build_keyword(
|
||||||
|
&self,
|
||||||
|
name: &str,
|
||||||
|
span: pest::Span,
|
||||||
|
) -> TemplateParseResult<Property<'static, Self::Context>> {
|
||||||
|
Err(TemplateParseError::no_such_keyword(name, span))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct WithTemplateAliasesMap(TemplateAliasesMap);
|
struct WithTemplateAliasesMap(TemplateAliasesMap);
|
||||||
|
|
||||||
|
@ -1146,11 +1187,9 @@ mod tests {
|
||||||
WithTemplateAliasesMap(aliases_map)
|
WithTemplateAliasesMap(aliases_map)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse(template_text: &str) -> TemplateParseResult<Expression<()>> {
|
fn parse(template_text: &str) -> TemplateParseResult<Expression<'static, ()>> {
|
||||||
let node = parse_template(template_text)?;
|
let node = parse_template(template_text)?;
|
||||||
build_expression(&node, &|name, span| {
|
build_expression(&MinimalTemplateLanguage, &node)
|
||||||
Err(TemplateParseError::no_such_keyword(name, span))
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_normalized(template_text: &str) -> TemplateParseResult<ExpressionNode> {
|
fn parse_normalized(template_text: &str) -> TemplateParseResult<ExpressionNode> {
|
||||||
|
|
Loading…
Reference in a new issue