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

templater: implement expect_*_arguments() as methods

I'm thinking of moving them to dsl_util, but we'll probably want to avoid
importing dsl_util at call sites.
This commit is contained in:
Yuya Nishihara 2024-05-21 12:27:04 +09:00
parent 06f488a8f6
commit 04efac3a51
7 changed files with 139 additions and 142 deletions

View file

@ -117,7 +117,7 @@ impl CommitTemplateLanguageExtension for HexCounter {
table.commit_methods.insert(
"has_most_digits",
|language, _build_context, property, call| {
template_parser::expect_no_arguments(call)?;
call.expect_no_arguments()?;
let most_digits = language
.cache_extension::<MostDigitsInId>()
.unwrap()
@ -130,7 +130,7 @@ impl CommitTemplateLanguageExtension for HexCounter {
table.commit_methods.insert(
"num_digits_in_id",
|_language, _build_context, property, call| {
template_parser::expect_no_arguments(call)?;
call.expect_no_arguments()?;
Ok(L::wrap_integer(
property.map(|commit| num_digits_in_id(commit.id())),
))
@ -139,7 +139,7 @@ impl CommitTemplateLanguageExtension for HexCounter {
table.commit_methods.insert(
"num_char_in_id",
|_language, _build_context, property, call| {
let [string_arg] = template_parser::expect_exact_arguments(call)?;
let [string_arg] = call.expect_exact_arguments()?;
let char_arg =
template_parser::expect_string_literal_with(string_arg, |string, span| {
let chars: Vec<_> = string.chars().collect();

View file

@ -53,7 +53,7 @@ impl OperationTemplateLanguageExtension for HexCounter {
table.operation_methods.insert(
"num_digits_in_id",
|_language, _build_context, property, call| {
template_parser::expect_no_arguments(call)?;
call.expect_no_arguments()?;
Ok(L::wrap_integer(
property.map(|operation| num_digits_in_id(operation.id())),
))
@ -62,7 +62,7 @@ impl OperationTemplateLanguageExtension for HexCounter {
table.operation_methods.insert(
"num_char_in_id",
|_language, _build_context, property, call| {
let [string_arg] = template_parser::expect_exact_arguments(call)?;
let [string_arg] = call.expect_exact_arguments()?;
let char_arg =
template_parser::expect_string_literal_with(string_arg, |string, span| {
let chars: Vec<_> = string.chars().collect();

View file

@ -435,7 +435,7 @@ fn builtin_commit_methods<'repo>() -> CommitTemplateBuildMethodFnMap<'repo, Comm
map.insert(
"description",
|_language, _build_ctx, self_property, function| {
template_parser::expect_no_arguments(function)?;
function.expect_no_arguments()?;
let out_property =
self_property.map(|commit| text_util::complete_newline(commit.description()));
Ok(L::wrap_string(out_property))
@ -444,7 +444,7 @@ fn builtin_commit_methods<'repo>() -> CommitTemplateBuildMethodFnMap<'repo, Comm
map.insert(
"change_id",
|_language, _build_ctx, self_property, function| {
template_parser::expect_no_arguments(function)?;
function.expect_no_arguments()?;
let out_property =
self_property.map(|commit| CommitOrChangeId::Change(commit.change_id().to_owned()));
Ok(L::wrap_commit_or_change_id(out_property))
@ -453,7 +453,7 @@ fn builtin_commit_methods<'repo>() -> CommitTemplateBuildMethodFnMap<'repo, Comm
map.insert(
"commit_id",
|_language, _build_ctx, self_property, function| {
template_parser::expect_no_arguments(function)?;
function.expect_no_arguments()?;
let out_property =
self_property.map(|commit| CommitOrChangeId::Commit(commit.id().to_owned()));
Ok(L::wrap_commit_or_change_id(out_property))
@ -462,7 +462,7 @@ fn builtin_commit_methods<'repo>() -> CommitTemplateBuildMethodFnMap<'repo, Comm
map.insert(
"parents",
|_language, _build_ctx, self_property, function| {
template_parser::expect_no_arguments(function)?;
function.expect_no_arguments()?;
let out_property =
self_property.and_then(|commit| Ok(commit.parents().try_collect()?));
Ok(L::wrap_commit_list(out_property))
@ -471,7 +471,7 @@ fn builtin_commit_methods<'repo>() -> CommitTemplateBuildMethodFnMap<'repo, Comm
map.insert(
"author",
|_language, _build_ctx, self_property, function| {
template_parser::expect_no_arguments(function)?;
function.expect_no_arguments()?;
let out_property = self_property.map(|commit| commit.author().clone());
Ok(L::wrap_signature(out_property))
},
@ -479,13 +479,13 @@ fn builtin_commit_methods<'repo>() -> CommitTemplateBuildMethodFnMap<'repo, Comm
map.insert(
"committer",
|_language, _build_ctx, self_property, function| {
template_parser::expect_no_arguments(function)?;
function.expect_no_arguments()?;
let out_property = self_property.map(|commit| commit.committer().clone());
Ok(L::wrap_signature(out_property))
},
);
map.insert("mine", |language, _build_ctx, self_property, function| {
template_parser::expect_no_arguments(function)?;
function.expect_no_arguments()?;
let user_email = language.revset_parse_context.user_email().to_owned();
let out_property = self_property.map(move |commit| commit.author().email == user_email);
Ok(L::wrap_boolean(out_property))
@ -493,7 +493,7 @@ fn builtin_commit_methods<'repo>() -> CommitTemplateBuildMethodFnMap<'repo, Comm
map.insert(
"working_copies",
|language, _build_ctx, self_property, function| {
template_parser::expect_no_arguments(function)?;
function.expect_no_arguments()?;
let repo = language.repo;
let out_property = self_property.map(|commit| extract_working_copies(repo, &commit));
Ok(L::wrap_string(out_property))
@ -502,7 +502,7 @@ fn builtin_commit_methods<'repo>() -> CommitTemplateBuildMethodFnMap<'repo, Comm
map.insert(
"current_working_copy",
|language, _build_ctx, self_property, function| {
template_parser::expect_no_arguments(function)?;
function.expect_no_arguments()?;
let repo = language.repo;
let workspace_id = language.workspace_id.clone();
let out_property = self_property.map(move |commit| {
@ -514,7 +514,7 @@ fn builtin_commit_methods<'repo>() -> CommitTemplateBuildMethodFnMap<'repo, Comm
map.insert(
"branches",
|language, _build_ctx, self_property, function| {
template_parser::expect_no_arguments(function)?;
function.expect_no_arguments()?;
let index = language.keyword_cache.branches_index(language.repo).clone();
let out_property = self_property.map(move |commit| {
index
@ -530,7 +530,7 @@ fn builtin_commit_methods<'repo>() -> CommitTemplateBuildMethodFnMap<'repo, Comm
map.insert(
"local_branches",
|language, _build_ctx, self_property, function| {
template_parser::expect_no_arguments(function)?;
function.expect_no_arguments()?;
let index = language.keyword_cache.branches_index(language.repo).clone();
let out_property = self_property.map(move |commit| {
index
@ -546,7 +546,7 @@ fn builtin_commit_methods<'repo>() -> CommitTemplateBuildMethodFnMap<'repo, Comm
map.insert(
"remote_branches",
|language, _build_ctx, self_property, function| {
template_parser::expect_no_arguments(function)?;
function.expect_no_arguments()?;
let index = language.keyword_cache.branches_index(language.repo).clone();
let out_property = self_property.map(move |commit| {
index
@ -560,7 +560,7 @@ fn builtin_commit_methods<'repo>() -> CommitTemplateBuildMethodFnMap<'repo, Comm
},
);
map.insert("tags", |language, _build_ctx, self_property, function| {
template_parser::expect_no_arguments(function)?;
function.expect_no_arguments()?;
let index = language.keyword_cache.tags_index(language.repo).clone();
let out_property = self_property.map(move |commit| index.get(commit.id()).to_vec());
Ok(L::wrap_ref_name_list(out_property))
@ -568,7 +568,7 @@ fn builtin_commit_methods<'repo>() -> CommitTemplateBuildMethodFnMap<'repo, Comm
map.insert(
"git_refs",
|language, _build_ctx, self_property, function| {
template_parser::expect_no_arguments(function)?;
function.expect_no_arguments()?;
let index = language.keyword_cache.git_refs_index(language.repo).clone();
let out_property = self_property.map(move |commit| index.get(commit.id()).to_vec());
Ok(L::wrap_ref_name_list(out_property))
@ -577,7 +577,7 @@ fn builtin_commit_methods<'repo>() -> CommitTemplateBuildMethodFnMap<'repo, Comm
map.insert(
"git_head",
|language, _build_ctx, self_property, function| {
template_parser::expect_no_arguments(function)?;
function.expect_no_arguments()?;
let repo = language.repo;
let out_property = self_property.map(|commit| extract_git_head(repo, &commit));
Ok(L::wrap_ref_name_opt(out_property))
@ -586,7 +586,7 @@ fn builtin_commit_methods<'repo>() -> CommitTemplateBuildMethodFnMap<'repo, Comm
map.insert(
"divergent",
|language, _build_ctx, self_property, function| {
template_parser::expect_no_arguments(function)?;
function.expect_no_arguments()?;
let repo = language.repo;
let out_property = self_property.map(|commit| {
// The given commit could be hidden in e.g. obslog.
@ -597,7 +597,7 @@ fn builtin_commit_methods<'repo>() -> CommitTemplateBuildMethodFnMap<'repo, Comm
},
);
map.insert("hidden", |language, _build_ctx, self_property, function| {
template_parser::expect_no_arguments(function)?;
function.expect_no_arguments()?;
let repo = language.repo;
let out_property = self_property.map(|commit| {
let maybe_entries = repo.resolve_change_id(commit.change_id());
@ -608,7 +608,7 @@ fn builtin_commit_methods<'repo>() -> CommitTemplateBuildMethodFnMap<'repo, Comm
map.insert(
"immutable",
|language, _build_ctx, self_property, function| {
template_parser::expect_no_arguments(function)?;
function.expect_no_arguments()?;
let is_immutable = language
.keyword_cache
.is_immutable_fn(language, function.name_span)?
@ -620,7 +620,7 @@ fn builtin_commit_methods<'repo>() -> CommitTemplateBuildMethodFnMap<'repo, Comm
map.insert(
"contained_in",
|language, _build_ctx, self_property, function| {
let [revset_node] = template_parser::expect_exact_arguments(function)?;
let [revset_node] = function.expect_exact_arguments()?;
let is_contained =
template_parser::expect_string_literal_with(revset_node, |revset, span| {
@ -634,19 +634,19 @@ fn builtin_commit_methods<'repo>() -> CommitTemplateBuildMethodFnMap<'repo, Comm
map.insert(
"conflict",
|_language, _build_ctx, self_property, function| {
template_parser::expect_no_arguments(function)?;
function.expect_no_arguments()?;
let out_property = self_property.and_then(|commit| Ok(commit.has_conflict()?));
Ok(L::wrap_boolean(out_property))
},
);
map.insert("empty", |language, _build_ctx, self_property, function| {
template_parser::expect_no_arguments(function)?;
function.expect_no_arguments()?;
let repo = language.repo;
let out_property = self_property.and_then(|commit| Ok(commit.is_empty(repo)?));
Ok(L::wrap_boolean(out_property))
});
map.insert("root", |language, _build_ctx, self_property, function| {
template_parser::expect_no_arguments(function)?;
function.expect_no_arguments()?;
let repo = language.repo;
let out_property = self_property.map(|commit| commit.id() == repo.store().root_commit_id());
Ok(L::wrap_boolean(out_property))
@ -909,14 +909,14 @@ fn builtin_ref_name_methods<'repo>() -> CommitTemplateBuildMethodFnMap<'repo, Rc
// code completion inside macro is quite restricted.
let mut map = CommitTemplateBuildMethodFnMap::<Rc<RefName>>::new();
map.insert("name", |_language, _build_ctx, self_property, function| {
template_parser::expect_no_arguments(function)?;
function.expect_no_arguments()?;
let out_property = self_property.map(|ref_name| ref_name.name.clone());
Ok(L::wrap_string(out_property))
});
map.insert(
"remote",
|_language, _build_ctx, self_property, function| {
template_parser::expect_no_arguments(function)?;
function.expect_no_arguments()?;
let out_property =
self_property.map(|ref_name| ref_name.remote.clone().unwrap_or_default());
Ok(L::wrap_string(out_property))
@ -925,7 +925,7 @@ fn builtin_ref_name_methods<'repo>() -> CommitTemplateBuildMethodFnMap<'repo, Rc
map.insert(
"present",
|_language, _build_ctx, self_property, function| {
template_parser::expect_no_arguments(function)?;
function.expect_no_arguments()?;
let out_property = self_property.map(|ref_name| ref_name.is_present());
Ok(L::wrap_boolean(out_property))
},
@ -933,7 +933,7 @@ fn builtin_ref_name_methods<'repo>() -> CommitTemplateBuildMethodFnMap<'repo, Rc
map.insert(
"conflict",
|_language, _build_ctx, self_property, function| {
template_parser::expect_no_arguments(function)?;
function.expect_no_arguments()?;
let out_property = self_property.map(|ref_name| ref_name.has_conflict());
Ok(L::wrap_boolean(out_property))
},
@ -941,7 +941,7 @@ fn builtin_ref_name_methods<'repo>() -> CommitTemplateBuildMethodFnMap<'repo, Rc
map.insert(
"normal_target",
|language, _build_ctx, self_property, function| {
template_parser::expect_no_arguments(function)?;
function.expect_no_arguments()?;
let repo = language.repo;
let out_property = self_property.and_then(|ref_name| {
let maybe_id = ref_name.target.as_normal();
@ -953,7 +953,7 @@ fn builtin_ref_name_methods<'repo>() -> CommitTemplateBuildMethodFnMap<'repo, Rc
map.insert(
"removed_targets",
|language, _build_ctx, self_property, function| {
template_parser::expect_no_arguments(function)?;
function.expect_no_arguments()?;
let repo = language.repo;
let out_property = self_property.and_then(|ref_name| {
let ids = ref_name.target.removed_ids();
@ -965,7 +965,7 @@ fn builtin_ref_name_methods<'repo>() -> CommitTemplateBuildMethodFnMap<'repo, Rc
map.insert(
"added_targets",
|language, _build_ctx, self_property, function| {
template_parser::expect_no_arguments(function)?;
function.expect_no_arguments()?;
let repo = language.repo;
let out_property = self_property.and_then(|ref_name| {
let ids = ref_name.target.added_ids();
@ -977,7 +977,7 @@ fn builtin_ref_name_methods<'repo>() -> CommitTemplateBuildMethodFnMap<'repo, Rc
map.insert(
"tracked",
|_language, _build_ctx, self_property, function| {
template_parser::expect_no_arguments(function)?;
function.expect_no_arguments()?;
let out_property = self_property.map(|ref_name| ref_name.is_tracked());
Ok(L::wrap_boolean(out_property))
},
@ -985,7 +985,7 @@ fn builtin_ref_name_methods<'repo>() -> CommitTemplateBuildMethodFnMap<'repo, Rc
map.insert(
"tracking_present",
|_language, _build_ctx, self_property, function| {
template_parser::expect_no_arguments(function)?;
function.expect_no_arguments()?;
let out_property = self_property.map(|ref_name| ref_name.is_tracking_present());
Ok(L::wrap_boolean(out_property))
},
@ -993,7 +993,7 @@ fn builtin_ref_name_methods<'repo>() -> CommitTemplateBuildMethodFnMap<'repo, Rc
map.insert(
"tracking_ahead_count",
|language, _build_ctx, self_property, function| {
template_parser::expect_no_arguments(function)?;
function.expect_no_arguments()?;
let repo = language.repo;
let out_property =
self_property.and_then(|ref_name| ref_name.tracking_ahead_count(repo));
@ -1003,7 +1003,7 @@ fn builtin_ref_name_methods<'repo>() -> CommitTemplateBuildMethodFnMap<'repo, Rc
map.insert(
"tracking_behind_count",
|language, _build_ctx, self_property, function| {
template_parser::expect_no_arguments(function)?;
function.expect_no_arguments()?;
let repo = language.repo;
let out_property =
self_property.and_then(|ref_name| ref_name.tracking_behind_count(repo));
@ -1135,7 +1135,7 @@ fn builtin_commit_or_change_id_methods<'repo>(
// code completion inside macro is quite restricted.
let mut map = CommitTemplateBuildMethodFnMap::<CommitOrChangeId>::new();
map.insert("short", |language, build_ctx, self_property, function| {
let ([], [len_node]) = template_parser::expect_arguments(function)?;
let ([], [len_node]) = function.expect_arguments()?;
let len_property = len_node
.map(|node| template_builder::expect_usize_expression(language, build_ctx, node))
.transpose()?;
@ -1147,7 +1147,7 @@ fn builtin_commit_or_change_id_methods<'repo>(
"shortest",
|language, build_ctx, self_property, function| {
let id_prefix_context = &language.id_prefix_context;
let ([], [len_node]) = template_parser::expect_arguments(function)?;
let ([], [len_node]) = function.expect_arguments()?;
let len_property = len_node
.map(|node| template_builder::expect_usize_expression(language, build_ctx, node))
.transpose()?;
@ -1196,23 +1196,23 @@ fn builtin_shortest_id_prefix_methods<'repo>(
map.insert(
"prefix",
|_language, _build_ctx, self_property, function| {
template_parser::expect_no_arguments(function)?;
function.expect_no_arguments()?;
let out_property = self_property.map(|id| id.prefix);
Ok(L::wrap_string(out_property))
},
);
map.insert("rest", |_language, _build_ctx, self_property, function| {
template_parser::expect_no_arguments(function)?;
function.expect_no_arguments()?;
let out_property = self_property.map(|id| id.rest);
Ok(L::wrap_string(out_property))
});
map.insert("upper", |_language, _build_ctx, self_property, function| {
template_parser::expect_no_arguments(function)?;
function.expect_no_arguments()?;
let out_property = self_property.map(|id| id.to_upper());
Ok(L::wrap_shortest_id_prefix(out_property))
});
map.insert("lower", |_language, _build_ctx, self_property, function| {
template_parser::expect_no_arguments(function)?;
function.expect_no_arguments()?;
let out_property = self_property.map(|id| id.to_lower());
Ok(L::wrap_shortest_id_prefix(out_property))
});

View file

@ -104,7 +104,7 @@ impl<'a, C: 'a> TemplateLanguage<'a> for GenericTemplateLanguage<'a, C> {
let table = &self.build_fn_table.keywords;
let build = template_parser::lookup_method(type_name, table, function)?;
// For simplicity, only 0-ary method is supported.
template_parser::expect_no_arguments(function)?;
function.expect_no_arguments()?;
build(property)
}
}

View file

@ -229,7 +229,7 @@ fn builtin_operation_methods() -> OperationTemplateBuildMethodFnMap<Operation> {
map.insert(
"current_operation",
|language, _build_ctx, self_property, function| {
template_parser::expect_no_arguments(function)?;
function.expect_no_arguments()?;
let current_op_id = language.current_op_id.clone();
let out_property = self_property.map(move |op| Some(op.id()) == current_op_id.as_ref());
Ok(L::wrap_boolean(out_property))
@ -238,18 +238,18 @@ fn builtin_operation_methods() -> OperationTemplateBuildMethodFnMap<Operation> {
map.insert(
"description",
|_language, _build_ctx, self_property, function| {
template_parser::expect_no_arguments(function)?;
function.expect_no_arguments()?;
let out_property = self_property.map(|op| op.metadata().description.clone());
Ok(L::wrap_string(out_property))
},
);
map.insert("id", |_language, _build_ctx, self_property, function| {
template_parser::expect_no_arguments(function)?;
function.expect_no_arguments()?;
let out_property = self_property.map(|op| op.id().clone());
Ok(L::wrap_operation_id(out_property))
});
map.insert("tags", |_language, _build_ctx, self_property, function| {
template_parser::expect_no_arguments(function)?;
function.expect_no_arguments()?;
let out_property = self_property.map(|op| {
// TODO: introduce map type
op.metadata()
@ -263,13 +263,13 @@ fn builtin_operation_methods() -> OperationTemplateBuildMethodFnMap<Operation> {
map.insert(
"snapshot",
|_language, _build_ctx, self_property, function| {
template_parser::expect_no_arguments(function)?;
function.expect_no_arguments()?;
let out_property = self_property.map(|op| op.metadata().is_snapshot);
Ok(L::wrap_boolean(out_property))
},
);
map.insert("time", |_language, _build_ctx, self_property, function| {
template_parser::expect_no_arguments(function)?;
function.expect_no_arguments()?;
let out_property = self_property.map(|op| TimestampRange {
start: op.metadata().start_time.clone(),
end: op.metadata().end_time.clone(),
@ -277,7 +277,7 @@ fn builtin_operation_methods() -> OperationTemplateBuildMethodFnMap<Operation> {
Ok(L::wrap_timestamp_range(out_property))
});
map.insert("user", |_language, _build_ctx, self_property, function| {
template_parser::expect_no_arguments(function)?;
function.expect_no_arguments()?;
let out_property = self_property.map(|op| {
// TODO: introduce dedicated type and provide accessors?
format!("{}@{}", op.metadata().username, op.metadata().hostname)
@ -285,7 +285,7 @@ fn builtin_operation_methods() -> OperationTemplateBuildMethodFnMap<Operation> {
Ok(L::wrap_string(out_property))
});
map.insert("root", |language, _build_ctx, self_property, function| {
template_parser::expect_no_arguments(function)?;
function.expect_no_arguments()?;
let root_op_id = language.root_op_id.clone();
let out_property = self_property.map(move |op| op.id() == &root_op_id);
Ok(L::wrap_boolean(out_property))
@ -305,7 +305,7 @@ fn builtin_operation_id_methods() -> OperationTemplateBuildMethodFnMap<Operation
// code completion inside macro is quite restricted.
let mut map = OperationTemplateBuildMethodFnMap::<OperationId>::new();
map.insert("short", |language, build_ctx, self_property, function| {
let ([], [len_node]) = template_parser::expect_arguments(function)?;
let ([], [len_node]) = function.expect_arguments()?;
let len_property = len_node
.map(|node| template_builder::expect_usize_expression(language, build_ctx, node))
.transpose()?;

View file

@ -565,14 +565,14 @@ fn builtin_string_methods<'a, L: TemplateLanguage<'a> + ?Sized>(
// code completion inside macro is quite restricted.
let mut map = TemplateBuildMethodFnMap::<L, String>::new();
map.insert("len", |_language, _build_ctx, self_property, function| {
template_parser::expect_no_arguments(function)?;
function.expect_no_arguments()?;
let out_property = self_property.and_then(|s| Ok(s.len().try_into()?));
Ok(L::wrap_integer(out_property))
});
map.insert(
"contains",
|language, build_ctx, self_property, function| {
let [needle_node] = template_parser::expect_exact_arguments(function)?;
let [needle_node] = function.expect_exact_arguments()?;
// TODO: or .try_into_string() to disable implicit type cast?
let needle_property = expect_plain_text_expression(language, build_ctx, needle_node)?;
let out_property = (self_property, needle_property)
@ -583,7 +583,7 @@ fn builtin_string_methods<'a, L: TemplateLanguage<'a> + ?Sized>(
map.insert(
"starts_with",
|language, build_ctx, self_property, function| {
let [needle_node] = template_parser::expect_exact_arguments(function)?;
let [needle_node] = function.expect_exact_arguments()?;
let needle_property = expect_plain_text_expression(language, build_ctx, needle_node)?;
let out_property = (self_property, needle_property)
.map(|(haystack, needle)| haystack.starts_with(&needle));
@ -593,7 +593,7 @@ fn builtin_string_methods<'a, L: TemplateLanguage<'a> + ?Sized>(
map.insert(
"ends_with",
|language, build_ctx, self_property, function| {
let [needle_node] = template_parser::expect_exact_arguments(function)?;
let [needle_node] = function.expect_exact_arguments()?;
let needle_property = expect_plain_text_expression(language, build_ctx, needle_node)?;
let out_property = (self_property, needle_property)
.map(|(haystack, needle)| haystack.ends_with(&needle));
@ -603,7 +603,7 @@ fn builtin_string_methods<'a, L: TemplateLanguage<'a> + ?Sized>(
map.insert(
"remove_prefix",
|language, build_ctx, self_property, function| {
let [needle_node] = template_parser::expect_exact_arguments(function)?;
let [needle_node] = function.expect_exact_arguments()?;
let needle_property = expect_plain_text_expression(language, build_ctx, needle_node)?;
let out_property = (self_property, needle_property).map(|(haystack, needle)| {
haystack
@ -617,7 +617,7 @@ fn builtin_string_methods<'a, L: TemplateLanguage<'a> + ?Sized>(
map.insert(
"remove_suffix",
|language, build_ctx, self_property, function| {
let [needle_node] = template_parser::expect_exact_arguments(function)?;
let [needle_node] = function.expect_exact_arguments()?;
let needle_property = expect_plain_text_expression(language, build_ctx, needle_node)?;
let out_property = (self_property, needle_property).map(|(haystack, needle)| {
haystack
@ -629,7 +629,7 @@ fn builtin_string_methods<'a, L: TemplateLanguage<'a> + ?Sized>(
},
);
map.insert("substr", |language, build_ctx, self_property, function| {
let [start_idx, end_idx] = template_parser::expect_exact_arguments(function)?;
let [start_idx, end_idx] = function.expect_exact_arguments()?;
let start_idx_property = expect_isize_expression(language, build_ctx, start_idx)?;
let end_idx_property = expect_isize_expression(language, build_ctx, end_idx)?;
let out_property =
@ -643,24 +643,24 @@ fn builtin_string_methods<'a, L: TemplateLanguage<'a> + ?Sized>(
map.insert(
"first_line",
|_language, _build_ctx, self_property, function| {
template_parser::expect_no_arguments(function)?;
function.expect_no_arguments()?;
let out_property =
self_property.map(|s| s.lines().next().unwrap_or_default().to_string());
Ok(L::wrap_string(out_property))
},
);
map.insert("lines", |_language, _build_ctx, self_property, function| {
template_parser::expect_no_arguments(function)?;
function.expect_no_arguments()?;
let out_property = self_property.map(|s| s.lines().map(|l| l.to_owned()).collect());
Ok(L::wrap_string_list(out_property))
});
map.insert("upper", |_language, _build_ctx, self_property, function| {
template_parser::expect_no_arguments(function)?;
function.expect_no_arguments()?;
let out_property = self_property.map(|s| s.to_uppercase());
Ok(L::wrap_string(out_property))
});
map.insert("lower", |_language, _build_ctx, self_property, function| {
template_parser::expect_no_arguments(function)?;
function.expect_no_arguments()?;
let out_property = self_property.map(|s| s.to_lowercase());
Ok(L::wrap_string(out_property))
});
@ -689,19 +689,19 @@ fn builtin_signature_methods<'a, L: TemplateLanguage<'a> + ?Sized>(
// code completion inside macro is quite restricted.
let mut map = TemplateBuildMethodFnMap::<L, Signature>::new();
map.insert("name", |_language, _build_ctx, self_property, function| {
template_parser::expect_no_arguments(function)?;
function.expect_no_arguments()?;
let out_property = self_property.map(|signature| signature.name);
Ok(L::wrap_string(out_property))
});
map.insert("email", |_language, _build_ctx, self_property, function| {
template_parser::expect_no_arguments(function)?;
function.expect_no_arguments()?;
let out_property = self_property.map(|signature| signature.email);
Ok(L::wrap_string(out_property))
});
map.insert(
"username",
|_language, _build_ctx, self_property, function| {
template_parser::expect_no_arguments(function)?;
function.expect_no_arguments()?;
let out_property = self_property.map(|signature| {
let (username, _) = text_util::split_email(&signature.email);
username.to_owned()
@ -712,7 +712,7 @@ fn builtin_signature_methods<'a, L: TemplateLanguage<'a> + ?Sized>(
map.insert(
"timestamp",
|_language, _build_ctx, self_property, function| {
template_parser::expect_no_arguments(function)?;
function.expect_no_arguments()?;
let out_property = self_property.map(|signature| signature.timestamp);
Ok(L::wrap_timestamp(out_property))
},
@ -726,18 +726,18 @@ fn builtin_size_hint_methods<'a, L: TemplateLanguage<'a> + ?Sized>(
// code completion inside macro is quite restricted.
let mut map = TemplateBuildMethodFnMap::<L, SizeHint>::new();
map.insert("lower", |_language, _build_ctx, self_property, function| {
template_parser::expect_no_arguments(function)?;
function.expect_no_arguments()?;
let out_property = self_property.and_then(|(lower, _)| Ok(i64::try_from(lower)?));
Ok(L::wrap_integer(out_property))
});
map.insert("upper", |_language, _build_ctx, self_property, function| {
template_parser::expect_no_arguments(function)?;
function.expect_no_arguments()?;
let out_property =
self_property.and_then(|(_, upper)| Ok(upper.map(i64::try_from).transpose()?));
Ok(L::wrap_integer_opt(out_property))
});
map.insert("exact", |_language, _build_ctx, self_property, function| {
template_parser::expect_no_arguments(function)?;
function.expect_no_arguments()?;
let out_property = self_property.and_then(|(lower, upper)| {
let exact = (Some(lower) == upper).then_some(lower);
Ok(exact.map(i64::try_from).transpose()?)
@ -745,7 +745,7 @@ fn builtin_size_hint_methods<'a, L: TemplateLanguage<'a> + ?Sized>(
Ok(L::wrap_integer_opt(out_property))
});
map.insert("zero", |_language, _build_ctx, self_property, function| {
template_parser::expect_no_arguments(function)?;
function.expect_no_arguments()?;
let out_property = self_property.map(|(_, upper)| upper == Some(0));
Ok(L::wrap_boolean(out_property))
});
@ -758,7 +758,7 @@ fn builtin_timestamp_methods<'a, L: TemplateLanguage<'a> + ?Sized>(
// code completion inside macro is quite restricted.
let mut map = TemplateBuildMethodFnMap::<L, Timestamp>::new();
map.insert("ago", |_language, _build_ctx, self_property, function| {
template_parser::expect_no_arguments(function)?;
function.expect_no_arguments()?;
let now = Timestamp::now();
let format = timeago::Formatter::new();
let out_property = self_property
@ -769,7 +769,7 @@ fn builtin_timestamp_methods<'a, L: TemplateLanguage<'a> + ?Sized>(
"format",
|_language, _build_ctx, self_property, function| {
// No dynamic string is allowed as the templater has no runtime error type.
let [format_node] = template_parser::expect_exact_arguments(function)?;
let [format_node] = function.expect_exact_arguments()?;
let format =
template_parser::expect_string_literal_with(format_node, |format, span| {
time_util::FormattingItems::parse(format)
@ -785,7 +785,7 @@ fn builtin_timestamp_methods<'a, L: TemplateLanguage<'a> + ?Sized>(
},
);
map.insert("utc", |_language, _build_ctx, self_property, function| {
template_parser::expect_no_arguments(function)?;
function.expect_no_arguments()?;
let out_property = self_property.map(|mut timestamp| {
timestamp.tz_offset = 0;
timestamp
@ -793,7 +793,7 @@ fn builtin_timestamp_methods<'a, L: TemplateLanguage<'a> + ?Sized>(
Ok(L::wrap_timestamp(out_property))
});
map.insert("local", |_language, _build_ctx, self_property, function| {
template_parser::expect_no_arguments(function)?;
function.expect_no_arguments()?;
let tz_offset = std::env::var("JJ_TZ_OFFSET_MINS")
.ok()
.and_then(|tz_string| tz_string.parse::<i32>().ok())
@ -813,19 +813,19 @@ fn builtin_timestamp_range_methods<'a, L: TemplateLanguage<'a> + ?Sized>(
// code completion inside macro is quite restricted.
let mut map = TemplateBuildMethodFnMap::<L, TimestampRange>::new();
map.insert("start", |_language, _build_ctx, self_property, function| {
template_parser::expect_no_arguments(function)?;
function.expect_no_arguments()?;
let out_property = self_property.map(|time_range| time_range.start);
Ok(L::wrap_timestamp(out_property))
});
map.insert("end", |_language, _build_ctx, self_property, function| {
template_parser::expect_no_arguments(function)?;
function.expect_no_arguments()?;
let out_property = self_property.map(|time_range| time_range.end);
Ok(L::wrap_timestamp(out_property))
});
map.insert(
"duration",
|_language, _build_ctx, self_property, function| {
template_parser::expect_no_arguments(function)?;
function.expect_no_arguments()?;
let out_property = self_property.and_then(|time_range| Ok(time_range.duration()?));
Ok(L::wrap_string(out_property))
},
@ -841,7 +841,7 @@ fn build_list_template_method<'a, L: TemplateLanguage<'a> + ?Sized>(
) -> TemplateParseResult<L::Property> {
let property = match function.name {
"join" => {
let [separator_node] = template_parser::expect_exact_arguments(function)?;
let [separator_node] = function.expect_exact_arguments()?;
let separator = expect_template_expression(language, build_ctx, separator_node)?;
L::wrap_template(self_template.join(separator))
}
@ -866,12 +866,12 @@ where
{
let property = match function.name {
"len" => {
template_parser::expect_no_arguments(function)?;
function.expect_no_arguments()?;
let out_property = self_property.and_then(|items| Ok(items.len().try_into()?));
L::wrap_integer(out_property)
}
"join" => {
let [separator_node] = template_parser::expect_exact_arguments(function)?;
let [separator_node] = function.expect_exact_arguments()?;
let separator = expect_template_expression(language, build_ctx, separator_node)?;
let template =
ListPropertyTemplate::new(self_property, separator, |formatter, item| {
@ -898,7 +898,7 @@ where
{
let property = match function.name {
"len" => {
template_parser::expect_no_arguments(function)?;
function.expect_no_arguments()?;
let out_property = self_property.and_then(|items| Ok(items.len().try_into()?));
L::wrap_integer(out_property)
}
@ -928,7 +928,7 @@ where
{
// Build an item template with placeholder property, then evaluate it
// for each item.
let [lambda_node] = template_parser::expect_exact_arguments(function)?;
let [lambda_node] = function.expect_exact_arguments()?;
let item_placeholder = PropertyPlaceholder::new();
let item_template = template_parser::expect_lambda_with(lambda_node, |lambda, _span| {
let item_fn = || wrap_item(item_placeholder.clone());
@ -962,7 +962,7 @@ fn builtin_functions<'a, L: TemplateLanguage<'a> + ?Sized>() -> TemplateBuildFun
// code completion inside macro is quite restricted.
let mut map = TemplateBuildFunctionFnMap::<L>::new();
map.insert("fill", |language, build_ctx, function| {
let [width_node, content_node] = template_parser::expect_exact_arguments(function)?;
let [width_node, content_node] = function.expect_exact_arguments()?;
let width = expect_usize_expression(language, build_ctx, width_node)?;
let content = expect_template_expression(language, build_ctx, content_node)?;
let template =
@ -973,7 +973,7 @@ fn builtin_functions<'a, L: TemplateLanguage<'a> + ?Sized>() -> TemplateBuildFun
Ok(L::wrap_template(Box::new(template)))
});
map.insert("indent", |language, build_ctx, function| {
let [prefix_node, content_node] = template_parser::expect_exact_arguments(function)?;
let [prefix_node, content_node] = function.expect_exact_arguments()?;
let prefix = expect_template_expression(language, build_ctx, prefix_node)?;
let content = expect_template_expression(language, build_ctx, content_node)?;
let template = ReformatTemplate::new(content, move |formatter, recorded| {
@ -985,7 +985,7 @@ fn builtin_functions<'a, L: TemplateLanguage<'a> + ?Sized>() -> TemplateBuildFun
Ok(L::wrap_template(Box::new(template)))
});
map.insert("label", |language, build_ctx, function| {
let [label_node, content_node] = template_parser::expect_exact_arguments(function)?;
let [label_node, content_node] = function.expect_exact_arguments()?;
let label_property = expect_plain_text_expression(language, build_ctx, label_node)?;
let content = expect_template_expression(language, build_ctx, content_node)?;
let labels =
@ -995,8 +995,7 @@ fn builtin_functions<'a, L: TemplateLanguage<'a> + ?Sized>() -> TemplateBuildFun
))))
});
map.insert("if", |language, build_ctx, function| {
let ([condition_node, true_node], [false_node]) =
template_parser::expect_arguments(function)?;
let ([condition_node, true_node], [false_node]) = function.expect_arguments()?;
let condition = expect_boolean_expression(language, build_ctx, condition_node)?;
let true_template = expect_template_expression(language, build_ctx, true_node)?;
let false_template = false_node
@ -1022,7 +1021,7 @@ fn builtin_functions<'a, L: TemplateLanguage<'a> + ?Sized>() -> TemplateBuildFun
Ok(L::wrap_template(Box::new(ConcatTemplate(contents))))
});
map.insert("separate", |language, build_ctx, function| {
let ([separator_node], content_nodes) = template_parser::expect_some_arguments(function)?;
let ([separator_node], content_nodes) = function.expect_some_arguments()?;
let separator = expect_template_expression(language, build_ctx, separator_node)?;
let contents = content_nodes
.iter()
@ -1033,8 +1032,7 @@ fn builtin_functions<'a, L: TemplateLanguage<'a> + ?Sized>() -> TemplateBuildFun
))))
});
map.insert("surround", |language, build_ctx, function| {
let [prefix_node, suffix_node, content_node] =
template_parser::expect_exact_arguments(function)?;
let [prefix_node, suffix_node, content_node] = function.expect_exact_arguments()?;
let prefix = expect_template_expression(language, build_ctx, prefix_node)?;
let suffix = expect_template_expression(language, build_ctx, suffix_node)?;
let content = expect_template_expression(language, build_ctx, content_node)?;

View file

@ -681,60 +681,59 @@ pub fn parse<'i>(
expand_aliases(node, aliases_map)
}
pub fn expect_no_arguments(function: &FunctionCallNode) -> TemplateParseResult<()> {
if function.args.is_empty() {
impl<'i> FunctionCallNode<'i> {
pub fn expect_no_arguments(&self) -> TemplateParseResult<()> {
if self.args.is_empty() {
Ok(())
} else {
Err(TemplateParseError::invalid_arguments(
function,
self,
"Expected 0 arguments",
))
}
}
}
/// Extracts exactly N required arguments.
pub fn expect_exact_arguments<'a, 'i, const N: usize>(
function: &'a FunctionCallNode<'i>,
) -> TemplateParseResult<&'a [ExpressionNode<'i>; N]> {
function.args.as_slice().try_into().map_err(|_| {
TemplateParseError::invalid_arguments(function, format!("Expected {N} arguments"))
/// Extracts exactly N required arguments.
pub fn expect_exact_arguments<const N: usize>(
&self,
) -> TemplateParseResult<&[ExpressionNode<'i>; N]> {
self.args.as_slice().try_into().map_err(|_| {
TemplateParseError::invalid_arguments(self, format!("Expected {N} arguments"))
})
}
}
/// Extracts N required arguments and remainders.
pub fn expect_some_arguments<'a, 'i, const N: usize>(
function: &'a FunctionCallNode<'i>,
) -> TemplateParseResult<(&'a [ExpressionNode<'i>; N], &'a [ExpressionNode<'i>])> {
if function.args.len() >= N {
let (required, rest) = function.args.split_at(N);
/// Extracts N required arguments and remainders.
pub fn expect_some_arguments<const N: usize>(
&self,
) -> TemplateParseResult<(&[ExpressionNode<'i>; N], &[ExpressionNode<'i>])> {
if self.args.len() >= N {
let (required, rest) = self.args.split_at(N);
Ok((required.try_into().unwrap(), rest))
} else {
Err(TemplateParseError::invalid_arguments(
function,
self,
format!("Expected at least {N} arguments"),
))
}
}
}
/// Extracts N required arguments and M optional arguments.
pub fn expect_arguments<'a, 'i, const N: usize, const M: usize>(
function: &'a FunctionCallNode<'i>,
) -> TemplateParseResult<(
&'a [ExpressionNode<'i>; N],
[Option<&'a ExpressionNode<'i>>; M],
)> {
/// Extracts N required arguments and M optional arguments.
pub fn expect_arguments<const N: usize, const M: usize>(
&self,
) -> TemplateParseResult<(&[ExpressionNode<'i>; N], [Option<&ExpressionNode<'i>>; M])> {
let count_range = N..=(N + M);
if count_range.contains(&function.args.len()) {
let (required, rest) = function.args.split_at(N);
if count_range.contains(&self.args.len()) {
let (required, rest) = self.args.split_at(N);
let mut optional = rest.iter().map(Some).collect_vec();
optional.resize(M, None);
Ok((required.try_into().unwrap(), optional.try_into().unwrap()))
} else {
Err(TemplateParseError::invalid_arguments(
function,
self,
format!("Expected {min} to {max} arguments", min = N, max = N + M),
))
}
}
}
/// Applies the given function if the `node` is a string literal.