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}
32
33impl Processor {
34 fn with_bibliography_renderer<T>(&self, render: impl FnOnce(Renderer<'_>) -> T) -> T {
36 let bibliography_shared_config = self.get_bibliography_config();
37 let bibliography_config = self.get_bibliography_options().into_owned();
38 let renderer = Renderer::new(
39 RendererResources {
40 style: &self.style,
41 bibliography: &self.bibliography,
42 locale: &self.locale,
43 config: &bibliography_shared_config,
44 bibliography_config: Some(bibliography_config),
45 first_note_by_id: None,
46 },
47 &self.hints,
48 &self.citation_numbers,
49 CompoundRenderData {
50 set_by_ref: &self.compound_set_by_ref,
51 member_index: &self.compound_member_index,
52 sets: &self.compound_sets,
53 },
54 self.show_semantics,
55 self.inject_ast_indices,
56 self.abbreviation_map.as_ref(),
57 );
58
59 render(renderer)
60 }
61
62 fn process_sorted_refs<'a, I, F>(
69 &self,
70 sorted_refs: I,
71 process_fn: impl Fn(&Reference, usize) -> Option<ProcTemplate>,
72 ) -> Vec<ProcEntry>
73 where
74 I: Iterator<Item = &'a Reference>,
75 F: OutputFormat<Output = String>,
76 {
77 let mut bibliography = Vec::new();
78 let mut previous_reference: Option<&Reference> = None;
79
80 let bibliography_options = self.get_bibliography_options();
81 let substitute = bibliography_options.subsequent_author_substitute.as_ref();
82
83 for (index, reference) in sorted_refs.enumerate() {
84 let ref_id = reference.id().unwrap_or_default().to_string();
85 let entry_number = self
86 .citation_numbers
87 .borrow()
88 .get(&ref_id)
89 .copied()
90 .unwrap_or(index + 1);
91
92 if let Some(mut processed) = process_fn(reference, entry_number) {
93 if let Some(substitute_string) = substitute
94 && let Some(previous) = previous_reference
95 && self.contributors_match(previous, reference)
96 {
97 self.with_bibliography_renderer(|renderer| {
98 renderer.apply_author_substitution_with_format::<F>(
99 &mut processed,
100 substitute_string,
101 );
102 });
103 }
104
105 bibliography.push(ProcEntry {
106 id: ref_id,
107 template: processed,
108 metadata: self.extract_metadata(reference),
109 });
110 previous_reference = Some(reference);
111 }
112 }
113
114 bibliography
115 }
116
117 pub fn process_references(&self) -> ProcessedReferences {
122 self.process_references_with_format::<crate::render::plain::PlainText>()
123 }
124
125 pub fn process_references_with_format<F>(&self) -> ProcessedReferences
129 where
130 F: OutputFormat<Output = String>,
131 {
132 self.initialize_numeric_bibliography_numbers();
133 let sorted_refs = self.sort_references(self.bibliography.values().collect());
134 let bibliography = self.process_sorted_refs::<_, F>(
135 sorted_refs.iter().copied(),
136 |reference, entry_number| {
137 self.process_bibliography_entry_with_format::<F>(reference, entry_number)
138 },
139 );
140 ProcessedReferences {
141 bibliography,
142 citations: None,
143 }
144 }
145
146 pub fn process_bibliography_entry(
148 &self,
149 reference: &Reference,
150 entry_number: usize,
151 ) -> Option<ProcTemplate> {
152 self.with_bibliography_renderer(|renderer| {
153 renderer.process_bibliography_entry(reference, entry_number)
154 })
155 }
156
157 pub fn process_bibliography_entry_with_format<F>(
159 &self,
160 reference: &Reference,
161 entry_number: usize,
162 ) -> Option<ProcTemplate>
163 where
164 F: OutputFormat<Output = String>,
165 {
166 self.with_bibliography_renderer(|renderer| {
167 renderer.process_bibliography_entry_with_format::<F>(reference, entry_number)
168 })
169 }
170
171 pub fn contributors_match(&self, prev: &Reference, current: &Reference) -> bool {
175 let matcher = Matcher::new(&self.style, &self.default_config);
176 matcher.contributors_match(prev, current)
177 }
178
179 pub fn apply_author_substitution(&self, proc: &mut ProcTemplate, substitute: &str) {
183 self.with_bibliography_renderer(|renderer| {
184 renderer.apply_author_substitution(proc, substitute);
185 });
186 }
187
188 pub fn render_bibliography_with_format<F>(&self) -> String
190 where
191 F: OutputFormat<Output = String>,
192 {
193 self.render_bibliography_with_format_and_annotations::<F>(None, None)
194 }
195
196 pub fn render_bibliography_with_format_and_annotations<F>(
198 &self,
199 annotations: Option<&HashMap<String, String>>,
200 annotation_style: Option<&AnnotationStyle>,
201 ) -> String
202 where
203 F: OutputFormat<Output = String>,
204 {
205 self.render_selected_bibliography_with_format_and_annotations::<F, _>(
206 self.bibliography.keys().cloned().collect::<Vec<_>>(),
207 annotations,
208 annotation_style,
209 )
210 }
211
212 pub fn render_selected_bibliography_with_format<F, I>(&self, item_ids: I) -> String
214 where
215 F: OutputFormat<Output = String>,
216 I: IntoIterator<Item = String>,
217 {
218 self.render_selected_bibliography_with_format_and_annotations::<F, _>(item_ids, None, None)
219 }
220
221 pub fn render_selected_bibliography_with_format_and_annotations<F, I>(
228 &self,
229 item_ids: I,
230 annotations: Option<&HashMap<String, String>>,
231 annotation_style: Option<&AnnotationStyle>,
232 ) -> String
233 where
234 F: OutputFormat<Output = String>,
235 I: IntoIterator<Item = String>,
236 {
237 let selected: HashSet<String> = item_ids.into_iter().collect();
238
239 if let Some(groups) = self
241 .style
242 .bibliography
243 .as_ref()
244 .filter(|bibliography| bibliography.groups_enabled)
245 .and_then(|bibliography| bibliography.groups.as_ref())
246 {
247 let all_entries = self.process_references().bibliography;
248 return self.render_with_custom_groups_filtered::<F>(
249 &all_entries,
250 groups,
251 &selected,
252 annotations,
253 annotation_style,
254 );
255 }
256
257 let bibliography_options = self.get_bibliography_options();
259 if let Some(partitioning) = bibliography_options.sort_partitioning.as_ref()
260 && crate::sort_partitioning::should_render_sections(partitioning)
261 {
262 self.initialize_numeric_bibliography_numbers();
263 let all_sorted = self.sort_references(self.bibliography.values().collect());
264 let selected_sorted: Vec<&Reference> = all_sorted
265 .into_iter()
266 .filter(|r| r.id().as_deref().is_some_and(|id| selected.contains(id)))
267 .collect();
268 return self.render_with_partition_sections::<F>(
269 selected_sorted,
270 partitioning,
271 annotations,
272 annotation_style,
273 );
274 }
275
276 self.initialize_numeric_bibliography_numbers();
278 let sorted_refs = self.sort_references(self.bibliography.values().collect());
279
280 let bibliography = self.process_sorted_refs::<_, F>(
281 sorted_refs
282 .iter()
283 .filter(|r| r.id().as_deref().is_some_and(|id| selected.contains(id)))
284 .copied(),
285 |reference, entry_number| {
286 self.process_bibliography_entry_with_format::<F>(reference, entry_number)
287 },
288 );
289
290 let bibliography = self.merge_compound_entries::<F>(bibliography);
291 crate::render::refs_to_string_with_format::<F>(bibliography, annotations, annotation_style)
292 }
293
294 pub fn render_bibliography(&self) -> String {
296 self.render_bibliography_with_format::<crate::render::plain::PlainText>()
297 }
298}