ok/jj
1
0
Fork 0
forked from mirrors/jj

templater: parameterize property type

This commit is contained in:
Yuya Nishihara 2023-02-17 23:12:26 +09:00
parent 907bddaf1e
commit 5474268d22

View file

@ -584,47 +584,43 @@ fn expand_aliases<'i>(
/// Callbacks to build language-specific evaluation objects from AST nodes. /// Callbacks to build language-specific evaluation objects from AST nodes.
trait TemplateLanguage<'a> { trait TemplateLanguage<'a> {
type Context: 'a; type Context: 'a;
// TODO: type Property; type Property: IntoTemplateProperty<'a, Self::Context>;
fn wrap_string( fn wrap_string(
&self, &self,
property: Box<dyn TemplateProperty<Self::Context, Output = String> + 'a>, property: Box<dyn TemplateProperty<Self::Context, Output = String> + 'a>,
) -> Property<'a, Self::Context>; ) -> Self::Property;
fn wrap_boolean( fn wrap_boolean(
&self, &self,
property: Box<dyn TemplateProperty<Self::Context, Output = bool> + 'a>, property: Box<dyn TemplateProperty<Self::Context, Output = bool> + 'a>,
) -> Property<'a, Self::Context>; ) -> Self::Property;
fn wrap_integer( fn wrap_integer(
&self, &self,
property: Box<dyn TemplateProperty<Self::Context, Output = i64> + 'a>, property: Box<dyn TemplateProperty<Self::Context, Output = i64> + 'a>,
) -> Property<'a, Self::Context>; ) -> Self::Property;
fn wrap_commit_or_change_id( fn wrap_commit_or_change_id(
&self, &self,
property: Box<dyn TemplateProperty<Self::Context, Output = CommitOrChangeId<'a>> + 'a>, property: Box<dyn TemplateProperty<Self::Context, Output = CommitOrChangeId<'a>> + 'a>,
) -> Property<'a, Self::Context>; ) -> Self::Property;
fn wrap_shortest_id_prefix( fn wrap_shortest_id_prefix(
&self, &self,
property: Box<dyn TemplateProperty<Self::Context, Output = ShortestIdPrefix> + 'a>, property: Box<dyn TemplateProperty<Self::Context, Output = ShortestIdPrefix> + 'a>,
) -> Property<'a, Self::Context>; ) -> Self::Property;
fn wrap_signature( fn wrap_signature(
&self, &self,
property: Box<dyn TemplateProperty<Self::Context, Output = Signature> + 'a>, property: Box<dyn TemplateProperty<Self::Context, Output = Signature> + 'a>,
) -> Property<'a, Self::Context>; ) -> Self::Property;
fn wrap_timestamp( fn wrap_timestamp(
&self, &self,
property: Box<dyn TemplateProperty<Self::Context, Output = Timestamp> + 'a>, property: Box<dyn TemplateProperty<Self::Context, Output = Timestamp> + 'a>,
) -> Property<'a, Self::Context>; ) -> Self::Property;
fn build_keyword( fn build_keyword(&self, name: &str, span: pest::Span) -> TemplateParseResult<Self::Property>;
&self,
name: &str,
span: pest::Span,
) -> TemplateParseResult<Property<'a, Self::Context>>;
fn build_method( fn build_method(
&self, &self,
property: Property<'a, Self::Context>, property: Self::Property,
function: &FunctionCallNode, function: &FunctionCallNode,
) -> TemplateParseResult<Property<'a, Self::Context>>; ) -> TemplateParseResult<Self::Property>;
} }
/// Provides access to basic template property types. /// Provides access to basic template property types.
@ -689,12 +685,12 @@ impl<'a, I: 'a> IntoTemplateProperty<'a, I> for Property<'a, I> {
} }
} }
enum Expression<'a, C> { enum Expression<'a, C, P> {
Property(Property<'a, C>, Vec<String>), Property(P, Vec<String>),
Template(Box<dyn Template<C> + 'a>), Template(Box<dyn Template<C> + 'a>),
} }
impl<'a, C: 'a> Expression<'a, C> { impl<'a, C: 'a, P: IntoTemplateProperty<'a, C>> Expression<'a, C, P> {
fn try_into_boolean(self) -> Option<Box<dyn TemplateProperty<C, Output = bool> + 'a>> { fn try_into_boolean(self) -> Option<Box<dyn TemplateProperty<C, Output = bool> + 'a>> {
match self { match self {
Expression::Property(property, _) => property.try_into_boolean(), Expression::Property(property, _) => property.try_into_boolean(),
@ -800,7 +796,7 @@ fn split_email(email: &str) -> (&str, Option<&str>) {
fn build_method_call<'a, L: TemplateLanguage<'a>>( fn build_method_call<'a, L: TemplateLanguage<'a>>(
language: &L, language: &L,
method: &MethodCallNode, method: &MethodCallNode,
) -> TemplateParseResult<Expression<'a, L::Context>> { ) -> TemplateParseResult<Expression<'a, L::Context, L::Property>> {
match build_expression(language, &method.object)? { match build_expression(language, &method.object)? {
Expression::Property(property, mut labels) => { Expression::Property(property, mut labels) => {
let property = language.build_method(property, &method.function)?; let property = language.build_method(property, &method.function)?;
@ -827,7 +823,7 @@ fn build_core_method<'a, L: TemplateLanguage<'a>>(
language: &L, language: &L,
property: Property<'a, L::Context>, property: Property<'a, L::Context>,
function: &FunctionCallNode, function: &FunctionCallNode,
) -> TemplateParseResult<Property<'a, L::Context>> { ) -> TemplateParseResult<L::Property> {
match property { match property {
Property::String(property) => build_string_method(language, property, function), Property::String(property) => build_string_method(language, property, function),
Property::Boolean(property) => build_boolean_method(language, property, function), Property::Boolean(property) => build_boolean_method(language, property, function),
@ -847,7 +843,7 @@ fn build_string_method<'a, L: TemplateLanguage<'a>>(
language: &L, language: &L,
self_property: impl TemplateProperty<L::Context, Output = String> + 'a, self_property: impl TemplateProperty<L::Context, Output = String> + 'a,
function: &FunctionCallNode, function: &FunctionCallNode,
) -> TemplateParseResult<Property<'a, L::Context>> { ) -> TemplateParseResult<L::Property> {
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)?;
@ -876,7 +872,7 @@ fn build_boolean_method<'a, L: TemplateLanguage<'a>>(
_language: &L, _language: &L,
_self_property: impl TemplateProperty<L::Context, Output = bool> + 'a, _self_property: impl TemplateProperty<L::Context, Output = bool> + 'a,
function: &FunctionCallNode, function: &FunctionCallNode,
) -> TemplateParseResult<Property<'a, L::Context>> { ) -> TemplateParseResult<L::Property> {
Err(TemplateParseError::no_such_method("Boolean", function)) Err(TemplateParseError::no_such_method("Boolean", function))
} }
@ -884,7 +880,7 @@ fn build_integer_method<'a, L: TemplateLanguage<'a>>(
_language: &L, _language: &L,
_self_property: impl TemplateProperty<L::Context, Output = i64> + 'a, _self_property: impl TemplateProperty<L::Context, Output = i64> + 'a,
function: &FunctionCallNode, function: &FunctionCallNode,
) -> TemplateParseResult<Property<'a, L::Context>> { ) -> TemplateParseResult<L::Property> {
Err(TemplateParseError::no_such_method("Integer", function)) Err(TemplateParseError::no_such_method("Integer", function))
} }
@ -892,7 +888,7 @@ fn build_commit_or_change_id_method<'a, L: TemplateLanguage<'a>>(
language: &L, language: &L,
self_property: impl TemplateProperty<L::Context, Output = CommitOrChangeId<'a>> + 'a, self_property: impl TemplateProperty<L::Context, Output = CommitOrChangeId<'a>> + 'a,
function: &FunctionCallNode, function: &FunctionCallNode,
) -> TemplateParseResult<Property<'a, L::Context>> { ) -> TemplateParseResult<L::Property> {
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
@ -938,7 +934,7 @@ fn build_shortest_id_prefix_method<'a, L: TemplateLanguage<'a>>(
language: &L, language: &L,
self_property: impl TemplateProperty<L::Context, Output = ShortestIdPrefix> + 'a, self_property: impl TemplateProperty<L::Context, Output = ShortestIdPrefix> + 'a,
function: &FunctionCallNode, function: &FunctionCallNode,
) -> TemplateParseResult<Property<'a, L::Context>> { ) -> TemplateParseResult<L::Property> {
let property = match function.name { let property = match function.name {
"prefix" => { "prefix" => {
expect_no_arguments(function)?; expect_no_arguments(function)?;
@ -968,7 +964,7 @@ fn build_signature_method<'a, L: TemplateLanguage<'a>>(
language: &L, language: &L,
self_property: impl TemplateProperty<L::Context, Output = Signature> + 'a, self_property: impl TemplateProperty<L::Context, Output = Signature> + 'a,
function: &FunctionCallNode, function: &FunctionCallNode,
) -> TemplateParseResult<Property<'a, L::Context>> { ) -> TemplateParseResult<L::Property> {
let property = match function.name { let property = match function.name {
"name" => { "name" => {
expect_no_arguments(function)?; expect_no_arguments(function)?;
@ -1010,7 +1006,7 @@ fn build_timestamp_method<'a, L: TemplateLanguage<'a>>(
language: &L, language: &L,
self_property: impl TemplateProperty<L::Context, Output = Timestamp> + 'a, self_property: impl TemplateProperty<L::Context, Output = Timestamp> + 'a,
function: &FunctionCallNode, function: &FunctionCallNode,
) -> TemplateParseResult<Property<'a, L::Context>> { ) -> TemplateParseResult<L::Property> {
let property = match function.name { let property = match function.name {
"ago" => { "ago" => {
expect_no_arguments(function)?; expect_no_arguments(function)?;
@ -1027,7 +1023,7 @@ fn build_timestamp_method<'a, L: TemplateLanguage<'a>>(
fn build_global_function<'a, L: TemplateLanguage<'a>>( fn build_global_function<'a, L: TemplateLanguage<'a>>(
language: &L, language: &L,
function: &FunctionCallNode, function: &FunctionCallNode,
) -> TemplateParseResult<Expression<'a, L::Context>> { ) -> TemplateParseResult<Expression<'a, L::Context, L::Property>> {
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)?;
@ -1125,7 +1121,7 @@ fn build_commit_keyword<'a>(
fn build_expression<'a, L: TemplateLanguage<'a>>( fn build_expression<'a, L: TemplateLanguage<'a>>(
language: &L, language: &L,
node: &ExpressionNode, node: &ExpressionNode,
) -> TemplateParseResult<Expression<'a, L::Context>> { ) -> TemplateParseResult<Expression<'a, L::Context, L::Property>> {
match &node.kind { match &node.kind {
ExpressionKind::Identifier(name) => { ExpressionKind::Identifier(name) => {
let property = language.build_keyword(name, node.span)?; let property = language.build_keyword(name, node.span)?;
@ -1162,64 +1158,61 @@ struct CommitTemplateLanguage<'a, 'b> {
impl<'a> TemplateLanguage<'a> for CommitTemplateLanguage<'a, '_> { impl<'a> TemplateLanguage<'a> for CommitTemplateLanguage<'a, '_> {
type Context = Commit; type Context = Commit;
type Property = Property<'a, Commit>;
// TODO: maybe generate wrap_<type>() by macro? // TODO: maybe generate wrap_<type>() by macro?
fn wrap_string( fn wrap_string(
&self, &self,
property: Box<dyn TemplateProperty<Self::Context, Output = String> + 'a>, property: Box<dyn TemplateProperty<Self::Context, Output = String> + 'a>,
) -> Property<'a, Self::Context> { ) -> Self::Property {
Property::String(property) Property::String(property)
} }
fn wrap_boolean( fn wrap_boolean(
&self, &self,
property: Box<dyn TemplateProperty<Self::Context, Output = bool> + 'a>, property: Box<dyn TemplateProperty<Self::Context, Output = bool> + 'a>,
) -> Property<'a, Self::Context> { ) -> Self::Property {
Property::Boolean(property) Property::Boolean(property)
} }
fn wrap_integer( fn wrap_integer(
&self, &self,
property: Box<dyn TemplateProperty<Self::Context, Output = i64> + 'a>, property: Box<dyn TemplateProperty<Self::Context, Output = i64> + 'a>,
) -> Property<'a, Self::Context> { ) -> Self::Property {
Property::Integer(property) Property::Integer(property)
} }
fn wrap_commit_or_change_id( fn wrap_commit_or_change_id(
&self, &self,
property: Box<dyn TemplateProperty<Self::Context, Output = CommitOrChangeId<'a>> + 'a>, property: Box<dyn TemplateProperty<Self::Context, Output = CommitOrChangeId<'a>> + 'a>,
) -> Property<'a, Self::Context> { ) -> Self::Property {
Property::CommitOrChangeId(property) Property::CommitOrChangeId(property)
} }
fn wrap_shortest_id_prefix( fn wrap_shortest_id_prefix(
&self, &self,
property: Box<dyn TemplateProperty<Self::Context, Output = ShortestIdPrefix> + 'a>, property: Box<dyn TemplateProperty<Self::Context, Output = ShortestIdPrefix> + 'a>,
) -> Property<'a, Self::Context> { ) -> Self::Property {
Property::ShortestIdPrefix(property) Property::ShortestIdPrefix(property)
} }
fn wrap_signature( fn wrap_signature(
&self, &self,
property: Box<dyn TemplateProperty<Self::Context, Output = Signature> + 'a>, property: Box<dyn TemplateProperty<Self::Context, Output = Signature> + 'a>,
) -> Property<'a, Self::Context> { ) -> Self::Property {
Property::Signature(property) Property::Signature(property)
} }
fn wrap_timestamp( fn wrap_timestamp(
&self, &self,
property: Box<dyn TemplateProperty<Self::Context, Output = Timestamp> + 'a>, property: Box<dyn TemplateProperty<Self::Context, Output = Timestamp> + 'a>,
) -> Property<'a, Self::Context> { ) -> Self::Property {
Property::Timestamp(property) Property::Timestamp(property)
} }
fn build_keyword( fn build_keyword(&self, name: &str, span: pest::Span) -> TemplateParseResult<Self::Property> {
&self,
name: &str,
span: pest::Span,
) -> TemplateParseResult<Property<'a, Self::Context>> {
build_commit_keyword(self, name, span) build_commit_keyword(self, name, span)
} }
fn build_method( fn build_method(
&self, &self,
property: Property<'a, Self::Context>, property: Self::Property,
function: &FunctionCallNode, function: &FunctionCallNode,
) -> TemplateParseResult<Property<'a, Self::Context>> { ) -> TemplateParseResult<Self::Property> {
build_core_method(self, property, function) build_core_method(self, property, function)
} }
} }
@ -1245,23 +1238,24 @@ mod tests {
impl TemplateLanguage<'static> for MinimalTemplateLanguage { impl TemplateLanguage<'static> for MinimalTemplateLanguage {
type Context = (); type Context = ();
type Property = Property<'static, ()>;
fn wrap_string( fn wrap_string(
&self, &self,
property: Box<dyn TemplateProperty<Self::Context, Output = String> + 'static>, property: Box<dyn TemplateProperty<Self::Context, Output = String> + 'static>,
) -> Property<'static, Self::Context> { ) -> Self::Property {
Property::String(property) Property::String(property)
} }
fn wrap_boolean( fn wrap_boolean(
&self, &self,
property: Box<dyn TemplateProperty<Self::Context, Output = bool> + 'static>, property: Box<dyn TemplateProperty<Self::Context, Output = bool> + 'static>,
) -> Property<'static, Self::Context> { ) -> Self::Property {
Property::Boolean(property) Property::Boolean(property)
} }
fn wrap_integer( fn wrap_integer(
&self, &self,
property: Box<dyn TemplateProperty<Self::Context, Output = i64> + 'static>, property: Box<dyn TemplateProperty<Self::Context, Output = i64> + 'static>,
) -> Property<'static, Self::Context> { ) -> Self::Property {
Property::Integer(property) Property::Integer(property)
} }
fn wrap_commit_or_change_id( fn wrap_commit_or_change_id(
@ -1269,25 +1263,25 @@ mod tests {
property: Box< property: Box<
dyn TemplateProperty<Self::Context, Output = CommitOrChangeId<'static>> + 'static, dyn TemplateProperty<Self::Context, Output = CommitOrChangeId<'static>> + 'static,
>, >,
) -> Property<'static, Self::Context> { ) -> Self::Property {
Property::CommitOrChangeId(property) Property::CommitOrChangeId(property)
} }
fn wrap_shortest_id_prefix( fn wrap_shortest_id_prefix(
&self, &self,
property: Box<dyn TemplateProperty<Self::Context, Output = ShortestIdPrefix> + 'static>, property: Box<dyn TemplateProperty<Self::Context, Output = ShortestIdPrefix> + 'static>,
) -> Property<'static, Self::Context> { ) -> Self::Property {
Property::ShortestIdPrefix(property) Property::ShortestIdPrefix(property)
} }
fn wrap_signature( fn wrap_signature(
&self, &self,
property: Box<dyn TemplateProperty<Self::Context, Output = Signature> + 'static>, property: Box<dyn TemplateProperty<Self::Context, Output = Signature> + 'static>,
) -> Property<'static, Self::Context> { ) -> Self::Property {
Property::Signature(property) Property::Signature(property)
} }
fn wrap_timestamp( fn wrap_timestamp(
&self, &self,
property: Box<dyn TemplateProperty<Self::Context, Output = Timestamp> + 'static>, property: Box<dyn TemplateProperty<Self::Context, Output = Timestamp> + 'static>,
) -> Property<'static, Self::Context> { ) -> Self::Property {
Property::Timestamp(property) Property::Timestamp(property)
} }
@ -1295,15 +1289,15 @@ mod tests {
&self, &self,
name: &str, name: &str,
span: pest::Span, span: pest::Span,
) -> TemplateParseResult<Property<'static, Self::Context>> { ) -> TemplateParseResult<Self::Property> {
Err(TemplateParseError::no_such_keyword(name, span)) Err(TemplateParseError::no_such_keyword(name, span))
} }
fn build_method( fn build_method(
&self, &self,
property: Property<'static, Self::Context>, property: Self::Property,
function: &FunctionCallNode, function: &FunctionCallNode,
) -> TemplateParseResult<Property<'static, Self::Context>> { ) -> TemplateParseResult<Self::Property> {
build_core_method(self, property, function) build_core_method(self, property, function)
} }
} }
@ -1335,7 +1329,9 @@ mod tests {
WithTemplateAliasesMap(aliases_map) WithTemplateAliasesMap(aliases_map)
} }
fn parse(template_text: &str) -> TemplateParseResult<Expression<'static, ()>> { fn parse(
template_text: &str,
) -> TemplateParseResult<Expression<'static, (), Property<'static, ()>>> {
let node = parse_template(template_text)?; let node = parse_template(template_text)?;
build_expression(&MinimalTemplateLanguage, &node) build_expression(&MinimalTemplateLanguage, &node)
} }
@ -1420,7 +1416,9 @@ mod tests {
#[test] #[test]
fn test_integer_literal() { fn test_integer_literal() {
let extract = |x: Expression<()>| x.try_into_integer().unwrap().extract(&()); fn extract<'a>(x: Expression<'a, (), Property<'a, ()>>) -> i64 {
x.try_into_integer().unwrap().extract(&())
}
assert_eq!(extract(parse("0").unwrap()), 0); assert_eq!(extract(parse("0").unwrap()), 0);
assert_eq!(extract(parse("(42)").unwrap()), 42); assert_eq!(extract(parse("(42)").unwrap()), 42);