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

templater: leverage TemplateProperty::map/and_then() helpers

This commit is contained in:
Yuya Nishihara 2024-03-21 11:38:23 +09:00
parent a3d44485f4
commit fad712811c
7 changed files with 144 additions and 215 deletions

View file

@ -18,7 +18,7 @@ use jj_cli::commit_templater::{
};
use jj_cli::template_builder::TemplateLanguage;
use jj_cli::template_parser::{self, TemplateParseError};
use jj_cli::templater::{TemplateFunction, TemplatePropertyError};
use jj_cli::templater::TemplatePropertyExt as _;
use jj_lib::backend::CommitId;
use jj_lib::commit::Commit;
use jj_lib::extensions_map::ExtensionsMap;
@ -39,14 +39,14 @@ fn num_digits_in_id(id: &CommitId) -> i64 {
count
}
fn num_char_in_id(commit: Commit, ch_match: char) -> Result<i64, TemplatePropertyError> {
fn num_char_in_id(commit: Commit, ch_match: char) -> i64 {
let mut count = 0;
for ch in commit.id().hex().chars() {
if ch == ch_match {
count += 1;
}
}
Ok(count)
count
}
struct MostDigitsInId {
@ -85,19 +85,18 @@ impl CommitTemplateLanguageExtension for HexCounter {
.cache_extension::<MostDigitsInId>()
.unwrap()
.count(language.repo());
Ok(L::wrap_boolean(TemplateFunction::new(
property,
move |commit| Ok(num_digits_in_id(commit.id()) == most_digits),
)))
Ok(L::wrap_boolean(property.map(move |commit| {
num_digits_in_id(commit.id()) == most_digits
})))
},
);
table.commit_methods.insert(
"num_digits_in_id",
|_language, _build_context, property, call| {
template_parser::expect_no_arguments(call)?;
Ok(L::wrap_integer(TemplateFunction::new(property, |commit| {
Ok(num_digits_in_id(commit.id()))
})))
Ok(L::wrap_integer(
property.map(|commit| num_digits_in_id(commit.id())),
))
},
);
table.commit_methods.insert(
@ -116,10 +115,9 @@ impl CommitTemplateLanguageExtension for HexCounter {
}
})?;
Ok(L::wrap_integer(TemplateFunction::new(
property,
move |commit| num_char_in_id(commit, char_arg),
)))
Ok(L::wrap_integer(
property.map(move |commit| num_char_in_id(commit, char_arg)),
))
},
);

View file

@ -18,7 +18,7 @@ use jj_cli::operation_templater::{
};
use jj_cli::template_builder::TemplateLanguage;
use jj_cli::template_parser::{self, TemplateParseError};
use jj_cli::templater::{TemplateFunction, TemplatePropertyError};
use jj_cli::templater::TemplatePropertyExt as _;
use jj_lib::extensions_map::ExtensionsMap;
use jj_lib::object_id::ObjectId;
use jj_lib::op_store::OperationId;
@ -36,14 +36,14 @@ fn num_digits_in_id(id: &OperationId) -> i64 {
count
}
fn num_char_in_id(operation: Operation, ch_match: char) -> Result<i64, TemplatePropertyError> {
fn num_char_in_id(operation: Operation, ch_match: char) -> i64 {
let mut count = 0;
for ch in operation.id().hex().chars() {
if ch == ch_match {
count += 1;
}
}
Ok(count)
count
}
impl OperationTemplateLanguageExtension for HexCounter {
@ -54,10 +54,9 @@ impl OperationTemplateLanguageExtension for HexCounter {
"num_digits_in_id",
|_language, _build_context, property, call| {
template_parser::expect_no_arguments(call)?;
Ok(L::wrap_integer(TemplateFunction::new(
property,
|operation| Ok(num_digits_in_id(operation.id())),
)))
Ok(L::wrap_integer(
property.map(|operation| num_digits_in_id(operation.id())),
))
},
);
table.operation_methods.insert(
@ -76,10 +75,9 @@ impl OperationTemplateLanguageExtension for HexCounter {
}
})?;
Ok(L::wrap_integer(TemplateFunction::new(
property,
move |operation| num_char_in_id(operation, char_arg),
)))
Ok(L::wrap_integer(
property.map(move |operation| num_char_in_id(operation, char_arg)),
))
},
);

View file

