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