use std::{any::Any, time::Duration};
use sim_kernel::{
CapabilityName, ClassRef, Consistency, Cx, EvalFabric, EvalMode, EvalReply, EvalRequest, Expr,
Object, ObjectCompat, Result, Symbol, Value,
};
use crate::{
CompiledGraph, Graph, capability::topology_run_capability, compile_graph, run::run_graph,
};
#[sim_citizen_derive::non_citizen(
reason = "live topology eval-fabric handle; reconstruct from topology/Package descriptor data",
kind = "handle",
descriptor = "topology/Package"
)]
#[derive(Clone, Debug)]
pub struct TopologyConnection {
source: Graph,
graph: CompiledGraph,
}
impl TopologyConnection {
pub fn new(source: Graph, graph: CompiledGraph) -> Self {
Self { source, graph }
}
pub fn source_graph(&self) -> &Graph {
&self.source
}
pub fn graph(&self) -> &CompiledGraph {
&self.graph
}
pub fn site_kind(&self) -> &'static str {
"topology"
}
pub fn request(
&self,
cx: &mut Cx,
expr: Expr,
timeout: Option<Duration>,
required_capabilities: Vec<CapabilityName>,
) -> Result<Value> {
let reply = self.realize(
cx,
EvalRequest {
expr,
result_shape: None,
required_capabilities,
deadline: timeout,
consistency: Consistency::LocalFirst,
mode: EvalMode::Eval,
answer_limit: None,
stream_buffer: None,
stream: false,
trace: false,
},
)?;
Ok(reply.value)
}
}
impl Object for TopologyConnection {
fn display(&self, _cx: &mut Cx) -> Result<String> {
Ok(format!("#<topology-connection {}>", self.source.name))
}
fn as_any(&self) -> &dyn Any {
self
}
}
impl ObjectCompat for TopologyConnection {
fn class(&self, cx: &mut Cx) -> Result<ClassRef> {
cx.factory().nil()
}
fn as_eval_fabric(&self) -> Option<&dyn EvalFabric> {
Some(self)
}
}
impl EvalFabric for TopologyConnection {
fn realize(&self, cx: &mut Cx, request: EvalRequest) -> Result<EvalReply> {
answer_request(cx, self, request)
}
}
pub fn connection_from_graph(cx: &mut Cx, graph: &Graph) -> Result<TopologyConnection> {
let compiled = compile_graph(cx, graph)?;
Ok(TopologyConnection::new(graph.clone(), compiled))
}
fn answer_request(
cx: &mut Cx,
connection: &TopologyConnection,
request: EvalRequest,
) -> Result<EvalReply> {
cx.require(&topology_run_capability())?;
cx.require_all(&request.required_capabilities)?;
let output = run_graph(
cx,
connection.source_graph(),
connection.graph(),
request.expr,
)?;
let reply = EvalReply {
value: cx.factory().expr(output)?,
diagnostics: cx.take_diagnostics(),
trace: request
.trace
.then(|| cx.factory().symbol(Symbol::new("topology")).ok())
.flatten(),
};
Ok(reply)
}