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::BoxedTemplateProperty;
31use crate::templater::ListTemplate;
32use crate::templater::Template;
33
34pub struct GenericTemplateLanguage<'a, C> {
41 settings: UserSettings,
42 build_fn_table: GenericTemplateBuildFnTable<'a, C>,
43}
44
45impl<'a, C> GenericTemplateLanguage<'a, C> {
46 pub fn new(settings: &UserSettings) -> Self {
50 Self::with_keywords(HashMap::new(), settings)
51 }
52
53 pub fn with_keywords(
55 keywords: GenericTemplateBuildKeywordFnMap<'a, C>,
56 settings: &UserSettings,
57 ) -> Self {
58 GenericTemplateLanguage {
59 settings: settings.clone(),
61 build_fn_table: GenericTemplateBuildFnTable {
62 core: CoreTemplateBuildFnTable::builtin(),
63 keywords,
64 },
65 }
66 }
67
68 pub fn add_keyword<F>(&mut self, name: &'static str, build: F)
81 where
82 F: Fn(
83 BoxedTemplateProperty<'a, C>,
84 ) -> TemplateParseResult<GenericTemplatePropertyKind<'a, C>>
85 + 'a,
86 {
87 self.build_fn_table.keywords.insert(name, Box::new(build));
88 }
89}
90
91impl<'a, C> TemplateLanguage<'a> for GenericTemplateLanguage<'a, C> {
92 type Property = GenericTemplatePropertyKind<'a, C>;
93
94 fn settings(&self) -> &UserSettings {
95 &self.settings
96 }
97
98 fn build_function(
99 &self,
100 diagnostics: &mut TemplateDiagnostics,
101 build_ctx: &BuildContext<Self::Property>,
102 function: &FunctionCallNode,
103 ) -> TemplateParseResult<Self::Property> {
104 let table = &self.build_fn_table.core;
105 table.build_function(self, diagnostics, build_ctx, function)
106 }
107
108 fn build_method(
109 &self,
110 diagnostics: &mut TemplateDiagnostics,
111 build_ctx: &BuildContext<Self::Property>,
112 property: Self::Property,
113 function: &FunctionCallNode,
114 ) -> TemplateParseResult<Self::Property> {
115 let type_name = property.type_name();
116 match property {
117 GenericTemplatePropertyKind::Core(property) => {
118 let table = &self.build_fn_table.core;
119 table.build_method(self, diagnostics, build_ctx, property, function)
120 }
121 GenericTemplatePropertyKind::Self_(property) => {
122 let table = &self.build_fn_table.keywords;
123 let build = template_parser::lookup_method(type_name, table, function)?;
124 function.expect_no_arguments()?;
126 build(property)
127 }
128 }
129 }
130}
131
132pub enum GenericTemplatePropertyKind<'a, C> {
133 Core(CoreTemplatePropertyKind<'a>),
134 Self_(BoxedTemplateProperty<'a, C>),
135}
136
137template_builder::impl_core_property_wrappers!(<'a, C> GenericTemplatePropertyKind<'a, C> => Core);
138
139macro_rules! impl_self_property_wrapper {
145 ($context:path) => {
146 $crate::template_builder::impl_property_wrappers!(
147 $crate::generic_templater::GenericTemplatePropertyKind<'static, $context> {
148 Self_($context),
149 }
150 );
151 };
152 (<$a:lifetime> $context:path) => {
153 $crate::template_builder::impl_property_wrappers!(
154 <$a> $crate::generic_templater::GenericTemplatePropertyKind<$a, $context> {
155 Self_($context),
156 }
157 );
158 };
159}
160
161pub(crate) use impl_self_property_wrapper;
162
163impl<'a, C> CoreTemplatePropertyVar<'a> for GenericTemplatePropertyKind<'a, C> {
164 fn wrap_template(template: Box<dyn Template + 'a>) -> Self {
165 Self::Core(CoreTemplatePropertyKind::wrap_template(template))
166 }
167
168 fn wrap_list_template(template: Box<dyn ListTemplate + 'a>) -> Self {
169 Self::Core(CoreTemplatePropertyKind::wrap_list_template(template))
170 }
171
172 fn type_name(&self) -> &'static str {
173 match self {
174 Self::Core(property) => property.type_name(),
175 Self::Self_(_) => "Self",
176 }
177 }
178
179 fn try_into_boolean(self) -> Option<BoxedTemplateProperty<'a, bool>> {
180 match self {
181 Self::Core(property) => property.try_into_boolean(),
182 Self::Self_(_) => None,
183 }
184 }
185
186 fn try_into_integer(self) -> Option<BoxedTemplateProperty<'a, i64>> {
187 match self {
188 Self::Core(property) => property.try_into_integer(),
189 Self::Self_(_) => None,
190 }
191 }
192
193 fn try_into_plain_text(self) -> Option<BoxedTemplateProperty<'a, String>> {
194 match self {
195 Self::Core(property) => property.try_into_plain_text(),
196 Self::Self_(_) => None,
197 }
198 }
199
200 fn try_into_template(self) -> Option<Box<dyn Template + 'a>> {
201 match self {
202 Self::Core(property) => property.try_into_template(),
203 Self::Self_(_) => None,
204 }
205 }
206
207 fn try_into_eq(self, other: Self) -> Option<BoxedTemplateProperty<'a, bool>> {
208 match (self, other) {
209 (Self::Core(lhs), Self::Core(rhs)) => lhs.try_into_eq(rhs),
210 (Self::Core(_), _) => None,
211 (Self::Self_(_), _) => None,
212 }
213 }
214
215 fn try_into_cmp(self, other: Self) -> Option<BoxedTemplateProperty<'a, Ordering>> {
216 match (self, other) {
217 (Self::Core(lhs), Self::Core(rhs)) => lhs.try_into_cmp(rhs),
218 (Self::Core(_), _) => None,
219 (Self::Self_(_), _) => None,
220 }
221 }
222}
223
224pub type GenericTemplateBuildKeywordFn<'a, C> = Box<
230 dyn Fn(BoxedTemplateProperty<'a, C>) -> TemplateParseResult<GenericTemplatePropertyKind<'a, C>>
231 + 'a,
232>;
233
234pub type GenericTemplateBuildKeywordFnMap<'a, C> =
236 HashMap<&'static str, GenericTemplateBuildKeywordFn<'a, C>>;
237
238struct GenericTemplateBuildFnTable<'a, C> {
240 core: CoreTemplateBuildFnTable<'a, GenericTemplateLanguage<'a, C>>,
241 keywords: GenericTemplateBuildKeywordFnMap<'a, C>,
242}