@ -26,7 +26,7 @@ use crate::command_error::{user_error, CommandError};
use crate::config::{AnnotatedValue, ConfigSource};
use crate::generic_templater::GenericTemplateLanguage;
use crate::template_builder::TemplateLanguage as _;
use crate::templater::{Template as _, TemplateFunction};
use crate::templater::{Template as _, TemplatePropertyExt as _};
use crate::ui::Ui;
#[derive(clap::Args, Clone, Debug)]
@ -191,20 +191,16 @@ fn config_template_language() -> GenericTemplateLanguage<'static, AnnotatedValue
let mut language = L::new();
// "name" instead of "path" to avoid confusion with the source file path
language.add_keyword("name", |self_property| {
let out_property =
TemplateFunction::new(self_property, |annotated| Ok(annotated.path.join(".")));
let out_property = self_property.map(|annotated| annotated.path.join("."));
Ok(L::wrap_string(out_property))
});
language.add_keyword("value", |self_property| {
// TODO: would be nice if we can provide raw dynamically-typed value
let out_property = TemplateFunction::new(self_property, |annotated| {
Ok(serialize_config_value(&annotated.value))
});
let out_property = self_property.map(|annotated| serialize_config_value(&annotated.value));
Ok(L::wrap_string(out_property))
});
language.add_keyword("overridden", |self_property| {
let out_property =
TemplateFunction::new(self_property, |annotated| Ok(annotated.is_overridden));
let out_property = self_property.map(|annotated| annotated.is_overridden);
Ok(L::wrap_boolean(out_property))
});
language

View file

