use fabula::datasource::{DataSource, Edge, ValueConstraint};
use fabula::interval::Interval;
use std::fmt;
use std::hash::{Hash, Hasher};
#[derive(Debug, Clone, PartialEq, PartialOrd)]
pub enum MemValue {
Node(String),
Str(String),
Num(f64),
Bool(bool),
}
impl Hash for MemValue {
fn hash<H: Hasher>(&self, state: &mut H) {
std::mem::discriminant(self).hash(state);
match self {
MemValue::Node(s) => s.hash(state),
MemValue::Str(s) => s.hash(state),
MemValue::Num(n) => n.to_bits().hash(state),
MemValue::Bool(b) => b.hash(state),
}
}
}
impl fmt::Display for MemValue {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
MemValue::Node(id) => write!(f, "@{}", id),
MemValue::Str(s) => write!(f, "\"{}\"", s),
MemValue::Num(n) => write!(f, "{}", n),
MemValue::Bool(b) => write!(f, "{}", b),
}
}
}
#[derive(Debug, Clone)]
struct StoredEdge {
source: String,
label: String,
target: MemValue,
interval: Interval<i64>,
}
#[derive(Clone)]
pub struct MemGraph {
edges: Vec<StoredEdge>,
current_time: i64,
}
impl MemGraph {
pub fn new() -> Self {
Self {
edges: Vec::new(),
current_time: 0,
}
}
pub fn set_time(&mut self, t: i64) {
self.current_time = t;
}
pub fn add_edge(&mut self, source: &str, label: &str, target: MemValue, start: i64) {
self.edges.push(StoredEdge {
source: source.to_string(),
label: label.to_string(),
target,
interval: Interval::open(start),
});
}
pub fn add_edge_bounded(
&mut self,
source: &str,
label: &str,
target: MemValue,
start: i64,
end: i64,
) {
self.edges.push(StoredEdge {
source: source.to_string(),
label: label.to_string(),
target,
interval: Interval::new(start, end),
});
}
pub fn add_ref(&mut self, source: &str, label: &str, target_node: &str, start: i64) {
self.add_edge(
source,
label,
MemValue::Node(target_node.to_string()),
start,
);
}
pub fn add_str(&mut self, source: &str, label: &str, value: &str, start: i64) {
self.add_edge(source, label, MemValue::Str(value.to_string()), start);
}
pub fn add_num(&mut self, source: &str, label: &str, value: f64, start: i64) {
self.add_edge(source, label, MemValue::Num(value), start);
}
pub fn end_edge(&mut self, source: &str, label: &str, at: i64) -> bool {
for edge in self.edges.iter_mut().rev() {
if edge.source == source && edge.label == label && edge.interval.end.is_none() {
edge.interval.end = Some(at);
return true;
}
}
false
}
pub fn upsert_edge(&mut self, source: &str, label: &str, value: MemValue, at: i64) {
self.end_edge(source, label, at);
self.add_edge(source, label, value, at);
}
pub fn edge_count(&self) -> usize {
self.edges.len()
}
}
impl Default for MemGraph {
fn default() -> Self {
Self::new()
}
}
impl DataSource for MemGraph {
type N = String;
type L = String;
type V = MemValue;
type T = i64;
fn edges_from(
&self,
node: &String,
label: &String,
at: &i64,
) -> Vec<Edge<String, MemValue, i64>> {
self.edges
.iter()
.filter(|e| &e.source == node && &e.label == label && e.interval.covers(at))
.map(|e| Edge {
source: e.source.clone(),
target: e.target.clone(),
interval: e.interval.clone(),
})
.collect()
}
fn scan(
&self,
label: &String,
constraint: &ValueConstraint<MemValue>,
at: &i64,
) -> Vec<Edge<String, MemValue, i64>> {
self.edges
.iter()
.filter(|e| &e.label == label && constraint.matches(&e.target) && e.interval.covers(at))
.map(|e| Edge {
source: e.source.clone(),
target: e.target.clone(),
interval: e.interval.clone(),
})
.collect()
}
fn edges_from_any_time(
&self,
node: &String,
label: &String,
) -> Vec<Edge<String, MemValue, i64>> {
self.edges
.iter()
.filter(|e| &e.source == node && &e.label == label)
.map(|e| Edge {
source: e.source.clone(),
target: e.target.clone(),
interval: e.interval.clone(),
})
.collect()
}
fn scan_any_time(
&self,
label: &String,
constraint: &ValueConstraint<MemValue>,
) -> Vec<Edge<String, MemValue, i64>> {
self.edges
.iter()
.filter(|e| &e.label == label && constraint.matches(&e.target))
.map(|e| Edge {
source: e.source.clone(),
target: e.target.clone(),
interval: e.interval.clone(),
})
.collect()
}
fn now(&self) -> i64 {
self.current_time
}
fn value_as_node(&self, value: &MemValue) -> Option<String> {
match value {
MemValue::Node(id) => Some(id.clone()),
_ => None,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn end_edge_closes_open_interval() {
let mut g = MemGraph::new();
g.add_str("alice", "mood", "happy", 1);
g.set_time(5);
assert_eq!(g.edges_from(&"alice".into(), &"mood".into(), &5).len(), 1);
assert!(g.end_edge("alice", "mood", 3));
assert_eq!(g.edges_from(&"alice".into(), &"mood".into(), &2).len(), 1);
assert_eq!(g.edges_from(&"alice".into(), &"mood".into(), &3).len(), 0);
}
#[test]
fn end_edge_returns_false_when_no_open_edge() {
let mut g = MemGraph::new();
g.add_edge_bounded("alice", "mood", MemValue::Str("happy".into()), 1, 3);
assert!(!g.end_edge("alice", "mood", 5));
}
#[test]
fn end_edge_closes_most_recent() {
let mut g = MemGraph::new();
g.add_str("alice", "mood", "happy", 1);
g.add_str("alice", "mood", "sad", 5);
assert!(g.end_edge("alice", "mood", 8));
let edges = g.edges_from(&"alice".into(), &"mood".into(), &10);
assert_eq!(edges.len(), 1);
assert_eq!(edges[0].target, MemValue::Str("happy".into()));
}
#[test]
fn upsert_edge_closes_and_inserts() {
let mut g = MemGraph::new();
g.add_str("alice", "mood", "happy", 1);
g.set_time(5);
g.upsert_edge("alice", "mood", MemValue::Str("sad".into()), 5);
let at3 = g.edges_from(&"alice".into(), &"mood".into(), &3);
assert_eq!(at3.len(), 1);
assert_eq!(at3[0].target, MemValue::Str("happy".into()));
let at5 = g.edges_from(&"alice".into(), &"mood".into(), &5);
assert_eq!(at5.len(), 1);
assert_eq!(at5[0].target, MemValue::Str("sad".into()));
assert_eq!(g.edge_count(), 2);
}
}