aspero_core/matching/
derivative.rs1use super::Claim;
5use super::text::normalise;
6
7pub(super) const DERIVATIVE_MARKERS: &[&str] = &[
10 "reply",
11 "erratum",
12 "errata",
13 "corrigendum",
14 "correction",
15 "comment on",
16 "response to",
17 "in response",
18 "faculty opinions recommendation",
19 "retraction of",
20 "expression of concern",
21];
22
23const LEADING_MARKERS: &[&str] = &["comment on", "response to", "in response", "correction"];
28
29pub fn is_derivative(title: &str) -> bool {
32 derivative_kind(title).is_some()
33}
34
35#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
37#[non_exhaustive]
38pub enum WorkKind {
39 Reply,
40 Correction,
41 Comment,
42 Retraction,
43 ExpressionOfConcern,
44 Recommendation,
45}
46
47impl WorkKind {
48 pub fn as_str(self) -> &'static str {
51 match self {
52 WorkKind::Reply => "reply",
53 WorkKind::Correction => "correction",
54 WorkKind::Comment => "comment",
55 WorkKind::Retraction => "retraction",
56 WorkKind::ExpressionOfConcern => "expression of concern",
57 WorkKind::Recommendation => "recommendation",
58 }
59 }
60}
61
62pub fn derivative_kind(title: &str) -> Option<&'static str> {
64 derivative_work_kind(title).map(WorkKind::as_str)
65}
66
67pub fn derivative_work_kind(title: &str) -> Option<WorkKind> {
69 let norm = normalise(title);
70 let words = format!(" {norm} ");
71 let leading = |m: &str| match m {
72 "correction" => {
73 norm == "correction"
74 || norm.starts_with("correction to ")
75 || title
76 .trim_start()
77 .get(..11)
78 .is_some_and(|h| h.eq_ignore_ascii_case("correction:"))
79 }
80 _ => words.starts_with(&format!(" {m} ")),
81 };
82 let marker = DERIVATIVE_MARKERS
83 .iter()
84 .find(|m| match LEADING_MARKERS.contains(m) {
85 true => leading(m),
86 false => words.contains(&format!(" {m} ")),
87 })?;
88 Some(match *marker {
89 "reply" | "response to" | "in response" => WorkKind::Reply,
90 "erratum" | "errata" | "corrigendum" | "correction" => WorkKind::Correction,
91 "comment on" => WorkKind::Comment,
92 "retraction of" => WorkKind::Retraction,
93 "expression of concern" => WorkKind::ExpressionOfConcern,
94 _ => WorkKind::Recommendation,
95 })
96}
97
98pub fn claim_mentions_derivative(claim: &Claim) -> bool {
101 [claim.text.as_deref(), claim.title.as_deref()]
102 .into_iter()
103 .flatten()
104 .any(|text| {
105 let words = format!(" {} ", normalise(text));
106 DERIVATIVE_MARKERS
107 .iter()
108 .any(|m| words.contains(&format!(" {m} ")))
109 })
110}