citum_engine/processor/bibliography/
mod.rs1mod compound;
13mod grouping;
14
15use super::matching::Matcher;
16use super::rendering::{CompoundRenderData, Renderer, RendererResources};
17use super::{ProcessedReferences, Processor};
18use crate::api::AnnotationStyle;
19use crate::reference::Reference;
20use crate::render::format::OutputFormat;
21use crate::render::{ProcEntry, ProcTemplate};
22use std::collections::{HashMap, HashSet};
23use std::rc::Rc;
24
25#[derive(Debug, Clone, Default)]
27pub(crate) struct RenderedBibliographyGroup {
28 pub(crate) heading: Option<String>,
30 pub(crate) body: String,
32 pub(crate) entries: Vec<crate::render::ProcEntry>,
34}
35
36#[derive(Debug, Clone, Default)]
43pub(crate) struct DocumentBibliography {
44 pub(crate) content: String,
46 pub(crate) entries: Vec<crate::render::ProcEntry>,
48}
49
50impl Processor {
51 fn with_bibliography_renderer<T>(&self, render: impl FnOnce(Renderer<'_>) -> T) -> T {
53 let bibliography_shared_config = self.get_bibliography_config();
54 let bibliography_config = self.get_bibliography_options().into_owned();
55 let renderer = Renderer::new(
56 RendererResources {
57 style: &self.style,
58 bibliography: &self.bibliography,
59 locale: &self.locale,
60 config: Rc::new(bibliography_shared_config.into_owned()),
61 bibliography_config: Some(Rc::new(bibliography_config)),
62 first_note_by_id: None,
63 },
64 &self.hints,
65 &self.citation_numbers,
66 CompoundRenderData {
67 set_by_ref: &self.compound_set_by_ref,
68 member_index: &self.compound_member_index,
69 sets: &self.compound_sets,
70 },
71 self.show_semantics,
72 self.inject_ast_indices,
73 self.abbreviation_map.as_ref(),
74 );
75
76 render(renderer)
77 }
78
79 fn process_sorted_refs<'a, I, F>(
86 &self,
87 sorted_refs: I,
88 process_fn: impl Fn(&Reference, usize) -> Option<ProcTemplate>,
89 ) -> Vec<ProcEntry>
90 where
91 I: Iterator<Item = &'a Reference>,
92 F: OutputFormat<Output = String>,
93 {
94 let mut bibliography = Vec::new();
95 let mut previous_reference: Option<&Reference> = None;
96
97 let bibliography_options = self.get_bibliography_options();
98 let substitute = bibliography_options.subsequent_author_substitute.as_ref();
99
100 for (index, reference) in sorted_refs.enumerate() {
101 let ref_id = reference.id().unwrap_or_default().to_string();
102 let entry_number = self
103 .citation_numbers
104 .borrow()
105 .get(&ref_id)
106 .copied()
107 .unwrap_or(index + 1);
108
109 if let Some(mut processed) = process_fn(reference, entry_number) {
110 if let Some(substitute_string) = substitute
111 && let Some(previous) = previous_reference
112 && self.contributors_match(previous, reference)
113 {
114 self.with_bibliography_renderer(|renderer| {
115 renderer.apply_author_substitution_with_format::<F>(
116 &mut processed,
117 substitute_string,
118 );
119 });
120 }
121
122 bibliography.push(ProcEntry {
123 id: ref_id,
124 template: processed,
125 metadata: self.extract_metadata(reference),
126 });
127 previous_reference = Some(reference);
128 }
129 }
130
131 bibliography
132 }
133
134 pub fn process_references(&self) -> ProcessedReferences {
139 self.process_references_with_format::<crate::render::plain::PlainText>()
140 }
141
142 pub fn process_references_with_format<F>(&self) -> ProcessedReferences
146 where
147 F: OutputFormat<Output = String>,
148 {
149 self.initialize_numeric_bibliography_numbers();
150 let sorted_refs = self.sort_references(self.bibliography.values().collect());
151 let bibliography = self.process_sorted_refs::<_, F>(
152 sorted_refs.iter().copied(),
153 |reference, entry_number| {
154 self.process_bibliography_entry_with_format::<F>(reference, entry_number)
155 },
156 );
157 ProcessedReferences {
158 bibliography,
159 citations: None,
160 }
161 }
162
163 pub(crate) fn process_selected_references_with_format<F, I>(
172 &self,
173 item_ids: I,
174 ) -> ProcessedReferences
175 where
176 F: OutputFormat<Output = String>,
177 I: IntoIterator<Item = String>,
178 {
179 self.initialize_numeric_bibliography_numbers();
180 let selected: HashSet<String> = item_ids.into_iter().collect();
181 let sorted_refs = self.sort_references(self.bibliography.values().collect());
182 let bibliography = self.process_sorted_refs::<_, F>(
183 sorted_refs
184 .iter()
185 .filter(|r| r.id().as_deref().is_some_and(|id| selected.contains(id)))
186 .copied(),
187 |reference, entry_number| {
188 self.process_bibliography_entry_with_format::<F>(reference, entry_number)
189 },
190 );
191 ProcessedReferences {
192 bibliography,
193 citations: None,
194 }
195 }
196
197 pub fn process_bibliography_entry(
199 &self,
200 reference: &Reference,
201 entry_number: usize,
202 ) -> Option<ProcTemplate> {
203 self.with_bibliography_renderer(|renderer| {
204 renderer.process_bibliography_entry(reference, entry_number)
205 })
206 }
207
208 pub fn process_bibliography_entry_with_format<F>(
210 &self,
211 reference: &Reference,
212 entry_number: usize,
213 ) -> Option<ProcTemplate>
214 where
215 F: OutputFormat<Output = String>,
216 {
217 self.with_bibliography_renderer(|renderer| {
218 renderer.process_bibliography_entry_with_format::<F>(reference, entry_number)
219 })
220 }
221
222 pub fn contributors_match(&self, prev: &Reference, current: &Reference) -> bool {
226 let matcher = Matcher::new(&self.style, &self.default_config);
227 matcher.contributors_match(prev, current)
228 }
229
230 pub fn apply_author_substitution(&self, proc: &mut ProcTemplate, substitute: &str) {
234 self.with_bibliography_renderer(|renderer| {
235 renderer.apply_author_substitution(proc, substitute);
236 });
237 }
238
239 pub fn render_bibliography_with_format<F>(&self) -> String
241 where
242 F: OutputFormat<Output = String>,
243 {
244 self.render_bibliography_with_format_and_annotations::<F>(None, None)
245 }
246
247 pub fn render_bibliography_with_format_and_annotations<F>(
249 &self,
250 annotations: Option<&HashMap<String, String>>,
251 annotation_style: Option<&AnnotationStyle>,
252 ) -> String
253 where
254 F: OutputFormat<Output = String>,
255 {
256 self.render_selected_bibliography_with_format_and_annotations::<F, _>(
257 self.bibliography.keys().cloned().collect::<Vec<_>>(),
258 annotations,
259 annotation_style,
260 )
261 }
262
263 pub fn render_selected_bibliography_with_format<F, I>(&self, item_ids: I) -> String
265 where
266 F: OutputFormat<Output = String>,
267 I: IntoIterator<Item = String>,
268 {
269 self.render_selected_bibliography_with_format_and_annotations::<F, _>(item_ids, None, None)
270 }
271
272 pub fn render_selected_bibliography_with_format_and_annotations<F, I>(
279 &self,
280 item_ids: I,
281 annotations: Option<&HashMap<String, String>>,
282 annotation_style: Option<&AnnotationStyle>,
283 ) -> String
284 where
285 F: OutputFormat<Output = String>,
286 I: IntoIterator<Item = String>,
287 {
288 let selected: HashSet<String> = item_ids.into_iter().collect();
289
290 if let Some(groups) = self
292 .style
293 .bibliography
294 .as_ref()
295 .filter(|bibliography| bibliography.groups_enabled)
296 .and_then(|bibliography| bibliography.groups.as_ref())
297 {
298 let all_entries = self.sorted_id_stubs();
299 return self.render_with_custom_groups_filtered::<F>(
300 &all_entries,
301 groups,
302 &selected,
303 annotations,
304 annotation_style,
305 );
306 }
307
308 let bibliography_options = self.get_bibliography_options();
310 if let Some(partitioning) = bibliography_options.sort_partitioning.as_ref()
311 && crate::sort_partitioning::should_render_sections(partitioning)
312 {
313 self.initialize_numeric_bibliography_numbers();
314 let all_sorted = self.sort_references(self.bibliography.values().collect());
315 let selected_sorted: Vec<&Reference> = all_sorted
316 .into_iter()
317 .filter(|r| r.id().as_deref().is_some_and(|id| selected.contains(id)))
318 .collect();
319 return self.render_with_partition_sections::<F>(
320 selected_sorted,
321 partitioning,
322 annotations,
323 annotation_style,
324 );
325 }
326
327 self.initialize_numeric_bibliography_numbers();
329 let sorted_refs = self.sort_references(self.bibliography.values().collect());
330
331 let bibliography = self.process_sorted_refs::<_, F>(
332 sorted_refs
333 .iter()
334 .filter(|r| r.id().as_deref().is_some_and(|id| selected.contains(id)))
335 .copied(),
336 |reference, entry_number| {
337 self.process_bibliography_entry_with_format::<F>(reference, entry_number)
338 },
339 );
340
341 let bibliography = self.merge_compound_entries::<F>(bibliography);
342 crate::render::refs_to_string_with_format::<F>(bibliography, annotations, annotation_style)
343 }
344
345 pub fn render_bibliography(&self) -> String {
347 self.render_bibliography_with_format::<crate::render::plain::PlainText>()
348 }
349}