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 },
46 &self.hints,
47 &self.citation_numbers,
48 CompoundRenderData {
49 set_by_ref: &self.compound_set_by_ref,
50 member_index: &self.compound_member_index,
51 sets: &self.compound_sets,
52 },
53 self.show_semantics,
54 self.inject_ast_indices,
55 self.abbreviation_map.as_ref(),
56 );
57
58 render(renderer)
59 }
60
61 fn process_sorted_refs<'a, I, F>(
65 &self,
66 sorted_refs: I,
67 process_fn: impl Fn(&Reference, usize) -> Option<ProcTemplate>,
68 ) -> Vec<ProcEntry>
69 where
70 I: Iterator<Item = &'a Reference>,
71 F: OutputFormat<Output = String>,
72 {
73 let mut bibliography = Vec::new();
74 let mut previous_reference: Option<&Reference> = None;
75
76 let bibliography_options = self.get_bibliography_options();
77 let substitute = bibliography_options.subsequent_author_substitute.as_ref();
78
79 for (index, reference) in sorted_refs.enumerate() {
80 let ref_id = reference.id().unwrap_or_default().to_string();
81 let entry_number = self
82 .citation_numbers
83 .borrow()
84 .get(&ref_id)
85 .copied()
86 .unwrap_or(index + 1);
87
88 if let Some(mut processed) = process_fn(reference, entry_number) {
89 if let Some(substitute_string) = substitute
90 && let Some(previous) = previous_reference
91 && self.contributors_match(previous, reference)
92 {
93 self.with_bibliography_renderer(|renderer| {
94 renderer.apply_author_substitution_with_format::<F>(
95 &mut processed,
96 substitute_string,
97 );
98 });
99 }
100
101 bibliography.push(ProcEntry {
102 id: ref_id,
103 template: processed,
104 metadata: self.extract_metadata(reference),
105 });
106 previous_reference = Some(reference);
107 }
108 }
109
110 bibliography
111 }
112
113 pub fn process_references(&self) -> ProcessedReferences {
118 self.initialize_numeric_bibliography_numbers();
119 let sorted_refs = self.sort_references(self.bibliography.values().collect());
120 let bibliography = self.process_sorted_refs::<_, crate::render::plain::PlainText>(
121 sorted_refs.iter().copied(),
122 |reference, entry_number| self.process_bibliography_entry(reference, entry_number),
123 );
124
125 ProcessedReferences {
126 bibliography,
127 citations: None,
128 }
129 }
130
131 pub fn process_bibliography_entry(
133 &self,
134 reference: &Reference,
135 entry_number: usize,
136 ) -> Option<ProcTemplate> {
137 self.with_bibliography_renderer(|renderer| {
138 renderer.process_bibliography_entry(reference, entry_number)
139 })
140 }
141
142 pub fn process_bibliography_entry_with_format<F>(
144 &self,
145 reference: &Reference,
146 entry_number: usize,
147 ) -> Option<ProcTemplate>
148 where
149 F: OutputFormat<Output = String>,
150 {
151 self.with_bibliography_renderer(|renderer| {
152 renderer.process_bibliography_entry_with_format::<F>(reference, entry_number)
153 })
154 }
155
156 pub fn contributors_match(&self, prev: &Reference, current: &Reference) -> bool {
160 let matcher = Matcher::new(&self.style, &self.default_config);
161 matcher.contributors_match(prev, current)
162 }
163
164 pub fn apply_author_substitution(&self, proc: &mut ProcTemplate, substitute: &str) {
168 self.with_bibliography_renderer(|renderer| {
169 renderer.apply_author_substitution(proc, substitute);
170 });
171 }
172
173 pub fn render_bibliography_with_format<F>(&self) -> String
175 where
176 F: OutputFormat<Output = String>,
177 {
178 self.render_bibliography_with_format_and_annotations::<F>(None, None)
179 }
180
181 pub fn render_bibliography_with_format_and_annotations<F>(
183 &self,
184 annotations: Option<&HashMap<String, String>>,
185 annotation_style: Option<&AnnotationStyle>,
186 ) -> String
187 where
188 F: OutputFormat<Output = String>,
189 {
190 self.render_selected_bibliography_with_format_and_annotations::<F, _>(
191 self.bibliography.keys().cloned().collect::<Vec<_>>(),
192 annotations,
193 annotation_style,
194 )
195 }
196
197 pub fn render_selected_bibliography_with_format<F, I>(&self, item_ids: I) -> String
199 where
200 F: OutputFormat<Output = String>,
201 I: IntoIterator<Item = String>,
202 {
203 self.render_selected_bibliography_with_format_and_annotations::<F, _>(item_ids, None, None)
204 }
205
206 pub fn render_selected_bibliography_with_format_and_annotations<F, I>(
208 &self,
209 item_ids: I,
210 annotations: Option<&HashMap<String, String>>,
211 annotation_style: Option<&AnnotationStyle>,
212 ) -> String
213 where
214 F: OutputFormat<Output = String>,
215 I: IntoIterator<Item = String>,
216 {
217 let selected: HashSet<String> = item_ids.into_iter().collect();
218
219 if let Some(groups) = self
221 .style
222 .bibliography
223 .as_ref()
224 .filter(|bibliography| bibliography.groups_enabled)
225 .and_then(|bibliography| bibliography.groups.as_ref())
226 {
227 let all_entries = self.process_references().bibliography;
228 return self.render_with_custom_groups_filtered::<F>(
229 &all_entries,
230 groups,
231 &selected,
232 annotations,
233 annotation_style,
234 );
235 }
236
237 let bibliography_options = self.get_bibliography_options();
239 if let Some(partitioning) = bibliography_options.sort_partitioning.as_ref()
240 && crate::sort_partitioning::should_render_sections(partitioning)
241 {
242 self.initialize_numeric_bibliography_numbers();
243 let all_sorted = self.sort_references(self.bibliography.values().collect());
244 let selected_sorted: Vec<&Reference> = all_sorted
245 .into_iter()
246 .filter(|r| r.id().as_deref().is_some_and(|id| selected.contains(id)))
247 .collect();
248 return self.render_with_partition_sections::<F>(
249 selected_sorted,
250 partitioning,
251 annotations,
252 annotation_style,
253 );
254 }
255
256 self.initialize_numeric_bibliography_numbers();
258 let sorted_refs = self.sort_references(self.bibliography.values().collect());
259
260 let bibliography = self.process_sorted_refs::<_, F>(
261 sorted_refs
262 .iter()
263 .filter(|r| r.id().as_deref().is_some_and(|id| selected.contains(id)))
264 .copied(),
265 |reference, entry_number| {
266 self.process_bibliography_entry_with_format::<F>(reference, entry_number)
267 },
268 );
269
270 let bibliography = self.merge_compound_entries::<F>(bibliography);
271 crate::render::refs_to_string_with_format::<F>(bibliography, annotations, annotation_style)
272 }
273
274 pub fn render_bibliography(&self) -> String {
276 self.render_bibliography_with_format::<crate::render::plain::PlainText>()
277 }
278}