1use std::cmp::Ordering;
16use std::collections::HashMap;
17
18use jj_lib::settings::UserSettings;
19
20use crate::template_builder;
21use crate::template_builder::BuildContext;
22use crate::template_builder::CoreTemplateBuildFnTable;
23use crate::template_builder::CoreTemplatePropertyKind;
24use crate::template_builder::CoreTemplatePropertyVar;
25use crate::template_builder::TemplateLanguage;
26use crate::template_parser;
27use crate::template_parser::FunctionCallNode;
28use crate::template_parser::TemplateDiagnostics;
29use crate::template_parser::TemplateParseResult;
30use crate::templater::BoxedAnyProperty;
31use crate::templater::BoxedSerializeProperty;
32use crate::templater::BoxedTemplateProperty;
33use crate::templater::Template;
34use crate::templater::TemplatePropertyExt as _;
35
36pub struct GenericTemplateLanguage<'a, C> {
43 settings: UserSettings,
44 build_fn_table: GenericTemplateBuildFnTable<'a, C>,
45}
46
47impl<'a, C> GenericTemplateLanguage<'a, C>
48where
49 C: serde::Serialize + 'a,
50{
51 pub fn new(settings: &UserSettings) -> Self {
55 Self::with_keywords(HashMap::new(), settings)
56 }
57
58 pub fn with_keywords(
60 keywords: GenericTemplateBuildKeywordFnMap<'a, C>,
61 settings: &UserSettings,
62 ) -> Self {
63 Self {
64 settings: settings.clone(),
66 build_fn_table: GenericTemplateBuildFnTable {
67 core: CoreTemplateBuildFnTable::builtin(),
68 keywords,
69 },
70 }
71 }
72
73 pub fn add_keyword<F>(&mut self, name: &'static str, build: F)
86 where
87 F: Fn(
88 BoxedTemplateProperty<'a, C>,
89 ) -> TemplateParseResult<GenericTemplatePropertyKind<'a, C>>
90 + 'a,
91 {
92 self.build_fn_table.keywords.insert(name, Box::new(build));
93 }
94}
95
96impl<'a, C> TemplateLanguage<'a> for GenericTemplateLanguage<'a, C>
97where
98 C: serde::Serialize + 'a,
99{
100 type Property = GenericTemplatePropertyKind<'a, C>;
101
102 fn settings(&self) -> &UserSettings {
103 &self.settings
104 }
105
106 fn build_function(
107 &self,
108 diagnostics: &mut TemplateDiagnostics,
109 build_ctx: &BuildContext<Self::Property>,
110 function: &FunctionCallNode,
111 ) -> TemplateParseResult<Self::Property> {
112 let table = &self.build_fn_table.core;
113 table.build_function(self, diagnostics, build_ctx, function)
114 }
115
116 fn build_method(
117 &self,
118 diagnostics: &mut TemplateDiagnostics,
119 build_ctx: &BuildContext<Self::Property>,
120 property: Self::Property,
121 function: &FunctionCallNode,
122 ) -> TemplateParseResult<Self::Property> {
123 let type_name = property.type_name();
124 match property {
125 GenericTemplatePropertyKind::Core(property) => {
126 let table = &self.build_fn_table.core;
127 table.build_method(self, diagnostics, build_ctx, property, function)
128 }
129 GenericTemplatePropertyKind::Self_(property) => {
130 let table = &self.build_fn_table.keywords;
131 let build = template_parser::lookup_method(type_name, table, function)?;
132 function.expect_no_arguments()?;
134 build(property)
135 }
136 }
137 }
138}
139
140pub enum GenericTemplatePropertyKind<'a, C> {
141 Core(CoreTemplatePropertyKind<'a>),
142 Self_(BoxedTemplateProperty<'a, C>),
143}
144
145template_builder::impl_core_property_wrappers!(<'a, C> GenericTemplatePropertyKind<'a, C> => Core);
146
147macro_rules! impl_self_property_wrapper {
153 ($context:path) => {
154 $crate::template_builder::impl_property_wrappers!(
155 $crate::generic_templater::GenericTemplatePropertyKind<'static, $context> {
156 Self_($context),
157 }
158 );
159 };
160 (<$a:lifetime> $context:path) => {
161 $crate::template_builder::impl_property_wrappers!(
162 <$a> $crate::generic_templater::GenericTemplatePropertyKind<$a, $context> {
163 Self_($context),
164 }
165 );
166 };
167}
168
169pub(crate) use impl_self_property_wrapper;
170
171impl<'a, C> CoreTemplatePropertyVar<'a> for GenericTemplatePropertyKind<'a, C>
172where
173 C: serde::Serialize + 'a,
174{
175 fn wrap_template(template: Box<dyn Template + 'a>) -> Self {
176 Self::Core(CoreTemplatePropertyKind::wrap_template(template))
177 }
178
179 fn wrap_any(property: BoxedAnyProperty<'a>) -> Self {
180 Self::Core(CoreTemplatePropertyKind::wrap_any(property))
181 }
182
183 fn wrap_any_list(property: BoxedAnyProperty<'a>) -> Self {
184 Self::Core(CoreTemplatePropertyKind::wrap_any_list(property))
185 }
186
187 fn type_name(&self) -> &'static str {
188 match self {
189 Self::Core(property) => property.type_name(),
190 Self::Self_(_) => "Self",
191 }
192 }
193
194 fn try_into_boolean(self) -> Option<BoxedTemplateProperty<'a, bool>> {
195 match self {
196 Self::Core(property) => property.try_into_boolean(),
197 Self::Self_(_) => None,
198 }
199 }
200
201 fn try_into_integer(self) -> Option<BoxedTemplateProperty<'a, i64>> {
202 match self {
203 Self::Core(property) => property.try_into_integer(),
204 Self::Self_(_) => None,
205 }
206 }
207
208 fn try_into_timestamp(self) -> Option<BoxedTemplateProperty<'a, jj_lib::backend::Timestamp>> {
209 match self {
210 Self::Core(property) => property.try_into_timestamp(),
211 Self::Self_(_) => None,
212 }
213 }
214
215 fn try_into_stringify(self) -> Option<BoxedTemplateProperty<'a, String>> {
216 match self {
217 Self::Core(property) => property.try_into_stringify(),
218 Self::Self_(_) => None,
219 }
220 }
221
222 fn try_into_serialize(self) -> Option<BoxedSerializeProperty<'a>> {
223 match self {
224 Self::Core(property) => property.try_into_serialize(),
225 Self::Self_(property) => Some(property.into_serialize()),
226 }
227 }
228
229 fn try_into_template(self) -> Option<Box<dyn Template + 'a>> {
230 match self {
231 Self::Core(property) => property.try_into_template(),
232 Self::Self_(_) => None,
233 }
234 }
235
236 fn try_into_eq(self, other: Self) -> Option<BoxedTemplateProperty<'a, bool>> {
237 match (self, other) {
238 (Self::Core(lhs), Self::Core(rhs)) => lhs.try_into_eq(rhs),
239 (Self::Core(_), _) => None,
240 (Self::Self_(_), _) => None,
241 }
242 }
243
244 fn try_into_cmp(self, other: Self) -> Option<BoxedTemplateProperty<'a, Ordering>> {
245 match (self, other) {
246 (Self::Core(lhs), Self::Core(rhs)) => lhs.try_into_cmp(rhs),
247 (Self::Core(_), _) => None,
248 (Self::Self_(_), _) => None,
249 }
250 }
251}
252
253pub type GenericTemplateBuildKeywordFn<'a, C> = Box<
259 dyn Fn(BoxedTemplateProperty<'a, C>) -> TemplateParseResult<GenericTemplatePropertyKind<'a, C>>
260 + 'a,
261>;
262
263pub type GenericTemplateBuildKeywordFnMap<'a, C> =
265 HashMap<&'static str, GenericTemplateBuildKeywordFn<'a, C>>;
266
267struct GenericTemplateBuildFnTable<'a, C> {
269 core: CoreTemplateBuildFnTable<
270 'a,
271 GenericTemplateLanguage<'a, C>,
272 GenericTemplatePropertyKind<'a, C>,
273 >,
274 keywords: GenericTemplateBuildKeywordFnMap<'a, C>,
275}