use std::cmp::Ordering;
use std::collections::HashMap;
use bstr::BString;
use jj_lib::backend::Timestamp;
use jj_lib::settings::UserSettings;
use crate::template_builder;
use crate::template_builder::BuildContext;
use crate::template_builder::CoreTemplateBuildFnTable;
use crate::template_builder::CoreTemplatePropertyKind;
use crate::template_builder::CoreTemplatePropertyVar;
use crate::template_builder::TemplateLanguage;
use crate::template_parser;
use crate::template_parser::FunctionCallNode;
use crate::template_parser::TemplateDiagnostics;
use crate::template_parser::TemplateParseResult;
use crate::templater::BoxedAnyProperty;
use crate::templater::BoxedSerializeProperty;
use crate::templater::BoxedTemplateProperty;
use crate::templater::Template;
use crate::templater::TemplatePropertyExt as _;
pub struct GenericTemplateLanguage<'a, C> {
settings: UserSettings,
build_fn_table: GenericTemplateBuildFnTable<'a, C>,
}
impl<'a, C> GenericTemplateLanguage<'a, C>
where
C: serde::Serialize + 'a,
{
pub fn new(settings: &UserSettings) -> Self {
Self::with_keywords(HashMap::new(), settings)
}
pub fn with_keywords(
keywords: GenericTemplateBuildKeywordFnMap<'a, C>,
settings: &UserSettings,
) -> Self {
Self {
settings: settings.clone(),
build_fn_table: GenericTemplateBuildFnTable {
core: CoreTemplateBuildFnTable::builtin(),
keywords,
},
}
}
pub fn add_keyword<F>(&mut self, name: &'static str, build: F)
where
F: Fn(
BoxedTemplateProperty<'a, C>,
) -> TemplateParseResult<GenericTemplatePropertyKind<'a, C>>
+ 'a,
{
self.build_fn_table.keywords.insert(name, Box::new(build));
}
}
impl<'a, C> TemplateLanguage<'a> for GenericTemplateLanguage<'a, C>
where
C: serde::Serialize + 'a,
{
type Property = GenericTemplatePropertyKind<'a, C>;
fn settings(&self) -> &UserSettings {
&self.settings
}
fn build_function(
&self,
diagnostics: &mut TemplateDiagnostics,
build_ctx: &BuildContext<Self::Property>,
function: &FunctionCallNode,
) -> TemplateParseResult<Self::Property> {
let table = &self.build_fn_table.core;
table.build_function(self, diagnostics, build_ctx, function)
}
fn build_method(
&self,
diagnostics: &mut TemplateDiagnostics,
build_ctx: &BuildContext<Self::Property>,
property: Self::Property,
function: &FunctionCallNode,
) -> TemplateParseResult<Self::Property> {
let type_name = property.type_name();
match property {
GenericTemplatePropertyKind::Core(property) => {
let table = &self.build_fn_table.core;
table.build_method(self, diagnostics, build_ctx, property, function)
}
GenericTemplatePropertyKind::Self_(property) => {
let table = &self.build_fn_table.keywords;
let build = template_parser::lookup_method(type_name, table, function)?;
function.expect_no_arguments()?;
build(property)
}
}
}
}
pub enum GenericTemplatePropertyKind<'a, C> {
Core(CoreTemplatePropertyKind<'a>),
Self_(BoxedTemplateProperty<'a, C>),
}
template_builder::impl_core_property_wrappers!(<'a, C> GenericTemplatePropertyKind<'a, C> => Core);
macro_rules! impl_self_property_wrapper {
($context:path) => {
$crate::template_builder::impl_property_wrappers!(
$crate::generic_templater::GenericTemplatePropertyKind<'static, $context> {
Self_($context),
}
);
};
(<$a:lifetime> $context:path) => {
$crate::template_builder::impl_property_wrappers!(
<$a> $crate::generic_templater::GenericTemplatePropertyKind<$a, $context> {
Self_($context),
}
);
};
}
pub(crate) use impl_self_property_wrapper;
impl<'a, C> CoreTemplatePropertyVar<'a> for GenericTemplatePropertyKind<'a, C>
where
C: serde::Serialize + 'a,
{
fn wrap_template(template: Box<dyn Template + 'a>) -> Self {
Self::Core(CoreTemplatePropertyKind::wrap_template(template))
}
fn wrap_any(property: BoxedAnyProperty<'a>) -> Self {
Self::Core(CoreTemplatePropertyKind::wrap_any(property))
}
fn wrap_any_list(property: BoxedAnyProperty<'a>) -> Self {
Self::Core(CoreTemplatePropertyKind::wrap_any_list(property))
}
fn type_name(&self) -> &'static str {
match self {
Self::Core(property) => property.type_name(),
Self::Self_(_) => "Self",
}
}
fn try_into_byte_string(self) -> Result<BoxedTemplateProperty<'a, BString>, Self> {
match self {
Self::Core(property) => property.try_into_byte_string().map_err(Self::Core),
Self::Self_(_) => Err(self),
}
}
fn try_into_string(self) -> Result<BoxedTemplateProperty<'a, String>, Self> {
match self {
Self::Core(property) => property.try_into_string().map_err(Self::Core),
Self::Self_(_) => Err(self),
}
}
fn try_into_boolean(self) -> Result<BoxedTemplateProperty<'a, bool>, Self> {
match self {
Self::Core(property) => property.try_into_boolean().map_err(Self::Core),
Self::Self_(_) => Err(self),
}
}
fn try_into_integer(self) -> Result<BoxedTemplateProperty<'a, i64>, Self> {
match self {
Self::Core(property) => property.try_into_integer().map_err(Self::Core),
Self::Self_(_) => Err(self),
}
}
fn try_into_timestamp(self) -> Result<BoxedTemplateProperty<'a, Timestamp>, Self> {
match self {
Self::Core(property) => property.try_into_timestamp().map_err(Self::Core),
Self::Self_(_) => Err(self),
}
}
fn try_into_serialize(self) -> Option<BoxedSerializeProperty<'a>> {
match self {
Self::Core(property) => property.try_into_serialize(),
Self::Self_(property) => Some(property.into_serialize()),
}
}
fn try_into_template(self) -> Option<Box<dyn Template + 'a>> {
match self {
Self::Core(property) => property.try_into_template(),
Self::Self_(_) => None,
}
}
fn try_into_eq(self, other: Self) -> Option<BoxedTemplateProperty<'a, bool>> {
match (self, other) {
(Self::Core(lhs), Self::Core(rhs)) => lhs.try_into_eq(rhs),
(Self::Core(_), _) => None,
(Self::Self_(_), _) => None,
}
}
fn try_into_cmp(self, other: Self) -> Option<BoxedTemplateProperty<'a, Ordering>> {
match (self, other) {
(Self::Core(lhs), Self::Core(rhs)) => lhs.try_into_cmp(rhs),
(Self::Core(_), _) => None,
(Self::Self_(_), _) => None,
}
}
}
pub type GenericTemplateBuildKeywordFn<'a, C> = Box<
dyn Fn(BoxedTemplateProperty<'a, C>) -> TemplateParseResult<GenericTemplatePropertyKind<'a, C>>
+ 'a,
>;
pub type GenericTemplateBuildKeywordFnMap<'a, C> =
HashMap<&'static str, GenericTemplateBuildKeywordFn<'a, C>>;
struct GenericTemplateBuildFnTable<'a, C> {
core: CoreTemplateBuildFnTable<
'a,
GenericTemplateLanguage<'a, C>,
GenericTemplatePropertyKind<'a, C>,
>,
keywords: GenericTemplateBuildKeywordFnMap<'a, C>,
}