use crate::ast::*;
use crate::error::CardinalityError;
mod treat_xrefs;
pub(crate) use self::treat_xrefs::*;
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum Cardinality {
ZeroOrOne,
One,
NotOne,
Any,
}
impl Cardinality {
pub fn is_match(&self, n: usize) -> bool {
match self {
Cardinality::ZeroOrOne => n < 2,
Cardinality::One => n == 1,
Cardinality::NotOne => n != 1,
Cardinality::Any => true,
}
}
pub fn to_error<S: Into<String>>(&self, n: usize, tag: S) -> Option<CardinalityError> {
use self::CardinalityError::*;
let name = tag.into();
match self {
Cardinality::ZeroOrOne if n > 1 => Some(DuplicateClauses { name }),
Cardinality::One if n == 0 => Some(MissingClause { name }),
Cardinality::One if n > 1 => Some(DuplicateClauses { name }),
Cardinality::NotOne if n == 1 => Some(SingleClause { name }),
_ => None,
}
}
}
pub trait Orderable {
fn sort(&mut self);
fn is_sorted(&self) -> bool;
}
pub trait OboFrame {
type Clause: OboClause;
fn clauses_ref(&self) -> Vec<&Self::Clause>;
fn cardinality_check(&self) -> Result<(), CardinalityError> {
use std::collections::HashMap;
let mut clause_index: HashMap<_, Vec<&Self::Clause>> = HashMap::new();
for clause in self.clauses_ref() {
clause_index.entry(clause.tag()).or_default().push(clause);
}
for (tag, clauses) in clause_index {
let cardinality = clauses[0].cardinality();
if let Some(err) = cardinality.to_error(clauses.len(), tag) {
return Err(err);
}
}
Ok(())
}
}
pub trait OboClause {
fn tag(&self) -> &str;
fn cardinality(&self) -> Cardinality;
}
pub trait Identified {
fn as_id(&self) -> &Ident;
fn as_id_mut(&mut self) -> &mut Ident;
}
#[cfg(test)]
mod tests {
use super::*;
mod cardinality {
use super::*;
use pretty_assertions::assert_eq;
#[test]
fn is_match() {
assert!(Cardinality::ZeroOrOne.is_match(0));
assert!(Cardinality::ZeroOrOne.is_match(1));
assert!(!Cardinality::ZeroOrOne.is_match(2));
assert!(!Cardinality::One.is_match(0));
assert!(Cardinality::One.is_match(1));
assert!(!Cardinality::One.is_match(2));
assert!(Cardinality::NotOne.is_match(0));
assert!(!Cardinality::NotOne.is_match(1));
assert!(Cardinality::NotOne.is_match(2));
assert!(Cardinality::Any.is_match(0));
assert!(Cardinality::Any.is_match(1));
assert!(Cardinality::Any.is_match(2));
}
#[test]
fn to_error() {
assert_eq!(Cardinality::ZeroOrOne.to_error(0, "ok"), None);
assert_eq!(
Cardinality::ZeroOrOne.to_error(2, "ok"),
Some(CardinalityError::duplicate("ok"))
);
assert_eq!(Cardinality::One.to_error(1, "ok"), None);
assert_eq!(
Cardinality::One.to_error(2, "ok"),
Some(CardinalityError::duplicate("ok"))
);
assert_eq!(
Cardinality::One.to_error(0, "ok"),
Some(CardinalityError::missing("ok"))
);
assert_eq!(Cardinality::NotOne.to_error(0, "ok"), None);
assert_eq!(
Cardinality::NotOne.to_error(1, "ok"),
Some(CardinalityError::single("ok"))
);
assert_eq!(Cardinality::Any.to_error(0, "ok"), None);
}
}
}