diff --git a/cli/src/commit_templater.rs b/cli/src/commit_templater.rs index 50e28ecc8f..dbd037b51a 100644 --- a/cli/src/commit_templater.rs +++ b/cli/src/commit_templater.rs @@ -54,8 +54,9 @@ impl<'repo> TemplateLanguage<'repo> for CommitTemplateLanguage<'repo, '_> { template_builder::impl_core_wrap_property_fns!('repo, CommitTemplatePropertyKind::Core); - fn build_keyword(&self, name: &str, span: pest::Span) -> TemplateParseResult { - build_commit_keyword(self, name, span) + fn build_self(&self) -> Self::Property { + // Commit object is lightweight (a few Arc + CommitId) + self.wrap_commit(TemplatePropertyFn(|commit: &Commit| commit.clone())) } fn build_method( @@ -236,21 +237,6 @@ impl CommitKeywordCache { } } -fn build_commit_keyword<'repo>( - language: &CommitTemplateLanguage<'repo, '_>, - name: &str, - span: pest::Span, -) -> TemplateParseResult> { - // Commit object is lightweight (a few Arc + CommitId), so just clone it - // to turn into a property type. Abstraction over "for<'a> (&'a T) -> &'a T" - // and "(&T) -> T" wouldn't be simple. If we want to remove Clone/Rc/Arc, - // maybe we can add an abstraction that takes "Fn(&Commit) -> O" and returns - // "TemplateProperty". - let property = TemplatePropertyFn(|commit: &Commit| commit.clone()); - build_commit_keyword_opt(language, property, name) - .ok_or_else(|| TemplateParseError::no_such_keyword(name, span)) -} - fn build_commit_method<'repo>( language: &CommitTemplateLanguage<'repo, '_>, _build_ctx: &BuildContext>, @@ -265,6 +251,7 @@ fn build_commit_method<'repo>( } } +// TODO: merge into build_commit_method() fn build_commit_keyword_opt<'repo>( language: &CommitTemplateLanguage<'repo, '_>, property: impl TemplateProperty + 'repo, diff --git a/cli/src/operation_templater.rs b/cli/src/operation_templater.rs index fd1192aa53..0d3981cee1 100644 --- a/cli/src/operation_templater.rs +++ b/cli/src/operation_templater.rs @@ -42,8 +42,9 @@ impl TemplateLanguage<'static> for OperationTemplateLanguage<'_> { template_builder::impl_core_wrap_property_fns!('static, OperationTemplatePropertyKind::Core); - fn build_keyword(&self, name: &str, span: pest::Span) -> TemplateParseResult { - build_operation_keyword(self, name, span) + fn build_self(&self) -> Self::Property { + // Operation object is lightweight (a few Arc + OperationId) + self.wrap_operation(TemplatePropertyFn(|op: &Operation| op.clone())) } fn build_method( @@ -67,7 +68,6 @@ impl TemplateLanguage<'static> for OperationTemplateLanguage<'_> { } impl OperationTemplateLanguage<'_> { - #[allow(unused)] // TODO fn wrap_operation( &self, property: impl TemplateProperty + 'static, @@ -124,18 +124,6 @@ impl IntoTemplateProperty<'static, Operation> for OperationTemplatePropertyKind } } -fn build_operation_keyword( - language: &OperationTemplateLanguage, - name: &str, - span: pest::Span, -) -> TemplateParseResult { - // Operation object is lightweight (a few Arc + OperationId), so just clone - // it to turn into a property type. - let property = TemplatePropertyFn(|op: &Operation| op.clone()); - build_operation_keyword_opt(language, property, name) - .ok_or_else(|| TemplateParseError::no_such_keyword(name, span)) -} - fn build_operation_method( language: &OperationTemplateLanguage, _build_ctx: &BuildContext, @@ -150,6 +138,7 @@ fn build_operation_method( } } +// TODO: merge into build_operation_method() fn build_operation_keyword_opt( language: &OperationTemplateLanguage, property: impl TemplateProperty + 'static, diff --git a/cli/src/template_builder.rs b/cli/src/template_builder.rs index 075a5c21dd..8bf8d46339 100644 --- a/cli/src/template_builder.rs +++ b/cli/src/template_builder.rs @@ -68,7 +68,10 @@ pub trait TemplateLanguage<'a> { template: Box + 'a>, ) -> Self::Property; - fn build_keyword(&self, name: &str, span: pest::Span) -> TemplateParseResult; + /// Creates the `self` template property, which is usually a function that + /// clones the `Context` object. + fn build_self(&self) -> Self::Property; + fn build_method( &self, build_ctx: &BuildContext, @@ -272,6 +275,28 @@ pub struct BuildContext<'i, P> { local_variables: HashMap<&'i str, &'i (dyn Fn() -> P)>, } +fn build_keyword<'a, L: TemplateLanguage<'a>>( + language: &L, + build_ctx: &BuildContext, + name: &str, + name_span: pest::Span<'_>, +) -> TemplateParseResult> { + // Keyword is a 0-ary method on the "self" property + let self_property = language.build_self(); + let function = FunctionCallNode { + name, + name_span, + args: vec![], + args_span: name_span.end_pos().span(&name_span.end_pos()), + }; + let property = language + .build_method(build_ctx, self_property, &function) + // Since keyword is a 0-ary method, any argument-related errors mean + // there's no such keyword. + .map_err(|_| TemplateParseError::no_such_keyword(name, name_span))?; + Ok(Expression::with_label(property, name)) +} + fn build_unary_operation<'a, L: TemplateLanguage<'a>>( language: &L, build_ctx: &BuildContext, @@ -842,8 +867,7 @@ pub fn build_expression<'a, L: TemplateLanguage<'a>>( // Don't label a local variable with its name Ok(Expression::unlabeled(make())) } else { - let property = language.build_keyword(name, node.span)?; - Ok(Expression::with_label(property, *name)) + build_keyword(language, build_ctx, name, node.span) } } ExpressionKind::Boolean(value) => { @@ -960,15 +984,8 @@ mod tests { impl_core_wrap_property_fns!('static, TestTemplatePropertyKind::Core); - fn build_keyword( - &self, - name: &str, - span: pest::Span, - ) -> TemplateParseResult { - self.keywords - .get(name) - .map(|f| f(self)) - .ok_or_else(|| TemplateParseError::no_such_keyword(name, span)) + fn build_self(&self) -> Self::Property { + TestTemplatePropertyKind::Unit } fn build_method( @@ -995,7 +1012,6 @@ mod tests { enum TestTemplatePropertyKind { Core(CoreTemplatePropertyKind<'static, ()>), - #[allow(unused)] // TODO Unit, }