1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
use std::error::Error;
use std::fmt::Display;
use std::fmt::Formatter;
use std::fmt::Result as FmtResult;
use fastobo::ast::*;
use fastobo::visit::Visit;
use super::ValidationError;
use super::Validator;
#[derive(Debug)]
pub struct ObsoletionError {
id: Ident,
replaced_by: usize,
consider: usize,
}
impl Display for ObsoletionError {
fn fmt(&self, f: &mut Formatter) -> FmtResult {
if self.replaced_by > 0 {
write!(
f,
"non-obsolete entity contains {} `replaced_by` clause(s)",
self.replaced_by
)
} else {
write!(
f,
"non-obsolete entity contains {} `consider` clause(s)",
self.consider
)
}
}
}
impl Error for ObsoletionError {
fn description(&self) -> &str {
"non-obsolete frame contains obsoletion clauses"
}
}
#[derive(Default)]
pub struct ObsoletionChecker {
errors: Vec<ObsoletionError>,
}
macro_rules! impl_visit {
($name:ident, $frame:ty, $clause:ident) => {
fn $name(&mut self, frame: &'a $frame) {
let mut obsolete = false;
let mut replaced_by: Vec<&'a Ident> = Vec::new();
let mut consider: Vec<&'a Ident> = Vec::new();
for clause in frame.clauses() {
match clause.as_inner() {
$clause::IsObsolete(true) => obsolete = true,
$clause::ReplacedBy(id) => replaced_by.push(id.as_ref().as_ref()),
$clause::Consider(id) => consider.push(id.as_ref().as_ref()),
_ => (),
}
}
if (replaced_by.len() > 0 || consider.len() > 1) && !obsolete {
self.errors.push(ObsoletionError {
id: frame.id().as_inner().as_ref().as_ref().clone(),
replaced_by: replaced_by.len(),
consider: consider.len(),
})
}
}
};
}
impl<'a> Visit<'a> for ObsoletionChecker {
impl_visit!(visit_term_frame, TermFrame, TermClause);
impl_visit!(visit_typedef_frame, TypedefFrame, TypedefClause);
impl_visit!(visit_instance_frame, InstanceFrame, InstanceClause);
}
impl Validator for ObsoletionChecker {
fn validate(doc: &OboDoc) -> Vec<ValidationError> {
let mut checker = Self::default();
checker.visit_doc(doc);
let mut errors = Vec::new();
for err in checker.errors {
errors.push(ValidationError {
location: format!("frame {}", err.id),
cause: Box::new(err),
})
}
errors
}
}