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
use std::collections::HashMap;
use std::collections::HashSet;
use std::error::Error;
use std::fmt::Display;
use std::fmt::Formatter;
use std::fmt::Result as FmtResult;
use std::str::FromStr;
use fastobo::ast::*;
use fastobo::semantics::Identified;
use fastobo::visit::Visit;
use isbn_crate::Isbn;
use super::ValidationError;
use super::Validator;
#[derive(Debug)]
pub struct InvalidIsbn(PrefixedIdent, isbn_crate::IsbnError);
impl Display for InvalidIsbn {
fn fmt(&self, f: &mut Formatter) -> FmtResult {
write!(f, "invalid isbn `{}` ", self.0)?;
f.write_str(match self.1 {
isbn_crate::IsbnError::InvalidChecksum => "(invalid checksum)",
isbn_crate::IsbnError::InvalidLength => "(invalid length)",
isbn_crate::IsbnError::InvalidDigit => "(invalid digit)",
isbn_crate::IsbnError::InvalidGroup => "(invalid group)",
isbn_crate::IsbnError::InvalidConversion => "(invalid conversion)",
isbn_crate::IsbnError::UndefinedRange => "(undefined range)",
})
}
}
impl Error for InvalidIsbn {
fn description(&self) -> &str {
"invalid isbn"
}
}
#[derive(Default)]
pub struct IsbnChecker<'a> {
current_entity: Option<&'a Ident>,
valid: HashSet<&'a PrefixedIdent>,
invalid: HashMap<&'a Ident, Vec<InvalidIsbn>>,
}
impl<'a> Visit<'a> for IsbnChecker<'a> {
fn visit_entity_frame(&mut self, entity: &'a EntityFrame) {
self.current_entity = Some(entity.as_id());
match entity {
EntityFrame::Term(t) => self.visit_term_frame(t),
EntityFrame::Typedef(t) => self.visit_typedef_frame(t),
EntityFrame::Instance(i) => self.visit_instance_frame(i),
}
}
fn visit_prefixed_ident(&mut self, id: &'a PrefixedIdent) {
if id.prefix() == "ISBN" {
if let Err(e) = Isbn::from_str(id.local()) {
self.invalid
.entry(self.current_entity.unwrap())
.or_default()
.push(InvalidIsbn(id.clone(), e));
} else {
self.valid.insert(id);
}
}
}
}
impl Validator for IsbnChecker<'_> {
fn validate(doc: &OboDoc) -> Vec<ValidationError> {
let mut checker = Self::default();
checker.visit_doc(doc);
let mut errors = Vec::new();
for (entity, errs) in checker.invalid {
for err in errs {
errors.push(ValidationError {
location: format!("frame {}", entity),
cause: Box::new(err),
})
}
}
errors
}
}