use super::{
activation::Activation,
component::Component,
errors::{NoSuchComponent, NoSuchItem},
undo::{NoMoreRedo, NoMoreUndo, UndoLimit},
variable::Variable,
};
use crate::{
event::Event,
planner::PlanError,
solver::SolveError,
thread::{DummyPool, ThreadPool},
};
use std::{collections::HashMap, fmt::Debug};
#[derive(Clone, Debug, PartialEq)]
pub struct ConstraintSystem<T> {
component_map: HashMap<String, usize>,
components: Vec<Component<T>>,
undo_stack: Vec<String>,
redo_stack: Vec<String>,
}
impl<T> Default for ConstraintSystem<T> {
fn default() -> Self {
Self {
component_map: HashMap::new(),
components: Vec::new(),
undo_stack: Vec::new(),
redo_stack: Vec::new(),
}
}
}
impl<T: Debug> ConstraintSystem<T> {
pub fn new() -> Self {
Self::default()
}
pub fn add_component(&mut self, component: Component<T>) {
let index = self.components.len();
self.component_map
.insert(component.name().to_owned(), index);
self.components.push(component);
}
pub fn component<'s>(&self, name: &'s str) -> Result<&Component<T>, NoSuchComponent<'s>> {
match self.component_map.get(name) {
Some(&index) => Ok(&self.components[index]),
None => Err(NoSuchComponent(&name)),
}
}
pub fn component_mut<'s>(
&mut self,
name: &'s str,
) -> Result<&mut Component<T>, NoSuchComponent<'s>> {
match self.component_map.get(name) {
Some(&index) => Ok(&mut self.components[index]),
None => Err(NoSuchComponent(&name)),
}
}
pub fn set_variable<'s>(
&mut self,
component: &'s str,
variable: &'s str,
value: T,
) -> Result<(), NoSuchItem<'s>> {
self.undo_stack.push(component.to_string());
self.redo_stack.clear();
log::trace!("Variable {}.{} updated to {:?}", component, variable, value);
let component = self.component_mut(component)?;
component.set_variable(variable, value)?;
Ok(())
}
pub fn variable<'a>(
&self,
component: &'a str,
variable: &'a str,
) -> Result<&Variable<Activation<T>>, NoSuchItem<'a>> {
let component = self.component(component)?;
let variable = component.variable(variable)?;
Ok(variable)
}
pub fn value<'a>(
&self,
component: &'a str,
variable: &'a str,
) -> Result<Activation<T>, NoSuchItem<'a>> {
let component = self.component(component)?;
let variable = component.value(variable)?;
Ok(variable)
}
pub fn update(&mut self) -> Result<(), PlanError>
where
T: Send + Sync + 'static + Debug,
{
log::trace!("update");
self.par_update(&mut DummyPool)
}
pub fn par_update(&mut self, spawn: &mut impl ThreadPool) -> Result<(), PlanError>
where
T: Send + Sync + 'static + Debug,
{
log::trace!("par_update");
for component in &mut self.components {
if component.is_modified() {
component.par_update(spawn)?;
}
}
Ok(())
}
pub fn par_update_always(&mut self, spawn: &mut impl ThreadPool) -> Result<(), PlanError>
where
T: Send + Sync + 'static + Debug,
{
for component in &mut self.components {
component.par_update(spawn)?;
}
Ok(())
}
pub fn subscribe<'a>(
&mut self,
component: &'a str,
variable: &'a str,
callback: impl for<'e> Fn(Event<'e, T, SolveError>) + Send + 'static,
) -> Result<(), NoSuchItem<'a>>
where
T: 'static,
{
log::trace!("Subscribing to {}.{}", component, variable);
let component = self.component_mut(component)?;
component
.subscribe(variable, callback)
.map_err(NoSuchItem::NoSuchVariable)
}
pub fn unsubscribe<'a>(
&mut self,
component: &'a str,
variable: &'a str,
) -> Result<(), NoSuchItem<'a>> {
log::trace!("Unsubscribing from {}.{}", component, variable);
let component = self.component_mut(component)?;
component
.unsubscribe(variable)
.map_err(NoSuchItem::NoSuchVariable)
}
pub fn pin<'s>(&mut self, component: &'s str, variable: &'s str) -> Result<(), NoSuchItem<'s>>
where
T: 'static,
{
let component = self.component_mut(component)?;
component.pin(variable)?;
Ok(())
}
pub fn unpin<'s>(&mut self, component: &'s str, variable: &'s str) -> Result<(), NoSuchItem<'s>>
where
T: 'static,
{
let component = self.component_mut(component)?;
component.unpin(variable)?;
Ok(())
}
pub fn undo(&mut self) -> Result<(), NoMoreUndo> {
let last_undone = self.undo_stack.pop().ok_or(NoMoreUndo)?;
let component = self
.component_mut(&last_undone)
.expect("Component was removed");
log::trace!("Undoing last change in {}", component.name());
let _ = component.undo()?;
self.redo_stack.push(last_undone);
Ok(())
}
pub fn redo(&mut self) -> Result<(), NoMoreRedo> {
let last_redone = self.redo_stack.pop().ok_or(NoMoreRedo)?;
let component = self
.component_mut(&last_redone)
.expect("Component was removed");
log::trace!("Redoing last change in {}", component.name());
let _ = component.redo()?;
self.undo_stack.push(last_redone);
Ok(())
}
pub fn set_undo_limit(&mut self, limit: UndoLimit) {
for component in &mut self.components {
component.set_undo_limit(limit);
}
}
pub fn enable_constraint<'a>(
&mut self,
component: &'a str,
variable: &'a str,
) -> Result<(), NoSuchItem<'a>> {
let component = self.component_mut(component)?;
component.enable_constraint(variable)?;
Ok(())
}
pub fn disable_constraint<'a>(
&mut self,
component: &'a str,
variable: &'a str,
) -> Result<(), NoSuchItem<'a>> {
let component = self.component_mut(component)?;
component.disable_constraint(variable)?;
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::ConstraintSystem;
use crate::{component, event::Event, ret};
#[test]
pub fn constraint_system_test() {
let mut cs = ConstraintSystem::new();
cs.add_component(component! {
component comp {
let a: i32 = 0, b: i32 = 0, c: i32 = 0;
constraint sum {
abc(a: &i32, b: &i32) -> [c] = ret![a + b];
bca(a: &i32, c: &i32) -> [b] = ret![c - a];
cab(b: &i32, c: &i32) -> [a] = ret![c - b];
}
}
});
cs.set_variable("comp", "a", 7).unwrap();
assert_eq!(cs.update(), Ok(()));
let comp = cs.component_mut("comp").unwrap();
comp.subscribe("a", |event| {
if let Event::Ready(v) = event {
assert_eq!(*v, 7)
}
})
.unwrap();
comp.subscribe("b", |event| {
if let Event::Ready(v) = event {
assert_eq!(*v, 0)
}
})
.unwrap();
comp.subscribe("c", |event| {
if let Event::Ready(v) = event {
assert_eq!(*v, 7)
}
})
.unwrap();
}
}