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::BoxedSerializeProperty;
31use crate::templater::BoxedTemplateProperty;
32use crate::templater::ListTemplate;
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_list_template(template: Box<dyn ListTemplate + 'a>) -> Self {
180 Self::Core(CoreTemplatePropertyKind::wrap_list_template(template))
181 }
182
183 fn type_name(&self) -> &'static str {
184 match self {
185 Self::Core(property) => property.type_name(),
186 Self::Self_(_) => "Self",
187 }
188 }
189
190 fn try_into_boolean(self) -> Option<BoxedTemplateProperty<'a, bool>> {
191 match self {
192 Self::Core(property) => property.try_into_boolean(),
193 Self::Self_(_) => None,
194 }
195 }
196
197 fn try_into_integer(self) -> Option<BoxedTemplateProperty<'a, i64>> {
198 match self {
199 Self::Core(property) => property.try_into_integer(),
200 Self::Self_(_) => None,
201 }
202 }
203
204 fn try_into_stringify(self) -> Option<BoxedTemplateProperty<'a, String>> {
205 match self {
206 Self::Core(property) => property.try_into_stringify(),
207 Self::Self_(_) => None,
208 }
209 }
210
211 fn try_into_serialize(self) -> Option<BoxedSerializeProperty<'a>> {
212 match self {
213 Self::Core(property) => property.try_into_serialize(),
214 Self::Self_(property) => Some(property.into_serialize()),
215 }
216 }
217
218 fn try_into_template(self) -> Option<Box<dyn Template + 'a>> {
219 match self {
220 Self::Core(property) => property.try_into_template(),
221 Self::Self_(_) => None,
222 }
223 }
224
225 fn try_into_eq(self, other: Self) -> Option<BoxedTemplateProperty<'a, bool>> {
226 match (self, other) {
227 (Self::Core(lhs), Self::Core(rhs)) => lhs.try_into_eq(rhs),
228 (Self::Core(_), _) => None,
229 (Self::Self_(_), _) => None,
230 }
231 }
232
233 fn try_into_cmp(self, other: Self) -> Option<BoxedTemplateProperty<'a, Ordering>> {
234 match (self, other) {
235 (Self::Core(lhs), Self::Core(rhs)) => lhs.try_into_cmp(rhs),
236 (Self::Core(_), _) => None,
237 (Self::Self_(_), _) => None,
238 }
239 }
240}
241
242pub type GenericTemplateBuildKeywordFn<'a, C> = Box<
248 dyn Fn(BoxedTemplateProperty<'a, C>) -> TemplateParseResult<GenericTemplatePropertyKind<'a, C>>
249 + 'a,
250>;
251
252pub type GenericTemplateBuildKeywordFnMap<'a, C> =
254 HashMap<&'static str, GenericTemplateBuildKeywordFn<'a, C>>;
255
256struct GenericTemplateBuildFnTable<'a, C> {
258 core: CoreTemplateBuildFnTable<
259 'a,
260 GenericTemplateLanguage<'a, C>,
261 GenericTemplatePropertyKind<'a, C>,
262 >,
263 keywords: GenericTemplateBuildKeywordFnMap<'a, C>,
264}