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

templater: remove Context type from TemplateLanguage/Property

Now a compiled template doesn't have a static Context type internally. A
property is basically of "Fn() -> Result<O, _>" type, and a type-erased "self"
variable will be injected as needed.

Template<C> types will be refactored separately.
This commit is contained in:
Yuya Nishihara 2024-03-20 13:08:34 +09:00
parent 0fad9c9795
commit 911cf4b8f6
6 changed files with 139 additions and 176 deletions

View file

@ -244,7 +244,7 @@ impl CommandHelper {
/// This function also loads template aliases from the settings. Use
/// `WorkspaceCommandHelper::parse_template()` if you've already
/// instantiated the workspace helper.
pub fn parse_template<'a, C: Clone + 'a, L: TemplateLanguage<'a, Context = ()> + ?Sized>(
pub fn parse_template<'a, C: Clone + 'a, L: TemplateLanguage<'a> + ?Sized>(
&self,
ui: &Ui,
language: &L,
@ -899,7 +899,7 @@ Set which revision the branch points to with `jj branch set {branch_name} -r <RE
///
/// `wrap_self` specifies the type of the top-level property, which should
/// be one of the `L::wrap_*()` functions.
pub fn parse_template<'a, C: Clone + 'a, L: TemplateLanguage<'a, Context = ()> + ?Sized>(
pub fn parse_template<'a, C: Clone + 'a, L: TemplateLanguage<'a> + ?Sized>(
&self,
language: &L,
template_text: &str,

View file

@ -96,7 +96,6 @@ impl<'repo> CommitTemplateLanguage<'repo> {
}
impl<'repo> TemplateLanguage<'repo> for CommitTemplateLanguage<'repo> {
type Context = ();
type Property = CommitTemplatePropertyKind<'repo>;
template_builder::impl_core_wrap_property_fns!('repo, CommitTemplatePropertyKind::Core);
@ -185,54 +184,54 @@ impl<'repo> CommitTemplateLanguage<'repo> {
}
pub fn wrap_commit(
property: impl TemplateProperty<(), Output = Commit> + 'repo,
property: impl TemplateProperty<Output = Commit> + 'repo,
) -> CommitTemplatePropertyKind<'repo> {
CommitTemplatePropertyKind::Commit(Box::new(property))
}
pub fn wrap_commit_list(
property: impl TemplateProperty<(), Output = Vec<Commit>> + 'repo,
property: impl TemplateProperty<Output = Vec<Commit>> + 'repo,
) -> CommitTemplatePropertyKind<'repo> {
CommitTemplatePropertyKind::CommitList(Box::new(property))
}
pub fn wrap_ref_name(
property: impl TemplateProperty<(), Output = RefName> + 'repo,
property: impl TemplateProperty<Output = RefName> + 'repo,
) -> CommitTemplatePropertyKind<'repo> {
CommitTemplatePropertyKind::RefName(Box::new(property))
}
pub fn wrap_ref_name_list(
property: impl TemplateProperty<(), Output = Vec<RefName>> + 'repo,
property: impl TemplateProperty<Output = Vec<RefName>> + 'repo,
) -> CommitTemplatePropertyKind<'repo> {
CommitTemplatePropertyKind::RefNameList(Box::new(property))
}
pub fn wrap_commit_or_change_id(
property: impl TemplateProperty<(), Output = CommitOrChangeId> + 'repo,
property: impl TemplateProperty<Output = CommitOrChangeId> + 'repo,
) -> CommitTemplatePropertyKind<'repo> {
CommitTemplatePropertyKind::CommitOrChangeId(Box::new(property))
}
pub fn wrap_shortest_id_prefix(
property: impl TemplateProperty<(), Output = ShortestIdPrefix> + 'repo,
property: impl TemplateProperty<Output = ShortestIdPrefix> + 'repo,
) -> CommitTemplatePropertyKind<'repo> {
CommitTemplatePropertyKind::ShortestIdPrefix(Box::new(property))
}
}
pub enum CommitTemplatePropertyKind<'repo> {
Core(CoreTemplatePropertyKind<'repo, ()>),
Commit(Box<dyn TemplateProperty<(), Output = Commit> + 'repo>),
CommitList(Box<dyn TemplateProperty<(), Output = Vec<Commit>> + 'repo>),
RefName(Box<dyn TemplateProperty<(), Output = RefName> + 'repo>),
RefNameList(Box<dyn TemplateProperty<(), Output = Vec<RefName>> + 'repo>),
CommitOrChangeId(Box<dyn TemplateProperty<(), Output = CommitOrChangeId> + 'repo>),
ShortestIdPrefix(Box<dyn TemplateProperty<(), Output = ShortestIdPrefix> + 'repo>),
Core(CoreTemplatePropertyKind<'repo>),
Commit(Box<dyn TemplateProperty<Output = Commit> + 'repo>),
CommitList(Box<dyn TemplateProperty<Output = Vec<Commit>> + 'repo>),
RefName(Box<dyn TemplateProperty<Output = RefName> + 'repo>),
RefNameList(Box<dyn TemplateProperty<Output = Vec<RefName>> + 'repo>),
CommitOrChangeId(Box<dyn TemplateProperty<Output = CommitOrChangeId> + 'repo>),
ShortestIdPrefix(Box<dyn TemplateProperty<Output = ShortestIdPrefix> + 'repo>),
}
impl<'repo> IntoTemplateProperty<'repo, ()> for CommitTemplatePropertyKind<'repo> {
fn try_into_boolean(self) -> Option<Box<dyn TemplateProperty<(), Output = bool> + 'repo>> {
impl<'repo> IntoTemplateProperty<'repo> for CommitTemplatePropertyKind<'repo> {
fn try_into_boolean(self) -> Option<Box<dyn TemplateProperty<Output = bool> + 'repo>> {
match self {
CommitTemplatePropertyKind::Core(property) => property.try_into_boolean(),
CommitTemplatePropertyKind::Commit(_) => None,
@ -252,14 +251,14 @@ impl<'repo> IntoTemplateProperty<'repo, ()> for CommitTemplatePropertyKind<'repo
}
}
fn try_into_integer(self) -> Option<Box<dyn TemplateProperty<(), Output = i64> + 'repo>> {
fn try_into_integer(self) -> Option<Box<dyn TemplateProperty<Output = i64> + 'repo>> {
match self {
CommitTemplatePropertyKind::Core(property) => property.try_into_integer(),
_ => None,
}
}
fn try_into_plain_text(self) -> Option<Box<dyn TemplateProperty<(), Output = String> + 'repo>> {
fn try_into_plain_text(self) -> Option<Box<dyn TemplateProperty<Output = String> + 'repo>> {
match self {
CommitTemplatePropertyKind::Core(property) => property.try_into_plain_text(),
_ => {

View file

@ -66,7 +66,7 @@ impl<'a, C> GenericTemplateLanguage<'a, C> {
pub fn add_keyword<F>(&mut self, name: &'static str, build: F)
where
F: Fn(
Box<dyn TemplateProperty<(), Output = C> + 'a>,
Box<dyn TemplateProperty<Output = C> + 'a>,
) -> TemplateParseResult<GenericTemplatePropertyKind<'a, C>>
+ 'a,
{
@ -75,7 +75,6 @@ impl<'a, C> GenericTemplateLanguage<'a, C> {
}
impl<'a, C: 'a> TemplateLanguage<'a> for GenericTemplateLanguage<'a, C> {
type Context = ();
type Property = GenericTemplatePropertyKind<'a, C>;
template_builder::impl_core_wrap_property_fns!('a, GenericTemplatePropertyKind::Core);
@ -113,33 +112,33 @@ impl<'a, C: 'a> TemplateLanguage<'a> for GenericTemplateLanguage<'a, C> {
impl<'a, C> GenericTemplateLanguage<'a, C> {
pub fn wrap_self(
property: impl TemplateProperty<(), Output = C> + 'a,
property: impl TemplateProperty<Output = C> + 'a,
) -> GenericTemplatePropertyKind<'a, C> {
GenericTemplatePropertyKind::Self_(Box::new(property))
}
}
pub enum GenericTemplatePropertyKind<'a, C> {
Core(CoreTemplatePropertyKind<'a, ()>),
Self_(Box<dyn TemplateProperty<(), Output = C> + 'a>),
Core(CoreTemplatePropertyKind<'a>),
Self_(Box<dyn TemplateProperty<Output = C> + 'a>),
}
impl<'a, C: 'a> IntoTemplateProperty<'a, ()> for GenericTemplatePropertyKind<'a, C> {
fn try_into_boolean(self) -> Option<Box<dyn TemplateProperty<(), Output = bool> + 'a>> {
impl<'a, C: 'a> IntoTemplateProperty<'a> for GenericTemplatePropertyKind<'a, C> {
fn try_into_boolean(self) -> Option<Box<dyn TemplateProperty<Output = bool> + 'a>> {
match self {
GenericTemplatePropertyKind::Core(property) => property.try_into_boolean(),
GenericTemplatePropertyKind::Self_(_) => None,
}
}
fn try_into_integer(self) -> Option<Box<dyn TemplateProperty<(), Output = i64> + 'a>> {
fn try_into_integer(self) -> Option<Box<dyn TemplateProperty<Output = i64> + 'a>> {
match self {
GenericTemplatePropertyKind::Core(property) => property.try_into_integer(),
GenericTemplatePropertyKind::Self_(_) => None,
}
}
fn try_into_plain_text(self) -> Option<Box<dyn TemplateProperty<(), Output = String> + 'a>> {
fn try_into_plain_text(self) -> Option<Box<dyn TemplateProperty<Output = String> + 'a>> {
match self {
GenericTemplatePropertyKind::Core(property) => property.try_into_plain_text(),
GenericTemplatePropertyKind::Self_(_) => None,
@ -161,7 +160,7 @@ impl<'a, C: 'a> IntoTemplateProperty<'a, ()> for GenericTemplatePropertyKind<'a,
/// global resources, the keyword function is allowed to capture resources.
pub type GenericTemplateBuildKeywordFn<'a, C> = Box<
dyn Fn(
Box<dyn TemplateProperty<(), Output = C> + 'a>,
Box<dyn TemplateProperty<Output = C> + 'a>,
) -> TemplateParseResult<GenericTemplatePropertyKind<'a, C>>
+ 'a,
>;

View file

@ -72,7 +72,6 @@ impl OperationTemplateLanguage {
}
impl TemplateLanguage<'static> for OperationTemplateLanguage {
type Context = ();
type Property = OperationTemplatePropertyKind;
template_builder::impl_core_wrap_property_fns!('static, OperationTemplatePropertyKind::Core);
@ -117,26 +116,26 @@ impl OperationTemplateLanguage {
}
pub fn wrap_operation(
property: impl TemplateProperty<(), Output = Operation> + 'static,
property: impl TemplateProperty<Output = Operation> + 'static,
) -> OperationTemplatePropertyKind {
OperationTemplatePropertyKind::Operation(Box::new(property))
}
pub fn wrap_operation_id(
property: impl TemplateProperty<(), Output = OperationId> + 'static,
property: impl TemplateProperty<Output = OperationId> + 'static,
) -> OperationTemplatePropertyKind {
OperationTemplatePropertyKind::OperationId(Box::new(property))
}
}
pub enum OperationTemplatePropertyKind {
Core(CoreTemplatePropertyKind<'static, ()>),
Operation(Box<dyn TemplateProperty<(), Output = Operation>>),
OperationId(Box<dyn TemplateProperty<(), Output = OperationId>>),
Core(CoreTemplatePropertyKind<'static>),
Operation(Box<dyn TemplateProperty<Output = Operation>>),
OperationId(Box<dyn TemplateProperty<Output = OperationId>>),
}
impl IntoTemplateProperty<'static, ()> for OperationTemplatePropertyKind {
fn try_into_boolean(self) -> Option<Box<dyn TemplateProperty<(), Output = bool>>> {
impl IntoTemplateProperty<'static> for OperationTemplatePropertyKind {
fn try_into_boolean(self) -> Option<Box<dyn TemplateProperty<Output = bool>>> {
match self {
OperationTemplatePropertyKind::Core(property) => property.try_into_boolean(),
OperationTemplatePropertyKind::Operation(_) => None,
@ -144,14 +143,14 @@ impl IntoTemplateProperty<'static, ()> for OperationTemplatePropertyKind {
}
}
fn try_into_integer(self) -> Option<Box<dyn TemplateProperty<(), Output = i64>>> {
fn try_into_integer(self) -> Option<Box<dyn TemplateProperty<Output = i64>>> {
match self {
OperationTemplatePropertyKind::Core(property) => property.try_into_integer(),
_ => None,
}
}
fn try_into_plain_text(self) -> Option<Box<dyn TemplateProperty<(), Output = String>>> {
fn try_into_plain_text(self) -> Option<Box<dyn TemplateProperty<Output = String>>> {
match self {
OperationTemplatePropertyKind::Core(property) => property.try_into_plain_text(),
_ => {

View file

@ -31,33 +31,22 @@ use crate::{text_util, time_util};
/// Callbacks to build language-specific evaluation objects from AST nodes.
pub trait TemplateLanguage<'a> {
type Context: 'a;
type Property: IntoTemplateProperty<'a, Self::Context>;
type Property: IntoTemplateProperty<'a>;
fn wrap_string(
property: impl TemplateProperty<Self::Context, Output = String> + 'a,
) -> Self::Property;
fn wrap_string(property: impl TemplateProperty<Output = String> + 'a) -> Self::Property;
fn wrap_string_list(
property: impl TemplateProperty<Self::Context, Output = Vec<String>> + 'a,
) -> Self::Property;
fn wrap_boolean(
property: impl TemplateProperty<Self::Context, Output = bool> + 'a,
) -> Self::Property;
fn wrap_integer(
property: impl TemplateProperty<Self::Context, Output = i64> + 'a,
) -> Self::Property;
fn wrap_signature(
property: impl TemplateProperty<Self::Context, Output = Signature> + 'a,
) -> Self::Property;
fn wrap_timestamp(
property: impl TemplateProperty<Self::Context, Output = Timestamp> + 'a,
property: impl TemplateProperty<Output = Vec<String>> + 'a,
) -> Self::Property;
fn wrap_boolean(property: impl TemplateProperty<Output = bool> + 'a) -> Self::Property;
fn wrap_integer(property: impl TemplateProperty<Output = i64> + 'a) -> Self::Property;
fn wrap_signature(property: impl TemplateProperty<Output = Signature> + 'a) -> Self::Property;
fn wrap_timestamp(property: impl TemplateProperty<Output = Timestamp> + 'a) -> Self::Property;
fn wrap_timestamp_range(
property: impl TemplateProperty<Self::Context, Output = TimestampRange> + 'a,
property: impl TemplateProperty<Output = TimestampRange> + 'a,
) -> Self::Property;
fn wrap_template(template: Box<dyn Template<Self::Context> + 'a>) -> Self::Property;
fn wrap_list_template(template: Box<dyn ListTemplate<Self::Context> + 'a>) -> Self::Property;
fn wrap_template(template: Box<dyn Template<()> + 'a>) -> Self::Property;
fn wrap_list_template(template: Box<dyn ListTemplate<()> + 'a>) -> Self::Property;
/// Translates the given global `function` call to a property.
///
@ -98,13 +87,13 @@ macro_rules! impl_core_wrap_property_fns {
}
);
fn wrap_template(
template: Box<dyn $crate::templater::Template<Self::Context> + $a>,
template: Box<dyn $crate::templater::Template<()> + $a>,
) -> Self::Property {
use $crate::template_builder::CoreTemplatePropertyKind as Kind;
$outer(Kind::Template(template))
}
fn wrap_list_template(
template: Box<dyn $crate::templater::ListTemplate<Self::Context> + $a>,
template: Box<dyn $crate::templater::ListTemplate<()> + $a>,
) -> Self::Property {
use $crate::template_builder::CoreTemplatePropertyKind as Kind;
$outer(Kind::ListTemplate(template))
@ -116,8 +105,7 @@ macro_rules! impl_wrap_property_fns {
($a:lifetime, $kind:path, $outer:path, { $( $func:ident($ty:ty) => $var:ident, )+ }) => {
$(
fn $func(
property: impl $crate::templater::TemplateProperty<
Self::Context, Output = $ty> + $a,
property: impl $crate::templater::TemplateProperty<Output = $ty> + $a,
) -> Self::Property {
use $kind as Kind; // https://github.com/rust-lang/rust/issues/48067
$outer(Kind::$var(Box::new(property)))
@ -129,32 +117,34 @@ macro_rules! impl_wrap_property_fns {
pub(crate) use {impl_core_wrap_property_fns, impl_wrap_property_fns};
/// Provides access to basic template property types.
pub trait IntoTemplateProperty<'a, C> {
fn try_into_boolean(self) -> Option<Box<dyn TemplateProperty<C, Output = bool> + 'a>>;
fn try_into_integer(self) -> Option<Box<dyn TemplateProperty<C, Output = i64> + 'a>>;
pub trait IntoTemplateProperty<'a> {
fn try_into_boolean(self) -> Option<Box<dyn TemplateProperty<Output = bool> + 'a>>;
fn try_into_integer(self) -> Option<Box<dyn TemplateProperty<Output = i64> + 'a>>;
fn try_into_plain_text(self) -> Option<Box<dyn TemplateProperty<C, Output = String> + 'a>>;
fn try_into_template(self) -> Option<Box<dyn Template<C> + 'a>>;
fn try_into_plain_text(self) -> Option<Box<dyn TemplateProperty<Output = String> + 'a>>;
fn try_into_template(self) -> Option<Box<dyn Template<()> + 'a>>;
}
pub enum CoreTemplatePropertyKind<'a, I> {
String(Box<dyn TemplateProperty<I, Output = String> + 'a>),
StringList(Box<dyn TemplateProperty<I, Output = Vec<String>> + 'a>),
Boolean(Box<dyn TemplateProperty<I, Output = bool> + 'a>),
Integer(Box<dyn TemplateProperty<I, Output = i64> + 'a>),
Signature(Box<dyn TemplateProperty<I, Output = Signature> + 'a>),
Timestamp(Box<dyn TemplateProperty<I, Output = Timestamp> + 'a>),
TimestampRange(Box<dyn TemplateProperty<I, Output = TimestampRange> + 'a>),
pub enum CoreTemplatePropertyKind<'a> {
String(Box<dyn TemplateProperty<Output = String> + 'a>),
StringList(Box<dyn TemplateProperty<Output = Vec<String>> + 'a>),
Boolean(Box<dyn TemplateProperty<Output = bool> + 'a>),
Integer(Box<dyn TemplateProperty<Output = i64> + 'a>),
Signature(Box<dyn TemplateProperty<Output = Signature> + 'a>),
Timestamp(Box<dyn TemplateProperty<Output = Timestamp> + 'a>),
TimestampRange(Box<dyn TemplateProperty<Output = TimestampRange> + 'a>),
// TODO: This argument no longer makes sense. Maybe we can migrate these to
// TemplateProperty<..>:
// Similar to `TemplateProperty<I, Output = Box<dyn Template<()> + 'a>`, but doesn't
// capture `I` to produce `Template<()>`. The context `I` would have to be cloned
// to convert `Template<I>` to `Template<()>`.
Template(Box<dyn Template<I> + 'a>),
ListTemplate(Box<dyn ListTemplate<I> + 'a>),
Template(Box<dyn Template<()> + 'a>),
ListTemplate(Box<dyn ListTemplate<()> + 'a>),
}
impl<'a, I: 'a> IntoTemplateProperty<'a, I> for CoreTemplatePropertyKind<'a, I> {
fn try_into_boolean(self) -> Option<Box<dyn TemplateProperty<I, Output = bool> + 'a>> {
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| {
@ -179,14 +169,14 @@ impl<'a, I: 'a> IntoTemplateProperty<'a, I> for CoreTemplatePropertyKind<'a, I>
}
}
fn try_into_integer(self) -> Option<Box<dyn TemplateProperty<I, Output = i64> + 'a>> {
fn try_into_integer(self) -> Option<Box<dyn TemplateProperty<Output = i64> + 'a>> {
match self {
CoreTemplatePropertyKind::Integer(property) => Some(property),
_ => None,
}
}
fn try_into_plain_text(self) -> Option<Box<dyn TemplateProperty<I, Output = String> + 'a>> {
fn try_into_plain_text(self) -> Option<Box<dyn TemplateProperty<Output = String> + 'a>> {
match self {
CoreTemplatePropertyKind::String(property) => Some(property),
_ => {
@ -204,7 +194,7 @@ impl<'a, I: 'a> IntoTemplateProperty<'a, I> for CoreTemplatePropertyKind<'a, I>
}
}
fn try_into_template(self) -> Option<Box<dyn Template<I> + 'a>> {
fn try_into_template(self) -> Option<Box<dyn Template<()> + 'a>> {
match self {
CoreTemplatePropertyKind::String(property) => Some(property.into_template()),
CoreTemplatePropertyKind::StringList(property) => Some(property.into_template()),
@ -237,7 +227,7 @@ pub type TemplateBuildMethodFn<'a, L, T> =
fn(
&L,
&BuildContext<<L as TemplateLanguage<'a>>::Property>,
Box<dyn TemplateProperty<<L as TemplateLanguage<'a>>::Context, Output = T> + 'a>,
Box<dyn TemplateProperty<Output = T> + 'a>,
&FunctionCallNode,
) -> TemplateParseResult<<L as TemplateLanguage<'a>>::Property>;
@ -331,7 +321,7 @@ impl<'a, L: TemplateLanguage<'a> + ?Sized> CoreTemplateBuildFnTable<'a, L> {
&self,
language: &L,
build_ctx: &BuildContext<L::Property>,
property: CoreTemplatePropertyKind<'a, L::Context>,
property: CoreTemplatePropertyKind<'a>,
function: &FunctionCallNode,
) -> TemplateParseResult<L::Property> {
match property {
@ -399,38 +389,22 @@ impl<P> Expression<P> {
let labels = vec![label.into()];
Expression { property, labels }
}
}
pub fn try_into_boolean<'a, C: 'a>(
self,
) -> Option<Box<dyn TemplateProperty<C, Output = bool> + 'a>>
where
P: IntoTemplateProperty<'a, C>,
{
impl<'a, P: IntoTemplateProperty<'a>> Expression<P> {
pub fn try_into_boolean(self) -> Option<Box<dyn TemplateProperty<Output = bool> + 'a>> {
self.property.try_into_boolean()
}
pub fn try_into_integer<'a, C: 'a>(
self,
) -> Option<Box<dyn TemplateProperty<C, Output = i64> + 'a>>
where
P: IntoTemplateProperty<'a, C>,
{
pub fn try_into_integer(self) -> Option<Box<dyn TemplateProperty<Output = i64> + 'a>> {
self.property.try_into_integer()
}
pub fn try_into_plain_text<'a, C: 'a>(
self,
) -> Option<Box<dyn TemplateProperty<C, Output = String> + 'a>>
where
P: IntoTemplateProperty<'a, C>,
{
pub fn try_into_plain_text(self) -> Option<Box<dyn TemplateProperty<Output = String> + 'a>> {
self.property.try_into_plain_text()
}
pub fn try_into_template<'a, C: 'a>(self) -> Option<Box<dyn Template<C> + 'a>>
where
P: IntoTemplateProperty<'a, C>,
{
pub fn try_into_template(self) -> Option<Box<dyn Template<()> + 'a>> {
let template = self.property.try_into_template()?;
if self.labels.is_empty() {
Some(template)
@ -802,7 +776,7 @@ fn builtin_timestamp_range_methods<'a, L: TemplateLanguage<'a> + ?Sized>(
fn build_list_template_method<'a, L: TemplateLanguage<'a> + ?Sized>(
language: &L,
build_ctx: &BuildContext<L::Property>,
self_template: Box<dyn ListTemplate<L::Context> + 'a>,
self_template: Box<dyn ListTemplate<()> + 'a>,
function: &FunctionCallNode,
) -> TemplateParseResult<L::Property> {
let property = match function.name {
@ -820,9 +794,9 @@ fn build_list_template_method<'a, L: TemplateLanguage<'a> + ?Sized>(
pub fn build_formattable_list_method<'a, L, O>(
language: &L,
build_ctx: &BuildContext<L::Property>,
self_property: impl TemplateProperty<L::Context, Output = Vec<O>> + 'a,
self_property: impl TemplateProperty<Output = Vec<O>> + 'a,
function: &FunctionCallNode,
// TODO: Generic L: WrapProperty<L::Context, O> trait might be needed to support more
// TODO: Generic L: WrapProperty<O> trait might be needed to support more
// list operations such as first()/slice(). For .map(), a simple callback works.
wrap_item: impl Fn(PropertyPlaceholder<O>) -> L::Property,
) -> TemplateParseResult<L::Property>
@ -855,7 +829,7 @@ where
pub fn build_unformattable_list_method<'a, L, O>(
language: &L,
build_ctx: &BuildContext<L::Property>,
self_property: impl TemplateProperty<L::Context, Output = Vec<O>> + 'a,
self_property: impl TemplateProperty<Output = Vec<O>> + 'a,
function: &FunctionCallNode,
wrap_item: impl Fn(PropertyPlaceholder<O>) -> L::Property,
) -> TemplateParseResult<L::Property>
@ -890,16 +864,12 @@ fn build_map_operation<'a, L, O, P>(
) -> TemplateParseResult<L::Property>
where
L: TemplateLanguage<'a> + ?Sized,
P: TemplateProperty<L::Context> + 'a,
P: TemplateProperty + 'a,
P::Output: IntoIterator<Item = O>,
O: Clone + 'a,
{
// Build an item template with placeholder property, then evaluate it
// for each item.
//
// It would be nice if we could build a template of (L::Context, O)
// input, but doing that for a generic item type wouldn't be easy. It's
// also invalid to convert &C to &(C, _).
let [lambda_node] = template_parser::expect_exact_arguments(function)?;
let item_placeholder = PropertyPlaceholder::new();
let item_template = template_parser::expect_lambda_with(lambda_node, |lambda, _span| {
@ -937,8 +907,8 @@ fn builtin_functions<'a, L: TemplateLanguage<'a> + ?Sized>() -> TemplateBuildFun
let [width_node, content_node] = template_parser::expect_exact_arguments(function)?;
let width = expect_usize_expression(language, build_ctx, width_node)?;
let content = expect_template_expression(language, build_ctx, content_node)?;
let template = ReformatTemplate::new(content, move |context, formatter, recorded| {
match width.extract(context) {
let template = ReformatTemplate::new(content, move |_context, formatter, recorded| {
match width.extract() {
Ok(width) => text_util::write_wrapped(formatter, recorded, width),
Err(err) => err.format(&(), formatter),
}
@ -1099,10 +1069,10 @@ pub type RootTemplate<'a, C> = PlaceholderTemplate<C, Box<dyn Template<()> + 'a>
///
/// `wrap_self` specifies the type of the top-level property, which should be
/// one of the `L::wrap_*()` functions.
pub fn build<'a, C: Clone + 'a, L: TemplateLanguage<'a, Context = ()> + ?Sized>(
pub fn build<'a, C: Clone + 'a, L: TemplateLanguage<'a> + ?Sized>(
language: &L,
node: &ExpressionNode,
// TODO: Generic L: WrapProperty<(), C> trait might be better. See the
// TODO: Generic L: WrapProperty<C> trait might be better. See the
// comment in build_formattable_list_method().
wrap_self: impl Fn(PropertyPlaceholder<C>) -> L::Property,
) -> TemplateParseResult<RootTemplate<'a, C>> {
@ -1116,7 +1086,7 @@ pub fn build<'a, C: Clone + 'a, L: TemplateLanguage<'a, Context = ()> + ?Sized>(
}
/// Parses text, expands aliases, then builds template evaluation tree.
pub fn parse<'a, C: Clone + 'a, L: TemplateLanguage<'a, Context = ()> + ?Sized>(
pub fn parse<'a, C: Clone + 'a, L: TemplateLanguage<'a> + ?Sized>(
language: &L,
template_text: &str,
aliases_map: &TemplateAliasesMap,
@ -1130,7 +1100,7 @@ pub fn expect_boolean_expression<'a, L: TemplateLanguage<'a> + ?Sized>(
language: &L,
build_ctx: &BuildContext<L::Property>,
node: &ExpressionNode,
) -> TemplateParseResult<Box<dyn TemplateProperty<L::Context, Output = bool> + 'a>> {
) -> TemplateParseResult<Box<dyn TemplateProperty<Output = bool> + 'a>> {
build_expression(language, build_ctx, node)?
.try_into_boolean()
.ok_or_else(|| TemplateParseError::expected_type("Boolean", node.span))
@ -1140,7 +1110,7 @@ pub fn expect_integer_expression<'a, L: TemplateLanguage<'a> + ?Sized>(
language: &L,
build_ctx: &BuildContext<L::Property>,
node: &ExpressionNode,
) -> TemplateParseResult<Box<dyn TemplateProperty<L::Context, Output = i64> + 'a>> {
) -> TemplateParseResult<Box<dyn TemplateProperty<Output = i64> + 'a>> {
build_expression(language, build_ctx, node)?
.try_into_integer()
.ok_or_else(|| TemplateParseError::expected_type("Integer", node.span))
@ -1151,7 +1121,7 @@ pub fn expect_isize_expression<'a, L: TemplateLanguage<'a> + ?Sized>(
language: &L,
build_ctx: &BuildContext<L::Property>,
node: &ExpressionNode,
) -> TemplateParseResult<Box<dyn TemplateProperty<L::Context, Output = isize> + 'a>> {
) -> 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)?));
Ok(Box::new(isize_property))
@ -1162,7 +1132,7 @@ pub fn expect_usize_expression<'a, L: TemplateLanguage<'a> + ?Sized>(
language: &L,
build_ctx: &BuildContext<L::Property>,
node: &ExpressionNode,
) -> TemplateParseResult<Box<dyn TemplateProperty<L::Context, Output = usize> + 'a>> {
) -> 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)?));
Ok(Box::new(usize_property))
@ -1172,7 +1142,7 @@ pub fn expect_plain_text_expression<'a, L: TemplateLanguage<'a> + ?Sized>(
language: &L,
build_ctx: &BuildContext<L::Property>,
node: &ExpressionNode,
) -> TemplateParseResult<Box<dyn TemplateProperty<L::Context, Output = String> + 'a>> {
) -> TemplateParseResult<Box<dyn TemplateProperty<Output = String> + 'a>> {
// Since any formattable type can be converted to a string property,
// the expected type is not a String, but a Template.
build_expression(language, build_ctx, node)?
@ -1184,7 +1154,7 @@ pub fn expect_template_expression<'a, L: TemplateLanguage<'a> + ?Sized>(
language: &L,
build_ctx: &BuildContext<L::Property>,
node: &ExpressionNode,
) -> TemplateParseResult<Box<dyn Template<L::Context> + 'a>> {
) -> TemplateParseResult<Box<dyn Template<()> + 'a>> {
build_expression(language, build_ctx, node)?
.try_into_template()
.ok_or_else(|| TemplateParseError::expected_type("Template", node.span))

View file

@ -148,7 +148,7 @@ impl<T, L> LabelTemplate<T, L> {
pub fn new<C>(content: T, labels: L) -> Self
where
T: Template<C>,
L: TemplateProperty<C, Output = Vec<String>>,
L: TemplateProperty<Output = Vec<String>>,
{
LabelTemplate { content, labels }
}
@ -157,10 +157,10 @@ impl<T, L> LabelTemplate<T, L> {
impl<C, T, L> Template<C> for LabelTemplate<T, L>
where
T: Template<C>,
L: TemplateProperty<C, Output = Vec<String>>,
L: TemplateProperty<Output = Vec<String>>,
{
fn format(&self, context: &C, formatter: &mut dyn Formatter) -> io::Result<()> {
let labels = match self.labels.extract(context) {
let labels = match self.labels.extract() {
Ok(labels) => labels,
Err(err) => return err.format(&(), formatter),
};
@ -285,27 +285,25 @@ impl Template<()> for TemplatePropertyError {
}
}
pub trait TemplateProperty<C> {
pub trait TemplateProperty {
type Output;
fn extract(&self, context: &C) -> Result<Self::Output, TemplatePropertyError>;
fn extract(&self) -> Result<Self::Output, TemplatePropertyError>;
}
impl<C, P: TemplateProperty<C> + ?Sized> TemplateProperty<C> for Box<P> {
type Output = <P as TemplateProperty<C>>::Output;
impl<P: TemplateProperty + ?Sized> TemplateProperty for Box<P> {
type Output = <P as TemplateProperty>::Output;
fn extract(&self, context: &C) -> Result<Self::Output, TemplatePropertyError> {
<P as TemplateProperty<C>>::extract(self, context)
fn extract(&self) -> Result<Self::Output, TemplatePropertyError> {
<P as TemplateProperty>::extract(self)
}
}
impl<C, P: TemplateProperty<C>> TemplateProperty<C> for Option<P> {
impl<P: TemplateProperty> TemplateProperty for Option<P> {
type Output = Option<P::Output>;
fn extract(&self, context: &C) -> Result<Self::Output, TemplatePropertyError> {
self.as_ref()
.map(|property| property.extract(context))
.transpose()
fn extract(&self) -> Result<Self::Output, TemplatePropertyError> {
self.as_ref().map(|property| property.extract()).transpose()
}
}
@ -313,11 +311,11 @@ impl<C, P: TemplateProperty<C>> TemplateProperty<C> for Option<P> {
macro_rules! tuple_impls {
($( ( $($n:tt $T:ident),+ ) )+) => {
$(
impl<C, $($T: TemplateProperty<C>,)+> TemplateProperty<C> for ($($T,)+) {
impl<$($T: TemplateProperty,)+> TemplateProperty for ($($T,)+) {
type Output = ($($T::Output,)+);
fn extract(&self, context: &C) -> Result<Self::Output, TemplatePropertyError> {
Ok(($(self.$n.extract(context)?,)+))
fn extract(&self) -> Result<Self::Output, TemplatePropertyError> {
Ok(($(self.$n.extract()?,)+))
}
}
)+
@ -340,10 +338,10 @@ impl<C, O: Template<()>> Template<C> for Literal<O> {
}
}
impl<C, O: Clone> TemplateProperty<C> for Literal<O> {
impl<O: Clone> TemplateProperty for Literal<O> {
type Output = O;
fn extract(&self, _context: &C) -> Result<Self::Output, TemplatePropertyError> {
fn extract(&self) -> Result<Self::Output, TemplatePropertyError> {
Ok(self.0.clone())
}
}
@ -354,33 +352,33 @@ pub struct FormattablePropertyTemplate<P> {
}
impl<P> FormattablePropertyTemplate<P> {
pub fn new<C>(property: P) -> Self
pub fn new(property: P) -> Self
where
P: TemplateProperty<C>,
P: TemplateProperty,
P::Output: Template<()>,
{
FormattablePropertyTemplate { property }
}
}
impl<C, P> Template<C> for FormattablePropertyTemplate<P>
impl<P> Template<()> for FormattablePropertyTemplate<P>
where
P: TemplateProperty<C>,
P: TemplateProperty,
P::Output: Template<()>,
{
fn format(&self, context: &C, formatter: &mut dyn Formatter) -> io::Result<()> {
match self.property.extract(context) {
fn format(&self, _: &(), formatter: &mut dyn Formatter) -> io::Result<()> {
match self.property.extract() {
Ok(template) => template.format(&(), formatter),
Err(err) => err.format(&(), formatter),
}
}
}
impl<'a, C: 'a, O> IntoTemplate<'a, C> for Box<dyn TemplateProperty<C, Output = O> + 'a>
impl<'a, O> IntoTemplate<'a, ()> for Box<dyn TemplateProperty<Output = O> + 'a>
where
O: Template<()> + 'a,
{
fn into_template(self) -> Box<dyn Template<C> + 'a> {
fn into_template(self) -> Box<dyn Template<()> + 'a> {
Box::new(FormattablePropertyTemplate::new(self))
}
}
@ -396,13 +394,13 @@ impl<T> PlainTextFormattedProperty<T> {
}
}
impl<C, T: Template<C>> TemplateProperty<C> for PlainTextFormattedProperty<T> {
impl<T: Template<()>> TemplateProperty for PlainTextFormattedProperty<T> {
type Output = String;
fn extract(&self, context: &C) -> Result<Self::Output, TemplatePropertyError> {
fn extract(&self) -> Result<Self::Output, TemplatePropertyError> {
let mut output = vec![];
self.template
.format(context, &mut PlainTextFormatter::new(&mut output))
.format(&(), &mut PlainTextFormatter::new(&mut output))
.expect("write() to PlainTextFormatter should never fail");
Ok(String::from_utf8(output).map_err(|err| err.utf8_error())?)
}
@ -421,7 +419,7 @@ pub struct ListPropertyTemplate<P, S, F> {
impl<P, S, F> ListPropertyTemplate<P, S, F> {
pub fn new<C, O>(property: P, separator: S, format_item: F) -> Self
where
P: TemplateProperty<C>,
P: TemplateProperty,
P::Output: IntoIterator<Item = O>,
S: Template<C>,
F: Fn(&C, &mut dyn Formatter, O) -> io::Result<()>,
@ -436,13 +434,13 @@ impl<P, S, F> ListPropertyTemplate<P, S, F> {
impl<C, O, P, S, F> Template<C> for ListPropertyTemplate<P, S, F>
where
P: TemplateProperty<C>,
P: TemplateProperty,
P::Output: IntoIterator<Item = O>,
S: Template<C>,
F: Fn(&C, &mut dyn Formatter, O) -> io::Result<()>,
{
fn format(&self, context: &C, formatter: &mut dyn Formatter) -> io::Result<()> {
let contents = match self.property.extract(context) {
let contents = match self.property.extract() {
Ok(contents) => contents,
Err(err) => return err.format(&(), formatter),
};
@ -458,7 +456,7 @@ where
impl<C, O, P, S, F> ListTemplate<C> for ListPropertyTemplate<P, S, F>
where
P: TemplateProperty<C>,
P: TemplateProperty,
P::Output: IntoIterator<Item = O>,
S: Template<C>,
F: Fn(&C, &mut dyn Formatter, O) -> io::Result<()>,
@ -494,7 +492,7 @@ pub struct ConditionalTemplate<P, T, U> {
impl<P, T, U> ConditionalTemplate<P, T, U> {
pub fn new<C>(condition: P, true_template: T, false_template: Option<U>) -> Self
where
P: TemplateProperty<C, Output = bool>,
P: TemplateProperty<Output = bool>,
T: Template<C>,
U: Template<C>,
{
@ -508,12 +506,12 @@ impl<P, T, U> ConditionalTemplate<P, T, U> {
impl<C, P, T, U> Template<C> for ConditionalTemplate<P, T, U>
where
P: TemplateProperty<C, Output = bool>,
P: TemplateProperty<Output = bool>,
T: Template<C>,
U: Template<C>,
{
fn format(&self, context: &C, formatter: &mut dyn Formatter) -> io::Result<()> {
let condition = match self.condition.extract(context) {
let condition = match self.condition.extract() {
Ok(condition) => condition,
Err(err) => return err.format(&(), formatter),
};
@ -526,32 +524,30 @@ where
}
}
// TODO: If needed, add a ContextualTemplateFunction where the function also
// gets the context
pub struct TemplateFunction<P, F> {
pub property: P,
pub function: F,
}
impl<P, F> TemplateFunction<P, F> {
pub fn new<C, O>(property: P, function: F) -> Self
pub fn new<O>(property: P, function: F) -> Self
where
P: TemplateProperty<C>,
P: TemplateProperty,
F: Fn(P::Output) -> Result<O, TemplatePropertyError>,
{
TemplateFunction { property, function }
}
}
impl<C, O, P, F> TemplateProperty<C> for TemplateFunction<P, F>
impl<O, P, F> TemplateProperty for TemplateFunction<P, F>
where
P: TemplateProperty<C>,
P: TemplateProperty,
F: Fn(P::Output) -> Result<O, TemplatePropertyError>,
{
type Output = O;
fn extract(&self, context: &C) -> Result<Self::Output, TemplatePropertyError> {
(self.function)(self.property.extract(context)?)
fn extract(&self) -> Result<Self::Output, TemplatePropertyError> {
(self.function)(self.property.extract()?)
}
}
@ -590,10 +586,10 @@ impl<O> Default for PropertyPlaceholder<O> {
}
}
impl<C, O: Clone> TemplateProperty<C> for PropertyPlaceholder<O> {
impl<O: Clone> TemplateProperty for PropertyPlaceholder<O> {
type Output = O;
fn extract(&self, _: &C) -> Result<Self::Output, TemplatePropertyError> {
fn extract(&self) -> Result<Self::Output, TemplatePropertyError> {
Ok(self
.value
.borrow()