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