use crate::interval::Interval;
use std::fmt::Debug;
use std::hash::Hash;
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum ValueConstraint<V> {
Eq(V),
Lt(V),
Gt(V),
Lte(V),
Gte(V),
Between(V, V),
Any,
EqVar(String),
LtVar(String),
GtVar(String),
LteVar(String),
GteVar(String),
}
impl<V: PartialOrd + PartialEq> ValueConstraint<V> {
pub fn matches(&self, value: &V) -> bool {
match self {
Self::Eq(v) => value == v,
Self::Lt(v) => value < v,
Self::Gt(v) => value > v,
Self::Lte(v) => value <= v,
Self::Gte(v) => value >= v,
Self::Between(lo, hi) => value >= lo && value <= hi,
Self::Any => true,
Self::EqVar(_)
| Self::LtVar(_)
| Self::GtVar(_)
| Self::LteVar(_)
| Self::GteVar(_) => {
debug_assert!(
false,
"BoundVar constraint reached matches() without resolution"
);
false
}
}
}
}
impl<V> ValueConstraint<V> {
pub fn map<V2>(&self, f: impl Fn(&V) -> V2) -> ValueConstraint<V2> {
match self {
Self::Eq(v) => ValueConstraint::Eq(f(v)),
Self::Lt(v) => ValueConstraint::Lt(f(v)),
Self::Gt(v) => ValueConstraint::Gt(f(v)),
Self::Lte(v) => ValueConstraint::Lte(f(v)),
Self::Gte(v) => ValueConstraint::Gte(f(v)),
Self::Between(lo, hi) => ValueConstraint::Between(f(lo), f(hi)),
Self::Any => ValueConstraint::Any,
Self::EqVar(s) => ValueConstraint::EqVar(s.clone()),
Self::LtVar(s) => ValueConstraint::LtVar(s.clone()),
Self::GtVar(s) => ValueConstraint::GtVar(s.clone()),
Self::LteVar(s) => ValueConstraint::LteVar(s.clone()),
Self::GteVar(s) => ValueConstraint::GteVar(s.clone()),
}
}
pub fn is_var(&self) -> bool {
matches!(
self,
Self::EqVar(_) | Self::LtVar(_) | Self::GtVar(_) | Self::LteVar(_) | Self::GteVar(_)
)
}
}
pub trait NodeId: Eq + Hash + Clone + Debug {}
impl<T: Eq + Hash + Clone + Debug> NodeId for T {}
pub trait Label: Eq + Hash + Clone + Debug {}
impl<T: Eq + Hash + Clone + Debug> Label for T {}
pub trait Val: PartialEq + PartialOrd + Clone + Debug + Hash {}
impl<T: PartialEq + PartialOrd + Clone + Debug + Hash> Val for T {}
#[derive(Debug, Clone)]
pub struct Edge<N, V, T> {
pub target: V,
pub interval: Interval<T>,
pub source: N,
}
pub trait DataSource {
type N: Eq + Hash + Clone + Debug;
type L: Eq + Hash + Clone + Debug;
type V: PartialEq + PartialOrd + Clone + Debug + Hash;
type T: Ord + Clone + Debug + Hash;
fn edges_from(
&self,
node: &Self::N,
label: &Self::L,
at: &Self::T,
) -> Vec<Edge<Self::N, Self::V, Self::T>>;
fn scan(
&self,
label: &Self::L,
constraint: &ValueConstraint<Self::V>,
at: &Self::T,
) -> Vec<Edge<Self::N, Self::V, Self::T>>;
fn edges_from_any_time(
&self,
node: &Self::N,
label: &Self::L,
) -> Vec<Edge<Self::N, Self::V, Self::T>>;
fn scan_any_time(
&self,
label: &Self::L,
constraint: &ValueConstraint<Self::V>,
) -> Vec<Edge<Self::N, Self::V, Self::T>>;
fn now(&self) -> Self::T;
fn value_as_node(&self, value: &Self::V) -> Option<Self::N>;
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_constraint_eq() {
let c = ValueConstraint::Eq(42);
assert!(c.matches(&42));
assert!(!c.matches(&43));
}
#[test]
fn test_constraint_range() {
let c = ValueConstraint::Between(10, 20);
assert!(c.matches(&10));
assert!(c.matches(&15));
assert!(c.matches(&20));
assert!(!c.matches(&9));
assert!(!c.matches(&21));
}
#[test]
fn test_constraint_any() {
let c: ValueConstraint<i32> = ValueConstraint::Any;
assert!(c.matches(&0));
assert!(c.matches(&i32::MAX));
}
}