1use aspero_core::{Field, FieldSet, IdKind, ProviderId, SourceRecord, Update, Usage, WorkId};
5
6use crate::{Engine, Entry, LookupReport, LookupResult, resolve};
7
8impl Engine {
9 pub async fn add_notices(&self, report: &mut LookupReport) {
15 if let Some(entry) = self.registry() {
16 let works = found_records(&report.results, |_| true);
17 for (i, found) in self.ask_about(entry, works, &mut report.usage).await {
18 let Some(record) = report.results[i].record.as_mut() else {
19 continue;
20 };
21 let work = &mut record.work;
22 resolve::merge_updates(&mut work.updates, &found.work.updates);
23 for target in found.work.update_to {
24 if !work.update_to.contains(&target) {
25 work.update_to.push(target);
26 }
27 }
28 work.type_as_notice();
29 let own_doi = record.ids.iter().find(|id| id.kind() == IdKind::Doi);
33 if work.update_to.iter().any(|t| t.kind.is_retraction())
34 && let Some(own) = own_doi
35 {
36 let bare = |u: &&mut Update| u.kind.is_retraction() && u.notice.is_none();
37 for update in work.updates.iter_mut().filter(bare) {
38 update.notice = Some(own.clone());
39 }
40 }
41 }
42 }
43 self.date_notices(report).await;
44 }
45
46 pub async fn add_open_access(&self, report: &mut LookupReport) {
49 for entry in &self.entries {
50 if !entry
51 .provider
52 .capabilities()
53 .fields
54 .contains(Field::OpenAccess)
55 {
56 continue;
57 }
58 let works = found_records(&report.results, |r| r.work.open_access.is_none());
59 for (i, found) in self.ask_about(entry, works, &mut report.usage).await {
60 if let (Some(oa), Some(record)) =
61 (found.work.open_access, report.results[i].record.as_mut())
62 {
63 record.work.open_access = Some(oa);
64 }
65 }
66 }
67 }
68
69 pub(crate) async fn ask_about<'a>(
73 &self,
74 entry: &Entry,
75 works: impl IntoIterator<Item = (usize, &'a SourceRecord)>,
76 usage: &mut Vec<(ProviderId, Usage)>,
77 ) -> Vec<(usize, SourceRecord)> {
78 let provider = entry.provider.id();
79 let kinds = &entry.provider.capabilities().lookup_kinds;
80 let wanted: Vec<(usize, WorkId)> = works
81 .into_iter()
82 .filter(|(_, r)| r.source != provider)
83 .filter_map(|(i, r)| {
84 let id = r.ids.iter().find(|id| kinds.contains(&id.kind()))?;
85 Some((i, id.clone()))
86 })
87 .collect();
88 if wanted.is_empty() {
89 return Vec::new();
90 }
91 let ids: Vec<WorkId> = wanted.iter().map(|(_, id)| id.clone()).collect();
92 let answers = self.lookup_at(entry, &ids, FieldSet::ALL, usage).await;
93 wanted
94 .into_iter()
95 .zip(answers)
96 .filter_map(|((i, _), (_, record))| Some((i, record?)))
97 .collect()
98 }
99}
100
101pub(crate) fn found_records(
103 results: &[LookupResult],
104 keep: impl Fn(&SourceRecord) -> bool,
105) -> impl Iterator<Item = (usize, &SourceRecord)> {
106 results
107 .iter()
108 .enumerate()
109 .filter_map(move |(i, r)| Some((i, r.record.as_ref().filter(|r| keep(r))?)))
110}