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

generic_templater: clone &Context to Self_ property like other languages

This prepares for removal of TemplateLanguage::Context type. "C: Clone" trait
bounds looked messy, but they can be removed soon.
This commit is contained in:
Yuya Nishihara 2024-03-19 21:17:48 +09:00
parent 915e75efc0
commit 017026148b
3 changed files with 55 additions and 39 deletions

View file

@ -26,7 +26,7 @@ use crate::command_error::{user_error, CommandError};
use crate::config::{AnnotatedValue, ConfigSource}; use crate::config::{AnnotatedValue, ConfigSource};
use crate::generic_templater::GenericTemplateLanguage; use crate::generic_templater::GenericTemplateLanguage;
use crate::template_builder::TemplateLanguage as _; use crate::template_builder::TemplateLanguage as _;
use crate::templater::TemplatePropertyFn; use crate::templater::TemplateFunction;
use crate::ui::Ui; use crate::ui::Ui;
#[derive(clap::Args, Clone, Debug)] #[derive(clap::Args, Clone, Debug)]
@ -184,25 +184,28 @@ pub(crate) fn cmd_config(
} }
} }
// AnnotatedValue will be cloned internally in the templater. If the cloning
// cost matters, wrap it with Rc.
fn config_template_language() -> GenericTemplateLanguage<'static, AnnotatedValue> { fn config_template_language() -> GenericTemplateLanguage<'static, AnnotatedValue> {
type L = GenericTemplateLanguage<'static, AnnotatedValue>; type L = GenericTemplateLanguage<'static, AnnotatedValue>;
fn prop_fn<R, F: Fn(&AnnotatedValue) -> R>(f: F) -> TemplatePropertyFn<F> { let mut language = L::new();
TemplatePropertyFn(f)
}
let mut language = GenericTemplateLanguage::new();
// "name" instead of "path" to avoid confusion with the source file path // "name" instead of "path" to avoid confusion with the source file path
language.add_keyword("name", || { language.add_keyword("name", |self_property| {
let property = prop_fn(|annotated| Ok(annotated.path.join("."))); let out_property =
Ok(L::wrap_string(property)) TemplateFunction::new(self_property, |annotated| Ok(annotated.path.join(".")));
Ok(L::wrap_string(out_property))
}); });
language.add_keyword("value", || { language.add_keyword("value", |self_property| {
// TODO: would be nice if we can provide raw dynamically-typed value // TODO: would be nice if we can provide raw dynamically-typed value
let property = prop_fn(|annotated| Ok(serialize_config_value(&annotated.value))); let out_property = TemplateFunction::new(self_property, |annotated| {
Ok(L::wrap_string(property)) Ok(serialize_config_value(&annotated.value))
});
Ok(L::wrap_string(out_property))
}); });
language.add_keyword("overridden", || { language.add_keyword("overridden", |self_property| {
let property = prop_fn(|annotated| Ok(annotated.is_overridden)); let out_property =
Ok(L::wrap_boolean(property)) TemplateFunction::new(self_property, |annotated| Ok(annotated.is_overridden));
Ok(L::wrap_boolean(out_property))
}); });
language language
} }

View file