@ -38,7 +38,8 @@ use crate::template_builder::{
};
use crate::template_parser::{self, FunctionCallNode, TemplateParseError, TemplateParseResult};
use crate::templater::{
self, IntoTemplate, PlainTextFormattedProperty, Template, TemplateFunction, TemplateProperty,
self, IntoTemplate, PlainTextFormattedProperty, Template, TemplateProperty,
TemplatePropertyExt as _,
};
use crate::{revset_util, text_util};
@ -236,15 +237,11 @@ impl<'repo> IntoTemplateProperty<'repo> for CommitTemplatePropertyKind<'repo> {
CommitTemplatePropertyKind::Core(property) => property.try_into_boolean(),
CommitTemplatePropertyKind::Commit(_) => None,
CommitTemplatePropertyKind::CommitList(property) => {
Some(Box::new(TemplateFunction::new(property, |l| {
Ok(!l.is_empty())
})))
Some(Box::new(property.map(|l| !l.is_empty())))
}
CommitTemplatePropertyKind::RefName(_) => None,
CommitTemplatePropertyKind::RefNameList(property) => {
Some(Box::new(TemplateFunction::new(property, |l| {
Ok(!l.is_empty())
})))
Some(Box::new(property.map(|l| !l.is_empty())))
}
CommitTemplatePropertyKind::CommitOrChangeId(_) => None,
CommitTemplatePropertyKind::ShortestIdPrefix(_) => None,
@ -377,9 +374,8 @@ fn builtin_commit_methods<'repo>() -> CommitTemplateBuildMethodFnMap<'repo, Comm
"description",
|_language, _build_ctx, self_property, function| {
template_parser::expect_no_arguments(function)?;
let out_property = TemplateFunction::new(self_property, |commit| {
Ok(text_util::complete_newline(commit.description()))
});
let out_property =
self_property.map(|commit| text_util::complete_newline(commit.description()));
Ok(L::wrap_string(out_property))
},
);
@ -387,9 +383,8 @@ fn builtin_commit_methods<'repo>() -> CommitTemplateBuildMethodFnMap<'repo, Comm
"change_id",
|_language, _build_ctx, self_property, function| {
template_parser::expect_no_arguments(function)?;
let out_property = TemplateFunction::new(self_property, |commit| {
Ok(CommitOrChangeId::Change(commit.change_id().to_owned()))
});
let out_property =
self_property.map(|commit| CommitOrChangeId::Change(commit.change_id().to_owned()));
Ok(L::wrap_commit_or_change_id(out_property))
},
);
@ -397,9 +392,8 @@ fn builtin_commit_methods<'repo>() -> CommitTemplateBuildMethodFnMap<'repo, Comm
"commit_id",
|_language, _build_ctx, self_property, function| {
template_parser::expect_no_arguments(function)?;
let out_property = TemplateFunction::new(self_property, |commit| {
Ok(CommitOrChangeId::Commit(commit.id().to_owned()))
});
let out_property =
self_property.map(|commit| CommitOrChangeId::Commit(commit.id().to_owned()));
Ok(L::wrap_commit_or_change_id(out_property))
},
);
@ -407,7 +401,7 @@ fn builtin_commit_methods<'repo>() -> CommitTemplateBuildMethodFnMap<'repo, Comm
"parents",
|_language, _build_ctx, self_property, function| {
template_parser::expect_no_arguments(function)?;
let out_property = TemplateFunction::new(self_property, |commit| Ok(commit.parents()));
let out_property = self_property.map(|commit| commit.parents());
Ok(L::wrap_commit_list(out_property))
},
);
@ -415,8 +409,7 @@ fn builtin_commit_methods<'repo>() -> CommitTemplateBuildMethodFnMap<'repo, Comm
"author",
|_language, _build_ctx, self_property, function| {
template_parser::expect_no_arguments(function)?;
let out_property =
TemplateFunction::new(self_property, |commit| Ok(commit.author().clone()));
let out_property = self_property.map(|commit| commit.author().clone());
Ok(L::wrap_signature(out_property))
},
);
@ -424,8 +417,7 @@ fn builtin_commit_methods<'repo>() -> CommitTemplateBuildMethodFnMap<'repo, Comm
"committer",
|_language, _build_ctx, self_property, function| {
template_parser::expect_no_arguments(function)?;
let out_property =
TemplateFunction::new(self_property, |commit| Ok(commit.committer().clone()));
let out_property = self_property.map(|commit| commit.committer().clone());
Ok(L::wrap_signature(out_property))
},
);
@ -434,9 +426,7 @@ fn builtin_commit_methods<'repo>() -> CommitTemplateBuildMethodFnMap<'repo, Comm
|language, _build_ctx, self_property, function| {
template_parser::expect_no_arguments(function)?;
let repo = language.repo;
let out_property = TemplateFunction::new(self_property, |commit| {
Ok(extract_working_copies(repo, &commit))
});
let out_property = self_property.map(|commit| extract_working_copies(repo, &commit));
Ok(L::wrap_string(out_property))
},
);
@ -446,8 +436,8 @@ fn builtin_commit_methods<'repo>() -> CommitTemplateBuildMethodFnMap<'repo, Comm
template_parser::expect_no_arguments(function)?;
let repo = language.repo;
let workspace_id = language.workspace_id.clone();
let out_property = TemplateFunction::new(self_property, move |commit| {
Ok(Some(commit.id()) == repo.view().get_wc_commit_id(&workspace_id))
let out_property = self_property.map(move |commit| {
Some(commit.id()) == repo.view().get_wc_commit_id(&workspace_id)
});
Ok(L::wrap_boolean(out_property))
},
@ -457,13 +447,13 @@ fn builtin_commit_methods<'repo>() -> CommitTemplateBuildMethodFnMap<'repo, Comm
|language, _build_ctx, self_property, function| {
template_parser::expect_no_arguments(function)?;
let index = language.keyword_cache.branches_index(language.repo).clone();
let out_property = TemplateFunction::new(self_property, move |commit| {
Ok(index
let out_property = self_property.map(move |commit| {
index
.get(commit.id())
.iter()
.filter(|ref_name| ref_name.is_local() || !ref_name.synced)
.cloned()
.collect())
.collect()
});
Ok(L::wrap_ref_name_list(out_property))
},
@ -473,13 +463,13 @@ fn builtin_commit_methods<'repo>() -> CommitTemplateBuildMethodFnMap<'repo, Comm
|language, _build_ctx, self_property, function| {
template_parser::expect_no_arguments(function)?;
let index = language.keyword_cache.branches_index(language.repo).clone();
let out_property = TemplateFunction::new(self_property, move |commit| {
Ok(index
let out_property = self_property.map(move |commit| {
index
.get(commit.id())
.iter()
.filter(|ref_name| ref_name.is_local())
.cloned()
.collect())
.collect()
});
Ok(L::wrap_ref_name_list(out_property))
},
@ -489,13 +479,13 @@ fn builtin_commit_methods<'repo>() -> CommitTemplateBuildMethodFnMap<'repo, Comm
|language, _build_ctx, self_property, function| {
template_parser::expect_no_arguments(function)?;
let index = language.keyword_cache.branches_index(language.repo).clone();
let out_property = TemplateFunction::new(self_property, move |commit| {
Ok(index
let out_property = self_property.map(move |commit| {
index
.get(commit.id())
.iter()
.filter(|ref_name| ref_name.is_remote())
.cloned()
.collect())
.collect()
});
Ok(L::wrap_ref_name_list(out_property))
},
@ -503,9 +493,7 @@ fn builtin_commit_methods<'repo>() -> CommitTemplateBuildMethodFnMap<'repo, Comm
map.insert("tags", |language, _build_ctx, self_property, function| {
template_parser::expect_no_arguments(function)?;
let index = language.keyword_cache.tags_index(language.repo).clone();
let out_property = TemplateFunction::new(self_property, move |commit| {
Ok(index.get(commit.id()).to_vec())
});
let out_property = self_property.map(move |commit| index.get(commit.id()).to_vec());
Ok(L::wrap_ref_name_list(out_property))
});
map.insert(
@ -513,9 +501,7 @@ fn builtin_commit_methods<'repo>() -> CommitTemplateBuildMethodFnMap<'repo, Comm
|language, _build_ctx, self_property, function| {
template_parser::expect_no_arguments(function)?;
let index = language.keyword_cache.git_refs_index(language.repo).clone();
let out_property = TemplateFunction::new(self_property, move |commit| {
Ok(index.get(commit.id()).to_vec())
});
let out_property = self_property.map(move |commit| index.get(commit.id()).to_vec());
Ok(L::wrap_ref_name_list(out_property))
},
);
@ -524,8 +510,7 @@ fn builtin_commit_methods<'repo>() -> CommitTemplateBuildMethodFnMap<'repo, Comm
|language, _build_ctx, self_property, function| {
template_parser::expect_no_arguments(function)?;
let repo = language.repo;
let out_property =
TemplateFunction::new(self_property, |commit| Ok(extract_git_head(repo, &commit)));
let out_property = self_property.map(|commit| extract_git_head(repo, &commit));
Ok(L::wrap_ref_name_list(out_property))
},
);
@ -534,10 +519,10 @@ fn builtin_commit_methods<'repo>() -> CommitTemplateBuildMethodFnMap<'repo, Comm
|language, _build_ctx, self_property, function| {
template_parser::expect_no_arguments(function)?;
let repo = language.repo;
let out_property = TemplateFunction::new(self_property, |commit| {
let out_property = self_property.map(|commit| {
// The given commit could be hidden in e.g. obslog.
let maybe_entries = repo.resolve_change_id(commit.change_id());
Ok(maybe_entries.map_or(0, |entries| entries.len()) > 1)
maybe_entries.map_or(0, |entries| entries.len()) > 1
});
Ok(L::wrap_boolean(out_property))
},
@ -545,9 +530,9 @@ fn builtin_commit_methods<'repo>() -> CommitTemplateBuildMethodFnMap<'repo, Comm
map.insert("hidden", |language, _build_ctx, self_property, function| {
template_parser::expect_no_arguments(function)?;
let repo = language.repo;
let out_property = TemplateFunction::new(self_property, |commit| {
let out_property = self_property.map(|commit| {
let maybe_entries = repo.resolve_change_id(commit.change_id());
Ok(maybe_entries.map_or(true, |entries| !entries.contains(commit.id())))
maybe_entries.map_or(true, |entries| !entries.contains(commit.id()))
});
Ok(L::wrap_boolean(out_property))
});
@ -557,8 +542,7 @@ fn builtin_commit_methods<'repo>() -> CommitTemplateBuildMethodFnMap<'repo, Comm
template_parser::expect_no_arguments(function)?;
let revset = evaluate_immutable_revset(language, function.name_span)?;
let is_immutable = revset.containing_fn();
let out_property =
TemplateFunction::new(self_property, move |commit| Ok(is_immutable(commit.id())));
let out_property = self_property.map(move |commit| is_immutable(commit.id()));
Ok(L::wrap_boolean(out_property))
},
);
@ -566,15 +550,14 @@ fn builtin_commit_methods<'repo>() -> CommitTemplateBuildMethodFnMap<'repo, Comm
"conflict",
|_language, _build_ctx, self_property, function| {
template_parser::expect_no_arguments(function)?;
let out_property =
TemplateFunction::new(self_property, |commit| Ok(commit.has_conflict()?));
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)?;
let repo = language.repo;
let out_property = TemplateFunction::new(self_property, |commit| {
let out_property = self_property.and_then(|commit| {
if let [parent] = &commit.parents()[..] {
return Ok(parent.tree_id() == commit.tree_id());
}
@ -586,9 +569,7 @@ fn builtin_commit_methods<'repo>() -> CommitTemplateBuildMethodFnMap<'repo, Comm
map.insert("root", |language, _build_ctx, self_property, function| {
template_parser::expect_no_arguments(function)?;
let repo = language.repo;
let out_property = TemplateFunction::new(self_property, |commit| {
Ok(commit.id() == repo.store().root_commit_id())
});
let out_property = self_property.map(|commit| commit.id() == repo.store().root_commit_id());
Ok(L::wrap_boolean(out_property))
});
map
@ -682,16 +663,14 @@ fn builtin_ref_name_methods<'repo>() -> CommitTemplateBuildMethodFnMap<'repo, Re
let mut map = CommitTemplateBuildMethodFnMap::<RefName>::new();
map.insert("name", |_language, _build_ctx, self_property, function| {
template_parser::expect_no_arguments(function)?;
let out_property = TemplateFunction::new(self_property, |ref_name| Ok(ref_name.name));
let out_property = self_property.map(|ref_name| ref_name.name);
Ok(L::wrap_string(out_property))
});
map.insert(
"remote",
|_language, _build_ctx, self_property, function| {
template_parser::expect_no_arguments(function)?;
let out_property = TemplateFunction::new(self_property, |ref_name| {
Ok(ref_name.remote.unwrap_or_default())
});
let out_property = self_property.map(|ref_name| ref_name.remote.unwrap_or_default());
Ok(L::wrap_string(out_property))
},
);
@ -842,9 +821,8 @@ fn builtin_commit_or_change_id_methods<'repo>(
let len_property = len_node
.map(|node| template_builder::expect_usize_expression(language, build_ctx, node))
.transpose()?;
let out_property = TemplateFunction::new((self_property, len_property), |(id, len)| {
Ok(id.short(len.unwrap_or(12)))
});
let out_property =
(self_property, len_property).map(|(id, len)| id.short(len.unwrap_or(12)));
Ok(L::wrap_string(out_property))
});
map.insert(
@ -855,9 +833,8 @@ fn builtin_commit_or_change_id_methods<'repo>(
let len_property = len_node
.map(|node| template_builder::expect_usize_expression(language, build_ctx, node))
.transpose()?;
let out_property = TemplateFunction::new((self_property, len_property), |(id, len)| {
Ok(id.shortest(language.repo, id_prefix_context, len.unwrap_or(0)))
});
let out_property = (self_property, len_property)
.map(|(id, len)| id.shortest(language.repo, id_prefix_context, len.unwrap_or(0)));
Ok(L::wrap_shortest_id_prefix(out_property))
},
);
@ -902,23 +879,23 @@ fn builtin_shortest_id_prefix_methods<'repo>(
"prefix",
|_language, _build_ctx, self_property, function| {
template_parser::expect_no_arguments(function)?;
let out_property = TemplateFunction::new(self_property, |id| Ok(id.prefix));
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)?;
let out_property = TemplateFunction::new(self_property, |id| Ok(id.rest));
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)?;
let out_property = TemplateFunction::new(self_property, |id| Ok(id.to_upper()));
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)?;
let out_property = TemplateFunction::new(self_property, |id| Ok(id.to_lower()));
let out_property = self_property.map(|id| id.to_lower());
Ok(L::wrap_shortest_id_prefix(out_property))
});
map

View file

@ -59,7 +59,7 @@ impl<'a, C> GenericTemplateLanguage<'a, C> {
///
/// ```ignore
/// language.add_keyword("name", |self_property| {
/// let out_property = TemplateFunction::new(self_property, |v| Ok(v.to_string()));
/// let out_property = self_property.map(|v| v.to_string());
/// Ok(GenericTemplateLanguage::wrap_string(out_property))
/// });
/// ```

View file

@ -29,7 +29,7 @@ use crate::template_builder::{
};
use crate::template_parser::{self, FunctionCallNode, TemplateParseResult};
use crate::templater::{
IntoTemplate, PlainTextFormattedProperty, Template, TemplateFunction, TemplateProperty,
IntoTemplate, PlainTextFormattedProperty, Template, TemplateProperty, TemplatePropertyExt as _,
TimestampRange,
};
@ -221,9 +221,7 @@ fn builtin_operation_methods() -> OperationTemplateBuildMethodFnMap<Operation> {
|language, _build_ctx, self_property, function| {
template_parser::expect_no_arguments(function)?;
let current_op_id = language.current_op_id.clone();
let out_property = TemplateFunction::new(self_property, move |op| {
Ok(Some(op.id()) == current_op_id.as_ref())
});
let out_property = self_property.map(move |op| Some(op.id()) == current_op_id.as_ref());
Ok(L::wrap_boolean(out_property))
},
);
@ -231,56 +229,47 @@ fn builtin_operation_methods() -> OperationTemplateBuildMethodFnMap<Operation> {
"description",
|_language, _build_ctx, self_property, function| {
template_parser::expect_no_arguments(function)?;
let out_property =
TemplateFunction::new(self_property, |op| Ok(op.metadata().description.clone()));
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)?;
let out_property = TemplateFunction::new(self_property, |op| Ok(op.id().clone()));
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)?;
let out_property = TemplateFunction::new(self_property, |op| {
let out_property = self_property.map(|op| {
// TODO: introduce map type
Ok(op
.metadata()
op.metadata()
.tags
.iter()
.map(|(key, value)| format!("{key}: {value}"))
.join("\n"))
.join("\n")
});
Ok(L::wrap_string(out_property))
});
map.insert("time", |_language, _build_ctx, self_property, function| {
template_parser::expect_no_arguments(function)?;
let out_property = TemplateFunction::new(self_property, |op| {
Ok(TimestampRange {
start: op.metadata().start_time.clone(),
end: op.metadata().end_time.clone(),
})
let out_property = self_property.map(|op| TimestampRange {
start: op.metadata().start_time.clone(),
end: op.metadata().end_time.clone(),
});
Ok(L::wrap_timestamp_range(out_property))
});
map.insert("user", |_language, _build_ctx, self_property, function| {
template_parser::expect_no_arguments(function)?;
let out_property = TemplateFunction::new(self_property, |op| {
let out_property = self_property.map(|op| {
// TODO: introduce dedicated type and provide accessors?
Ok(format!(
"{}@{}",
op.metadata().username,
op.metadata().hostname
))
format!("{}@{}", op.metadata().username, op.metadata().hostname)
});
Ok(L::wrap_string(out_property))
});
map.insert("root", |language, _build_ctx, self_property, function| {
template_parser::expect_no_arguments(function)?;
let root_op_id = language.root_op_id.clone();
let out_property =
TemplateFunction::new(self_property, move |op| Ok(op.id() == &root_op_id));
let out_property = self_property.map(move |op| op.id() == &root_op_id);
Ok(L::wrap_boolean(out_property))
});
map
@ -302,10 +291,10 @@ fn builtin_operation_id_methods() -> OperationTemplateBuildMethodFnMap<Operation
let len_property = len_node
.map(|node| template_builder::expect_usize_expression(language, build_ctx, node))
.transpose()?;
let out_property = TemplateFunction::new((self_property, len_property), |(id, len)| {
let out_property = (self_property, len_property).map(|(id, len)| {
let mut hex = id.hex();
hex.truncate(len.unwrap_or(12));
Ok(hex)
hex
});
Ok(L::wrap_string(out_property))
});

View file

@ -24,8 +24,8 @@ use crate::template_parser::{
use crate::templater::{
ConcatTemplate, ConditionalTemplate, IntoTemplate, LabelTemplate, ListPropertyTemplate,
ListTemplate, Literal, PlaceholderTemplate, PlainTextFormattedProperty, PropertyPlaceholder,
ReformatTemplate, SeparateTemplate, Template, TemplateFunction, TemplateProperty,
TemplatePropertyError, TimestampRange,
ReformatTemplate, SeparateTemplate, Template, TemplateProperty, TemplatePropertyError,
TemplatePropertyExt as _, TimestampRange,
};
use crate::{text_util, time_util};
@ -147,14 +147,10 @@ impl<'a> IntoTemplateProperty<'a> for CoreTemplatePropertyKind<'a> {
fn try_into_boolean(self) -> Option<Box<dyn TemplateProperty<Output = bool> + 'a>> {
match self {
CoreTemplatePropertyKind::String(property) => {
Some(Box::new(TemplateFunction::new(property, |s| {
Ok(!s.is_empty())
})))
Some(Box::new(property.map(|s| !s.is_empty())))
}
CoreTemplatePropertyKind::StringList(property) => {
Some(Box::new(TemplateFunction::new(property, |l| {
Ok(!l.is_empty())
})))
Some(Box::new(property.map(|l| !l.is_empty())))
}
CoreTemplatePropertyKind::Boolean(property) => Some(property),
CoreTemplatePropertyKind::Integer(_) => None,
@ -473,11 +469,11 @@ fn build_unary_operation<'a, L: TemplateLanguage<'a> + ?Sized>(
match op {
UnaryOp::LogicalNot => {
let arg = expect_boolean_expression(language, build_ctx, arg_node)?;
Ok(L::wrap_boolean(TemplateFunction::new(arg, |v| Ok(!v))))
Ok(L::wrap_boolean(arg.map(|v| !v)))
}
UnaryOp::Negate => {
let arg = expect_integer_expression(language, build_ctx, arg_node)?;
Ok(L::wrap_integer(TemplateFunction::new(arg, |v| {
Ok(L::wrap_integer(arg.and_then(|v| {
v.checked_neg()
.ok_or_else(|| TemplatePropertyError("Attempt to negate with overflow".into()))
})))
@ -497,19 +493,13 @@ fn build_binary_operation<'a, L: TemplateLanguage<'a> + ?Sized>(
// No short-circuiting supported
let lhs = expect_boolean_expression(language, build_ctx, lhs_node)?;
let rhs = expect_boolean_expression(language, build_ctx, rhs_node)?;
Ok(L::wrap_boolean(TemplateFunction::new(
(lhs, rhs),
|(l, r)| Ok(l | r),
)))
Ok(L::wrap_boolean((lhs, rhs).map(|(l, r)| l | r)))
}
BinaryOp::LogicalAnd => {
// No short-circuiting supported
let lhs = expect_boolean_expression(language, build_ctx, lhs_node)?;
let rhs = expect_boolean_expression(language, build_ctx, rhs_node)?;
Ok(L::wrap_boolean(TemplateFunction::new(
(lhs, rhs),
|(l, r)| Ok(l & r),
)))
Ok(L::wrap_boolean((lhs, rhs).map(|(l, r)| l & r)))
}
}
}
@ -521,7 +511,7 @@ fn builtin_string_methods<'a, L: TemplateLanguage<'a> + ?Sized>(
let mut map = TemplateBuildMethodFnMap::<L, String>::new();
map.insert("len", |_language, _build_ctx, self_property, function| {
template_parser::expect_no_arguments(function)?;
let out_property = TemplateFunction::new(self_property, |s| Ok(s.len().try_into()?));
let out_property = self_property.and_then(|s| Ok(s.len().try_into()?));
Ok(L::wrap_integer(out_property))
});
map.insert(
@ -530,10 +520,8 @@ fn builtin_string_methods<'a, L: TemplateLanguage<'a> + ?Sized>(
let [needle_node] = template_parser::expect_exact_arguments(function)?;
// 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 =
TemplateFunction::new((self_property, needle_property), |(haystack, needle)| {
Ok(haystack.contains(&needle))
});
let out_property = (self_property, needle_property)
.map(|(haystack, needle)| haystack.contains(&needle));
Ok(L::wrap_boolean(out_property))
},
);
@ -542,10 +530,8 @@ fn builtin_string_methods<'a, L: TemplateLanguage<'a> + ?Sized>(
|language, build_ctx, self_property, function| {
let [needle_node] = template_parser::expect_exact_arguments(function)?;
let needle_property = expect_plain_text_expression(language, build_ctx, needle_node)?;
let out_property =
TemplateFunction::new((self_property, needle_property), |(haystack, needle)| {
Ok(haystack.starts_with(&needle))
});
let out_property = (self_property, needle_property)
.map(|(haystack, needle)| haystack.starts_with(&needle));
Ok(L::wrap_boolean(out_property))
},
);
@ -554,10 +540,8 @@ fn builtin_string_methods<'a, L: TemplateLanguage<'a> + ?Sized>(
|language, build_ctx, self_property, function| {
let [needle_node] = template_parser::expect_exact_arguments(function)?;
let needle_property = expect_plain_text_expression(language, build_ctx, needle_node)?;
let out_property =
TemplateFunction::new((self_property, needle_property), |(haystack, needle)| {
Ok(haystack.ends_with(&needle))
});
let out_property = (self_property, needle_property)
.map(|(haystack, needle)| haystack.ends_with(&needle));
Ok(L::wrap_boolean(out_property))
},
);
@ -566,13 +550,12 @@ fn builtin_string_methods<'a, L: TemplateLanguage<'a> + ?Sized>(
|language, build_ctx, self_property, function| {
let [needle_node] = template_parser::expect_exact_arguments(function)?;
let needle_property = expect_plain_text_expression(language, build_ctx, needle_node)?;
let out_property =
TemplateFunction::new((self_property, needle_property), |(haystack, needle)| {
Ok(haystack
.strip_prefix(&needle)
.map(ToOwned::to_owned)
.unwrap_or(haystack))
});
let out_property = (self_property, needle_property).map(|(haystack, needle)| {
haystack
.strip_prefix(&needle)
.map(ToOwned::to_owned)
.unwrap_or(haystack)
});
Ok(L::wrap_string(out_property))
},
);
@ -581,13 +564,12 @@ fn builtin_string_methods<'a, L: TemplateLanguage<'a> + ?Sized>(
|language, build_ctx, self_property, function| {
let [needle_node] = template_parser::expect_exact_arguments(function)?;
let needle_property = expect_plain_text_expression(language, build_ctx, needle_node)?;
let out_property =
TemplateFunction::new((self_property, needle_property), |(haystack, needle)| {
Ok(haystack
.strip_suffix(&needle)
.map(ToOwned::to_owned)
.unwrap_or(haystack))
});
let out_property = (self_property, needle_property).map(|(haystack, needle)| {
haystack
.strip_suffix(&needle)
.map(ToOwned::to_owned)
.unwrap_or(haystack)
});
Ok(L::wrap_string(out_property))
},
);
@ -595,41 +577,36 @@ fn builtin_string_methods<'a, L: TemplateLanguage<'a> + ?Sized>(
let [start_idx, end_idx] = template_parser::expect_exact_arguments(function)?;
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 = TemplateFunction::new(
(self_property, start_idx_property, end_idx_property),
|(s, start_idx, end_idx)| {
let out_property =
(self_property, start_idx_property, end_idx_property).map(|(s, start_idx, end_idx)| {
let start_idx = string_index_to_char_boundary(&s, start_idx);
let end_idx = string_index_to_char_boundary(&s, end_idx);
Ok(s.get(start_idx..end_idx).unwrap_or_default().to_owned())
},
);
s.get(start_idx..end_idx).unwrap_or_default().to_owned()
});
Ok(L::wrap_string(out_property))
});
map.insert(
"first_line",
|_language, _build_ctx, self_property, function| {
template_parser::expect_no_arguments(function)?;
let out_property = TemplateFunction::new(self_property, |s| {
Ok(s.lines().next().unwrap_or_default().to_string())
});
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)?;
let out_property = TemplateFunction::new(self_property, |s| {
Ok(s.lines().map(|l| l.to_owned()).collect())
});
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)?;
let out_property = TemplateFunction::new(self_property, |s| Ok(s.to_uppercase()));
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)?;
let out_property = TemplateFunction::new(self_property, |s| Ok(s.to_lowercase()));
let out_property = self_property.map(|s| s.to_lowercase());
Ok(L::wrap_string(out_property))
});
map
@ -658,21 +635,21 @@ fn builtin_signature_methods<'a, L: TemplateLanguage<'a> + ?Sized>(
let mut map = TemplateBuildMethodFnMap::<L, Signature>::new();
map.insert("name", |_language, _build_ctx, self_property, function| {
template_parser::expect_no_arguments(function)?;
let out_property = TemplateFunction::new(self_property, |signature| Ok(signature.name));
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)?;
let out_property = TemplateFunction::new(self_property, |signature| Ok(signature.email));
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)?;
let out_property = TemplateFunction::new(self_property, |signature| {
let out_property = self_property.map(|signature| {
let (username, _) = text_util::split_email(&signature.email);
Ok(username.to_owned())
username.to_owned()
});
Ok(L::wrap_string(out_property))
},
@ -681,8 +658,7 @@ fn builtin_signature_methods<'a, L: TemplateLanguage<'a> + ?Sized>(
"timestamp",
|_language, _build_ctx, self_property, function| {
template_parser::expect_no_arguments(function)?;
let out_property =
TemplateFunction::new(self_property, |signature| Ok(signature.timestamp));
let out_property = self_property.map(|signature| signature.timestamp);
Ok(L::wrap_timestamp(out_property))
},
);
@ -698,9 +674,8 @@ fn builtin_timestamp_methods<'a, L: TemplateLanguage<'a> + ?Sized>(
template_parser::expect_no_arguments(function)?;
let now = Timestamp::now();
let format = timeago::Formatter::new();
let out_property = TemplateFunction::new(self_property, move |timestamp| {
Ok(time_util::format_duration(&timestamp, &now, &format)?)
});
let out_property = self_property
.and_then(move |timestamp| Ok(time_util::format_duration(&timestamp, &now, &format)?));
Ok(L::wrap_string(out_property))
});
map.insert(
@ -715,7 +690,7 @@ fn builtin_timestamp_methods<'a, L: TemplateLanguage<'a> + ?Sized>(
})
})?
.into_owned();
let out_property = TemplateFunction::new(self_property, move |timestamp| {
let out_property = self_property.and_then(move |timestamp| {
Ok(time_util::format_absolute_timestamp_with(
&timestamp, &format,
)?)
@ -725,9 +700,9 @@ fn builtin_timestamp_methods<'a, L: TemplateLanguage<'a> + ?Sized>(
);
map.insert("utc", |_language, _build_ctx, self_property, function| {
template_parser::expect_no_arguments(function)?;
let out_property = TemplateFunction::new(self_property, |mut timestamp| {
let out_property = self_property.map(|mut timestamp| {
timestamp.tz_offset = 0;
Ok(timestamp)
timestamp
});
Ok(L::wrap_timestamp(out_property))
});
@ -737,9 +712,9 @@ fn builtin_timestamp_methods<'a, L: TemplateLanguage<'a> + ?Sized>(
.ok()
.and_then(|tz_string| tz_string.parse::<i32>().ok())
.unwrap_or_else(|| chrono::Local::now().offset().local_minus_utc() / 60);
let out_property = TemplateFunction::new(self_property, move |mut timestamp| {
let out_property = self_property.map(move |mut timestamp| {
timestamp.tz_offset = tz_offset;
Ok(timestamp)
timestamp
});
Ok(L::wrap_timestamp(out_property))
});
@ -753,20 +728,19 @@ fn builtin_timestamp_range_methods<'a, L: TemplateLanguage<'a> + ?Sized>(
let mut map = TemplateBuildMethodFnMap::<L, TimestampRange>::new();
map.insert("start", |_language, _build_ctx, self_property, function| {
template_parser::expect_no_arguments(function)?;
let out_property = TemplateFunction::new(self_property, |time_range| Ok(time_range.start));
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)?;
let out_property = TemplateFunction::new(self_property, |time_range| Ok(time_range.end));
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)?;
let out_property =
TemplateFunction::new(self_property, |time_range| Ok(time_range.duration()?));
let out_property = self_property.and_then(|time_range| Ok(time_range.duration()?));
Ok(L::wrap_string(out_property))
},
);
@ -807,8 +781,7 @@ where
let property = match function.name {
"len" => {
template_parser::expect_no_arguments(function)?;
let out_property =
TemplateFunction::new(self_property, |items| Ok(items.len().try_into()?));
let out_property = self_property.and_then(|items| Ok(items.len().try_into()?));
L::wrap_integer(out_property)
}
"join" => {
@ -840,8 +813,7 @@ where
let property = match function.name {
"len" => {
template_parser::expect_no_arguments(function)?;
let out_property =
TemplateFunction::new(self_property, |items| Ok(items.len().try_into()?));
let out_property = self_property.and_then(|items| Ok(items.len().try_into()?));
L::wrap_integer(out_property)
}
// No "join"
@ -930,9 +902,8 @@ fn builtin_functions<'a, L: TemplateLanguage<'a> + ?Sized>() -> TemplateBuildFun
let [label_node, content_node] = template_parser::expect_exact_arguments(function)?;
let label_property = expect_plain_text_expression(language, build_ctx, label_node)?;
let content = expect_template_expression(language, build_ctx, content_node)?;
let labels = TemplateFunction::new(label_property, |s| {
Ok(s.split_whitespace().map(ToString::to_string).collect())
});
let labels =
label_property.map(|s| s.split_whitespace().map(ToString::to_string).collect());
Ok(L::wrap_template(Box::new(LabelTemplate::new(
content, labels,
))))
@ -1123,7 +1094,7 @@ pub fn expect_isize_expression<'a, L: TemplateLanguage<'a> + ?Sized>(
node: &ExpressionNode,
) -> TemplateParseResult<Box<dyn TemplateProperty<Output = isize> + 'a>> {
let i64_property = expect_integer_expression(language, build_ctx, node)?;
let isize_property = TemplateFunction::new(i64_property, |v| Ok(isize::try_from(v)?));
let isize_property = i64_property.and_then(|v| Ok(isize::try_from(v)?));
Ok(Box::new(isize_property))
}
@ -1134,7 +1105,7 @@ pub fn expect_usize_expression<'a, L: TemplateLanguage<'a> + ?Sized>(
node: &ExpressionNode,
) -> TemplateParseResult<Box<dyn TemplateProperty<Output = usize> + 'a>> {
let i64_property = expect_integer_expression(language, build_ctx, node)?;
let usize_property = TemplateFunction::new(i64_property, |v| Ok(usize::try_from(v)?));
let usize_property = i64_property.and_then(|v| Ok(usize::try_from(v)?));
Ok(Box::new(usize_property))
}