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>(
68 &self,
69 sorted_refs: I,
70 process_fn: impl Fn(&Reference, usize) -> Option<ProcTemplate>,
71 ) -> Vec<ProcEntry>
72 where
73 I: Iterator<Item = &'a Reference>,
74 F: OutputFormat<Output = String>,
75 {
76 let mut bibliography = Vec::new();
77 let mut previous_reference: Option<&Reference> = None;
78
79 let bibliography_options = self.get_bibliography_options();
80 let substitute = bibliography_options.subsequent_author_substitute.as_ref();
81
82 for (index, reference) in sorted_refs.enumerate() {
83 let ref_id = reference.id().unwrap_or_default().to_string();
84 let entry_number = self
85 .citation_numbers
86 .borrow()
87 .get(&ref_id)
88 .copied()
89 .unwrap_or(index + 1);
90
91 if let Some(mut processed) = process_fn(reference, entry_number) {
92 if let Some(substitute_string) = substitute
93 && let Some(previous) = previous_reference
94 && self.contributors_match(previous, reference)
95 {
96 self.with_bibliography_renderer(|renderer| {
97 renderer.apply_author_substitution_with_format::<F>(
98 &mut processed,
99 substitute_string,
100 );
101 });
102 }
103
104 bibliography.push(ProcEntry {
105 id: ref_id,
106 template: processed,
107 metadata: self.extract_metadata(reference),
108 });
109 previous_reference = Some(reference);
110 }
111 }
112
113 bibliography
114 }
115
116 pub fn process_references(&self) -> ProcessedReferences {
121 self.initialize_numeric_bibliography_numbers();
122 let sorted_refs = self.sort_references(self.bibliography.values().collect());
123 let bibliography = self.process_sorted_refs::<_, crate::render::plain::PlainText>(
124 sorted_refs.iter().copied(),
125 |reference, entry_number| self.process_bibliography_entry(reference, entry_number),
126 );
127
128 ProcessedReferences {
129 bibliography,
130 citations: None,
131 }
132 }
133
134 pub fn process_bibliography_entry(
136 &self,
137 reference: &Reference,
138 entry_number: usize,
139 ) -> Option<ProcTemplate> {
140 self.with_bibliography_renderer(|renderer| {
141 renderer.process_bibliography_entry(reference, entry_number)
142 })
143 }
144
145 pub fn process_bibliography_entry_with_format<F>(
147 &self,
148 reference: &Reference,
149 entry_number: usize,
150 ) -> Option<ProcTemplate>
151 where
152 F: OutputFormat<Output = String>,
153 {
154 self.with_bibliography_renderer(|renderer| {
155 renderer.process_bibliography_entry_with_format::<F>(reference, entry_number)
156 })
157 }
158
159 pub fn contributors_match(&self, prev: &Reference, current: &Reference) -> bool {
163 let matcher = Matcher::new(&self.style, &self.default_config);
164 matcher.contributors_match(prev, current)
165 }
166
167 pub fn apply_author_substitution(&self, proc: &mut ProcTemplate, substitute: &str) {
171 self.with_bibliography_renderer(|renderer| {
172 renderer.apply_author_substitution(proc, substitute);
173 });
174 }
175
176 pub fn render_bibliography_with_format<F>(&self) -> String
178 where
179 F: OutputFormat<Output = String>,
180 {
181 self.render_bibliography_with_format_and_annotations::<F>(None, None)
182 }
183
184 pub fn render_bibliography_with_format_and_annotations<F>(
186 &self,
187 annotations: Option<&HashMap<String, String>>,
188 annotation_style: Option<&AnnotationStyle>,
189 ) -> String
190 where
191 F: OutputFormat<Output = String>,
192 {
193 self.render_selected_bibliography_with_format_and_annotations::<F, _>(
194 self.bibliography.keys().cloned().collect::<Vec<_>>(),
195 annotations,
196 annotation_style,
197 )
198 }
199
200 pub fn render_selected_bibliography_with_format<F, I>(&self, item_ids: I) -> String
202 where
203 F: OutputFormat<Output = String>,
204 I: IntoIterator<Item = String>,
205 {
206 self.render_selected_bibliography_with_format_and_annotations::<F, _>(item_ids, None, None)
207 }
208
209 pub fn render_selected_bibliography_with_format_and_annotations<F, I>(
216 &self,
217 item_ids: I,
218 annotations: Option<&HashMap<String, String>>,
219 annotation_style: Option<&AnnotationStyle>,
220 ) -> String
221 where
222 F: OutputFormat<Output = String>,
223 I: IntoIterator<Item = String>,
224 {
225 let selected: HashSet<String> = item_ids.into_iter().collect();
226
227 if let Some(groups) = self
229 .style
230 .bibliography
231 .as_ref()
232 .filter(|bibliography| bibliography.groups_enabled)
233 .and_then(|bibliography| bibliography.groups.as_ref())
234 {
235 let all_entries = self.process_references().bibliography;
236 return self.render_with_custom_groups_filtered::<F>(
237 &all_entries,
238 groups,
239 &selected,
240 annotations,
241 annotation_style,
242 );
243 }
244
245 let bibliography_options = self.get_bibliography_options();
247 if let Some(partitioning) = bibliography_options.sort_partitioning.as_ref()
248 && crate::sort_partitioning::should_render_sections(partitioning)
249 {
250 self.initialize_numeric_bibliography_numbers();
251 let all_sorted = self.sort_references(self.bibliography.values().collect());
252 let selected_sorted: Vec<&Reference> = all_sorted
253 .into_iter()
254 .filter(|r| r.id().as_deref().is_some_and(|id| selected.contains(id)))
255 .collect();
256 return self.render_with_partition_sections::<F>(
257 selected_sorted,
258 partitioning,
259 annotations,
260 annotation_style,
261 );
262 }
263
264 self.initialize_numeric_bibliography_numbers();
266 let sorted_refs = self.sort_references(self.bibliography.values().collect());
267
268 let bibliography = self.process_sorted_refs::<_, F>(
269 sorted_refs
270 .iter()
271 .filter(|r| r.id().as_deref().is_some_and(|id| selected.contains(id)))
272 .copied(),
273 |reference, entry_number| {
274 self.process_bibliography_entry_with_format::<F>(reference, entry_number)
275 },
276 );
277
278 let bibliography = self.merge_compound_entries::<F>(bibliography);
279 crate::render::refs_to_string_with_format::<F>(bibliography, annotations, annotation_style)
280 }
281
282 pub fn render_bibliography(&self) -> String {
284 self.render_bibliography_with_format::<crate::render::plain::PlainText>()
285 }
286}