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().as_str() == "ISBN" { if let Err(e) = Isbn::from_str(id.local().as_str()) { 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 } }