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