@ -19,19 +19,19 @@ use crate::template_builder::{
TemplateLanguage, TemplateLanguage,
}; };
use crate::template_parser::{self, FunctionCallNode, TemplateParseResult}; use crate::template_parser::{self, FunctionCallNode, TemplateParseResult};
use crate::templater::{Template, TemplateProperty}; use crate::templater::{Template, TemplateProperty, TemplatePropertyFn};
/// General-purpose template language for basic value types. /// General-purpose template language for basic value types.
/// ///
/// This template language only supports the core template property types (plus /// This template language only supports the core template property types (plus
/// the context type `C`.) The context type `C` is usually a tuple or struct of /// the context type `C`.) The context type `C` is usually a tuple or struct of
/// value types. Keyword functions need to be registered to extract properties /// value types. It's cloned several times internally. Keyword functions need to
/// from the context object. /// be registered to extract properties from the context object.
pub struct GenericTemplateLanguage<'a, C> { pub struct GenericTemplateLanguage<'a, C: Clone> {
build_fn_table: GenericTemplateBuildFnTable<'a, C>, build_fn_table: GenericTemplateBuildFnTable<'a, C>,
} }
impl<'a, C> GenericTemplateLanguage<'a, C> { impl<'a, C: Clone> GenericTemplateLanguage<'a, C> {
/// Sets up environment with no keywords. /// Sets up environment with no keywords.
/// ///
/// New keyword functions can be registered by `add_keyword()`. /// New keyword functions can be registered by `add_keyword()`.
@ -55,32 +55,33 @@ impl<'a, C> GenericTemplateLanguage<'a, C> {
/// ///
/// A keyword function returns `Self::Property`, which is basically a /// A keyword function returns `Self::Property`, which is basically a
/// closure tagged by its return type. The inner closure is usually wrapped /// closure tagged by its return type. The inner closure is usually wrapped
/// by `TemplatePropertyFn`. /// by `TemplateFunction`.
/// ///
/// ```ignore /// ```ignore
/// language.add_keyword("name", || { /// language.add_keyword("name", |self_property| {
/// let property = TemplatePropertyFn(|v: &C| Ok(v.to_string())); /// let out_property = TemplateFunction::new(self_property, |v| Ok(v.to_string()));
/// Ok(GenericTemplateLanguage::wrap_string(property)) /// Ok(GenericTemplateLanguage::wrap_string(out_property))
/// }); /// });
/// ``` /// ```
pub fn add_keyword<F>(&mut self, name: &'static str, build: F) pub fn add_keyword<F>(&mut self, name: &'static str, build: F)
where where
F: Fn() -> TemplateParseResult<GenericTemplatePropertyKind<'a, C>> + 'a, F: Fn(
Box<dyn TemplateProperty<C, Output = C> + 'a>,
) -> TemplateParseResult<GenericTemplatePropertyKind<'a, C>>
+ 'a,
{ {
self.build_fn_table.keywords.insert(name, Box::new(build)); self.build_fn_table.keywords.insert(name, Box::new(build));
} }
} }
impl<'a, C: 'a> TemplateLanguage<'a> for GenericTemplateLanguage<'a, C> { impl<'a, C: Clone + 'a> TemplateLanguage<'a> for GenericTemplateLanguage<'a, C> {
type Context = C; type Context = C;
type Property = GenericTemplatePropertyKind<'a, C>; type Property = GenericTemplatePropertyKind<'a, C>;
template_builder::impl_core_wrap_property_fns!('a, GenericTemplatePropertyKind::Core); template_builder::impl_core_wrap_property_fns!('a, GenericTemplatePropertyKind::Core);
fn build_self(&self) -> Self::Property { fn build_self(&self) -> Self::Property {
// No need to clone the context object because there are no other Self::wrap_self(TemplatePropertyFn(|context: &C| Ok(context.clone())))
// objects of "Self" type, and the context is available everywhere.
GenericTemplatePropertyKind::Self_
} }
fn build_function( fn build_function(
@ -103,48 +104,56 @@ impl<'a, C: 'a> TemplateLanguage<'a> for GenericTemplateLanguage<'a, C> {
let table = &self.build_fn_table.core; let table = &self.build_fn_table.core;
table.build_method(self, build_ctx, property, function) table.build_method(self, build_ctx, property, function)
} }
GenericTemplatePropertyKind::Self_ => { GenericTemplatePropertyKind::Self_(property) => {
let table = &self.build_fn_table.keywords; let table = &self.build_fn_table.keywords;
let build = template_parser::lookup_method("Self", table, function)?; let build = template_parser::lookup_method("Self", table, function)?;
// For simplicity, only 0-ary method is supported. // For simplicity, only 0-ary method is supported.
template_parser::expect_no_arguments(function)?; template_parser::expect_no_arguments(function)?;
build() build(property)
} }
} }
} }
} }
impl<'a, C: Clone> GenericTemplateLanguage<'a, C> {
pub fn wrap_self(
property: impl TemplateProperty<C, Output = C> + 'a,
) -> GenericTemplatePropertyKind<'a, C> {
GenericTemplatePropertyKind::Self_(Box::new(property))
}
}
pub enum GenericTemplatePropertyKind<'a, C> { pub enum GenericTemplatePropertyKind<'a, C> {
Core(CoreTemplatePropertyKind<'a, C>), Core(CoreTemplatePropertyKind<'a, C>),
Self_, Self_(Box<dyn TemplateProperty<C, Output = C> + 'a>),
} }
impl<'a, C: 'a> IntoTemplateProperty<'a, C> for GenericTemplatePropertyKind<'a, C> { impl<'a, C: 'a> IntoTemplateProperty<'a, C> for GenericTemplatePropertyKind<'a, C> {
fn try_into_boolean(self) -> Option<Box<dyn TemplateProperty<C, Output = bool> + 'a>> { fn try_into_boolean(self) -> Option<Box<dyn TemplateProperty<C, Output = bool> + 'a>> {
match self { match self {
GenericTemplatePropertyKind::Core(property) => property.try_into_boolean(), GenericTemplatePropertyKind::Core(property) => property.try_into_boolean(),
GenericTemplatePropertyKind::Self_ => None, GenericTemplatePropertyKind::Self_(_) => None,
} }
} }
fn try_into_integer(self) -> Option<Box<dyn TemplateProperty<C, Output = i64> + 'a>> { fn try_into_integer(self) -> Option<Box<dyn TemplateProperty<C, Output = i64> + 'a>> {
match self { match self {
GenericTemplatePropertyKind::Core(property) => property.try_into_integer(), GenericTemplatePropertyKind::Core(property) => property.try_into_integer(),
GenericTemplatePropertyKind::Self_ => None, GenericTemplatePropertyKind::Self_(_) => None,
} }
} }
fn try_into_plain_text(self) -> Option<Box<dyn TemplateProperty<C, Output = String> + 'a>> { fn try_into_plain_text(self) -> Option<Box<dyn TemplateProperty<C, Output = String> + 'a>> {
match self { match self {
GenericTemplatePropertyKind::Core(property) => property.try_into_plain_text(), GenericTemplatePropertyKind::Core(property) => property.try_into_plain_text(),
GenericTemplatePropertyKind::Self_ => None, GenericTemplatePropertyKind::Self_(_) => None,
} }
} }
fn try_into_template(self) -> Option<Box<dyn Template<C> + 'a>> { fn try_into_template(self) -> Option<Box<dyn Template<C> + 'a>> {
match self { match self {
GenericTemplatePropertyKind::Core(property) => property.try_into_template(), GenericTemplatePropertyKind::Core(property) => property.try_into_template(),
GenericTemplatePropertyKind::Self_ => None, GenericTemplatePropertyKind::Self_(_) => None,
} }
} }
} }
@ -154,15 +163,19 @@ impl<'a, C: 'a> IntoTemplateProperty<'a, C> for GenericTemplatePropertyKind<'a,
/// ///
/// Because the `GenericTemplateLanguage` doesn't provide a way to pass around /// Because the `GenericTemplateLanguage` doesn't provide a way to pass around
/// global resources, the keyword function is allowed to capture resources. /// global resources, the keyword function is allowed to capture resources.
pub type GenericTemplateBuildKeywordFn<'a, C> = pub type GenericTemplateBuildKeywordFn<'a, C> = Box<
Box<dyn Fn() -> TemplateParseResult<GenericTemplatePropertyKind<'a, C>> + 'a>; dyn Fn(
Box<dyn TemplateProperty<C, Output = C> + 'a>,
) -> TemplateParseResult<GenericTemplatePropertyKind<'a, C>>
+ 'a,
>;
/// Table of functions that translate keyword node. /// Table of functions that translate keyword node.
pub type GenericTemplateBuildKeywordFnMap<'a, C> = pub type GenericTemplateBuildKeywordFnMap<'a, C> =
HashMap<&'static str, GenericTemplateBuildKeywordFn<'a, C>>; HashMap<&'static str, GenericTemplateBuildKeywordFn<'a, C>>;
/// Symbol table of methods available in the general-purpose template. /// Symbol table of methods available in the general-purpose template.
struct GenericTemplateBuildFnTable<'a, C: 'a> { struct GenericTemplateBuildFnTable<'a, C: Clone + 'a> {
core: CoreTemplateBuildFnTable<'a, GenericTemplateLanguage<'a, C>>, core: CoreTemplateBuildFnTable<'a, GenericTemplateLanguage<'a, C>>,
keywords: GenericTemplateBuildKeywordFnMap<'a, C>, keywords: GenericTemplateBuildKeywordFnMap<'a, C>,
} }

View file

@ -1206,7 +1206,7 @@ mod tests {
where where
F: Fn() -> TestTemplatePropertyKind + 'static, F: Fn() -> TestTemplatePropertyKind + 'static,
{ {
self.language.add_keyword(name, move || Ok(build())); self.language.add_keyword(name, move |_| Ok(build()));
} }
fn add_alias(&mut self, decl: impl AsRef<str>, defn: impl Into<String>) { fn add_alias(&mut self, decl: impl AsRef<str>, defn: impl Into<String>) {