use fact_db::{FactDB, FactId, CacheId};
use pg::dyn::{Value, Type};
use pg::dyn::types::default_types;
use engine::types::{Fact, Clause, Predicate, MatchExpr};
use std::collections::{HashMap, HashSet};
use std::cell::{RefCell, Cell};
#[allow(missing_docs)]
mod errors {
error_chain! {
errors {
Type(msg: String)
Arg(msg: String)
}
}
}
pub use self::errors::*;
pub struct MemDB {
facts: RefCell<HashMap<FactId, Fact>>,
facts_set: RefCell<HashSet<Fact>>,
next_id: Cell<FactId>,
rule_cache: RefCell<Vec<HashSet<Vec<FactId>>>>,
types: RefCell<HashMap<String, Type>>,
preds: RefCell<HashMap<String, Predicate>>,
}
impl MemDB {
pub fn new() -> MemDB {
MemDB {
facts: RefCell::new(HashMap::new()),
next_id: Cell::new(0),
facts_set: RefCell::new(HashSet::new()),
rule_cache: RefCell::new(Vec::new()),
types: RefCell::new(default_types()
.iter()
.filter_map(|type_| type_.name().map(|name| (name.to_owned(), type_.clone())))
.collect()),
preds: RefCell::new(HashMap::new()),
}
}
}
fn raw_option<T>(some: bool, val: T) -> Option<T> {
if some { Some(val) } else { None }
}
impl FactDB for MemDB {
type Error = Error;
fn new_rule_cache(&self, _preds: Vec<String>) -> Result<CacheId> {
self.rule_cache.borrow_mut().push(HashSet::new());
Ok((self.rule_cache.borrow().len() - 1) as CacheId)
}
fn cache_hit(&self, cache: CacheId, facts: Vec<FactId>) -> Result<()> {
self.rule_cache.borrow_mut()[cache as usize].insert(facts);
Ok(())
}
fn insert_fact(&self, fact: &Fact) -> Result<bool> {
if self.facts_set.borrow().contains(fact) {
return Ok(false);
};
let id = self.next_id.get();
self.next_id.set(id + 1);
self.facts.borrow_mut().insert(id, fact.clone());
self.facts_set.borrow_mut().insert(fact.clone());
Ok(true)
}
fn add_type(&self, type_: Type) -> Result<()> {
let name = type_.name().ok_or(ErrorKind::Arg("Provided type had no name".to_string()))?;
self.types.borrow_mut().insert(name.to_string(), type_);
Ok(())
}
fn get_type(&self, type_str: &str) -> Option<Type> {
self.types.borrow().get(type_str).cloned()
}
fn get_predicate(&self, pred_name: &str) -> Option<Predicate> {
self.preds.borrow().get(pred_name).cloned()
}
fn new_predicate(&self, pred: &Predicate) -> Result<()> {
match self.preds.borrow().get(&pred.name) {
Some(exist) => {
if exist == pred {
return Ok(());
} else {
bail!(ErrorKind::Type(format!("Predicate already registered with different \
type.\nExisting: {:?}\nNew: {:?}",
exist,
pred)));
}
}
None => (),
}
self.preds.borrow_mut().insert(pred.name.to_string(), pred.clone());
Ok(())
}
fn search_facts(&self,
query: &Vec<Clause>,
cache: Option<CacheId>)
-> Result<Vec<(Vec<FactId>, Vec<Value>)>> {
Ok(query.iter().fold(vec![(vec![], vec![])], |asgns, clause| {
let facts = self.facts.borrow();
asgns.iter()
.flat_map(|asgn| {
facts.iter().flat_map(move |(id, fact)| {
(if fact.pred_name == clause.pred_name {
fact.args
.iter()
.zip(clause.args.iter())
.fold(Some({
let mut nasgn = asgn.clone();
nasgn.0.push(*id);
nasgn
}),
|o_asgn, (val, &(ref _proj, ref arg))| {
o_asgn.and_then(|asgn| {
match *arg {
MatchExpr::Unbound => Some(asgn),
MatchExpr::Var(var) => {
if var >= asgn.1.len() {
let mut next = asgn.clone();
next.1.push(val.clone());
Some(next)
} else {
raw_option(&asgn.1[var] == val, asgn)
}
}
MatchExpr::Const(ref k) => {
raw_option(k == val, asgn)
}
}
})
})
} else {
None
})
.into_iter()
})
})
.filter(|&(ref facts, _)| {
match cache {
Some(c) => !self.rule_cache.borrow()[c as usize].contains(facts),
None => true,
}
})
.collect()
}))
}
}