use std::{
collections::BTreeMap,
path::{Path, PathBuf},
};
use sim_kernel::{Cx, Error, Expr, Result, Symbol};
use crate::{
Graph, TopologyConnection,
capability::{topology_file_capability, topology_write_capability},
compile_graph,
package::{TopologyPackage, load_package_file},
run::run_graph,
site::connection_from_graph,
};
mod runtime;
pub use runtime::{
SharedTopologyRegistry, TopologyLib, install_topology_lib, manifest_name, topology_exports,
};
#[derive(Clone, Debug)]
pub struct TopologyEntry {
pub name: Symbol,
pub graph: Graph,
pub source: Option<PathBuf>,
pub metadata: Vec<(Symbol, Expr)>,
pub capabilities: Vec<Symbol>,
}
impl TopologyEntry {
fn from_graph(name: Symbol, graph: Graph) -> Self {
Self {
name,
metadata: graph.metadata.clone(),
capabilities: graph.capabilities.clone(),
graph,
source: None,
}
}
fn from_package(package: TopologyPackage, source: PathBuf) -> Self {
Self {
name: package.name().clone(),
graph: package.graph,
source: Some(source),
metadata: package.metadata,
capabilities: package.capabilities,
}
}
pub fn connection(&self, cx: &mut Cx) -> Result<TopologyConnection> {
connection_from_graph(cx, &self.graph)
}
pub fn run(&self, cx: &mut Cx, input: Expr) -> Result<Expr> {
let plan = compile_graph(cx, &self.graph)?;
run_graph(cx, &self.graph, &plan, input)
}
}
#[derive(Clone, Debug, Default)]
pub struct TopologyRegistry {
entries: BTreeMap<Symbol, TopologyEntry>,
}
impl TopologyRegistry {
pub fn new() -> Self {
Self::default()
}
pub fn def(&mut self, cx: &mut Cx, name: Symbol, graph: Graph) -> Result<TopologyEntry> {
cx.require(&topology_write_capability())?;
let entry = TopologyEntry::from_graph(name.clone(), graph);
self.entries.insert(name, entry.clone());
Ok(entry)
}
pub fn get(&self, name: &Symbol) -> Option<&TopologyEntry> {
self.entries.get(name)
}
pub fn list(&self) -> Vec<Symbol> {
self.entries.keys().cloned().collect()
}
pub fn remove(&mut self, cx: &mut Cx, name: &Symbol) -> Result<Option<TopologyEntry>> {
cx.require(&topology_write_capability())?;
Ok(self.entries.remove(name))
}
pub fn load_file(&mut self, cx: &mut Cx, path: impl AsRef<Path>) -> Result<TopologyEntry> {
cx.require(&topology_file_capability())?;
cx.require(&topology_write_capability())?;
let path = path.as_ref().to_path_buf();
let package = load_package_file(&path)?;
let entry = TopologyEntry::from_package(package, path);
self.entries.insert(entry.name.clone(), entry.clone());
Ok(entry)
}
pub fn reload(&mut self, cx: &mut Cx, name: &Symbol) -> Result<TopologyEntry> {
cx.require(&topology_file_capability())?;
cx.require(&topology_write_capability())?;
let source = self
.entries
.get(name)
.and_then(|entry| entry.source.clone())
.ok_or_else(|| Error::Eval(format!("topology {name} has no reloadable source")))?;
let package = load_package_file(&source)?;
if package.name() != name {
return Err(Error::Eval(format!(
"topology reload name mismatch: expected {name}, found {}",
package.name()
)));
}
let entry = TopologyEntry::from_package(package, source);
self.entries.insert(name.clone(), entry.clone());
Ok(entry)
}
}
pub fn topology_def(
cx: &mut Cx,
registry: &mut TopologyRegistry,
name: Symbol,
graph: Graph,
) -> Result<TopologyEntry> {
registry.def(cx, name, graph)
}
pub fn topology_get<'a>(
registry: &'a TopologyRegistry,
name: &Symbol,
) -> Option<&'a TopologyEntry> {
registry.get(name)
}
pub fn topology_list(registry: &TopologyRegistry) -> Vec<Symbol> {
registry.list()
}
pub fn topology_remove(
cx: &mut Cx,
registry: &mut TopologyRegistry,
name: &Symbol,
) -> Result<Option<TopologyEntry>> {
registry.remove(cx, name)
}
pub fn topology_load_file(
cx: &mut Cx,
registry: &mut TopologyRegistry,
path: impl AsRef<Path>,
) -> Result<TopologyEntry> {
registry.load_file(cx, path)
}
pub fn topology_reload(
cx: &mut Cx,
registry: &mut TopologyRegistry,
name: &Symbol,
) -> Result<TopologyEntry> {
registry.reload(cx, name)
}