use std::collections::BTreeMap;
use sim_kernel::{Cx, Error, Expr, Result, Symbol};
use crate::{Cell, Graph, run_contract::check_expr_shape};
#[derive(Clone, Debug)]
pub struct TopologyCells {
specs: BTreeMap<Symbol, Cell>,
values: BTreeMap<Symbol, Expr>,
}
impl TopologyCells {
pub fn new(graph: &Graph) -> Result<Self> {
let mut specs = BTreeMap::new();
let mut values = BTreeMap::new();
for cell in &graph.cells {
if specs.insert(cell.name.clone(), cell.clone()).is_some() {
return Err(Error::Eval(format!(
"topology run: duplicate cell {}",
cell.name
)));
}
values.insert(cell.name.clone(), cell.initial.clone());
}
Ok(Self { specs, values })
}
pub fn read(&self, name: &Symbol) -> Result<Expr> {
self.values
.get(name)
.cloned()
.ok_or_else(|| Error::Eval(format!("topology run: unknown cell {name}")))
}
pub fn write(&mut self, cx: &mut Cx, name: &Symbol, value: Expr) -> Result<Expr> {
self.check_shape(cx, name, &value)?;
self.values.insert(name.clone(), value.clone());
Ok(value)
}
pub fn append(&mut self, cx: &mut Cx, name: &Symbol, value: Expr) -> Result<Expr> {
let next = append_value(self.read(name)?, value);
self.write(cx, name, next)
}
pub fn merge(&mut self, cx: &mut Cx, name: &Symbol, value: Expr) -> Result<Expr> {
let strategy = self
.spec(name)?
.merge
.as_ref()
.map(|symbol| symbol.name.to_string())
.unwrap_or_else(|| "last".to_owned());
let next = match strategy.as_str() {
"first" => self.read(name)?,
"last" => value,
"append" => append_value(self.read(name)?, value),
other => {
return Err(Error::Eval(format!(
"topology run: unsupported cell merge strategy {other}"
)));
}
};
self.write(cx, name, next)
}
pub fn clear(&mut self, cx: &mut Cx, name: &Symbol) -> Result<Expr> {
self.write(cx, name, Expr::Nil)
}
fn spec(&self, name: &Symbol) -> Result<&Cell> {
self.specs
.get(name)
.ok_or_else(|| Error::Eval(format!("topology run: unknown cell {name}")))
}
fn check_shape(&self, cx: &mut Cx, name: &Symbol, value: &Expr) -> Result<()> {
check_expr_shape(
cx,
format!("cell {name}"),
self.spec(name)?.shape.as_ref(),
value,
)
}
}
fn append_value(current: Expr, value: Expr) -> Expr {
match current {
Expr::Nil => Expr::List(vec![value]),
Expr::List(mut items) => {
items.push(value);
Expr::List(items)
}
Expr::Vector(mut items) => {
items.push(value);
Expr::Vector(items)
}
other => Expr::List(vec![other, value]),
}
}