use crate::node::{NodeCodec, UiBuiltins};
use gantz_ca as ca;
use gantz_core::data::ReifiedGraphs;
use gantz_core::node;
use gantz_core::{Builtins, Node};
use petgraph::visit::{IntoNodeReferences, NodeRef};
use std::borrow::Cow;
use std::collections::BTreeMap;
#[derive(Clone, Copy)]
pub struct Env<'a> {
pub registry: &'a ca::Registry,
pub builtins: &'a Builtins,
pub codec: &'a NodeCodec,
pub graphs: &'a ReifiedGraphs<crate::node::DynNode>,
pub instances: &'a UiBuiltins,
}
pub type Names = BTreeMap<ca::Name, ca::CommitAddr>;
impl Env<'_> {
pub fn node(&self, ca: &ca::ContentAddr) -> Option<&dyn Node> {
let graph_ca = ca::GraphAddr::from(*ca);
if let Some(graph) = self.graphs.get(&graph_ca) {
return Some(graph as &dyn Node);
}
self.instances.get(ca).map(|n| &**n as &dyn Node)
}
pub fn create_node(&self, node_type: &str) -> Option<ca::NodeData> {
let name: ca::Name = node_type.parse().expect("infallible");
head_graph_addr(self.registry, &name)
.and_then(|graph_addr| {
let ref_ = gantz_core::node::Ref::new(graph_addr.into());
let named = crate::node::NamedRef::new(name.clone(), ref_);
match gantz_core::data::erase_node_typed(&named) {
Ok(node_data) => Some(node_data),
Err(e) => {
log::error!("failed to erase a `NamedRef` to `{name}`: {e}");
None
}
}
})
.or_else(|| self.builtins.node_data(node_type).cloned())
}
pub fn name_ca(&self, name: &str) -> Option<ca::ContentAddr> {
let parsed: ca::Name = name.parse().expect("infallible");
head_graph_addr(self.registry, &parsed)
.map(Into::into)
.or_else(|| self.builtins.content_addr(name))
}
pub fn node_exists(&self, ca: &ca::ContentAddr) -> bool {
self.node(ca).is_some()
}
pub fn fn_node_names(&self) -> Vec<String> {
let builtin_names = self.builtins.names().map(str::to_string);
let registry_names = self.registry.heads().map(|(name, _)| name.to_string());
let all_names = builtin_names.chain(registry_names);
let get_node = |ca: &ca::ContentAddr| self.node(ca);
let mut names: Vec<_> = all_names
.filter(|name| {
let meta_ctx = node::MetaCtx::new(&get_node);
self.name_ca(name)
.and_then(|ca| self.node(&ca))
.map(|n| {
!n.stateful(meta_ctx)
&& n.branches(meta_ctx).is_empty()
&& n.n_outputs(meta_ctx) == 1
})
.unwrap_or(false)
})
.collect();
names.sort();
names
}
pub fn node_types(&self) -> Vec<&'_ str> {
let mut types = vec![crate::widget::gantz::NESTED_GRAPH_TYPE];
for name in self.builtins.names() {
types.push(name);
}
types.extend(
self.registry
.heads()
.filter(|(name, _)| !name.is_nested())
.map(|(name, _)| name.segments()[0].as_str()),
);
types.sort();
types.dedup();
types
}
pub fn command_formatted_kb_shortcut(
&self,
_ctx: &egui::Context,
_node_type: &str,
) -> Option<String> {
None
}
pub fn would_ref_cycle(&self, target: &str, editing: &str) -> bool {
let target: ca::Name = target.parse().expect("infallible");
let editing: ca::Name = editing.parse().expect("infallible");
crate::cycle::would_cycle(self.registry, &target, &editing)
}
pub fn demo_graph(&self, name: &str) -> Option<String> {
let parsed: ca::Name = name.parse().expect("infallible");
crate::section::demo(self.registry, &parsed)
}
pub fn socket_doc(
&self,
ca: &ca::ContentAddr,
kind: crate::SocketKind,
ix: usize,
) -> Option<crate::SocketDoc> {
let graph_ca = ca::GraphAddr::from(*ca);
let graph = self.graphs.get(&graph_ca)?;
let get_node = |c: &ca::ContentAddr| self.node(c);
let meta_ctx = node::MetaCtx::new(&get_node);
let node_ref = graph
.node_references()
.filter(|n| match kind {
crate::SocketKind::Input => n.weight().inlet(meta_ctx),
crate::SocketKind::Output => n.weight().outlet(meta_ctx),
})
.nth(ix)?;
let marker = node_ref.weight();
let marker_kind = match kind {
crate::SocketKind::Input => crate::SocketKind::Output,
crate::SocketKind::Output => crate::SocketKind::Input,
};
crate::NodeUi::socket_doc(&**marker, self, marker_kind, 0)
}
pub fn command_info(&self, name: &str) -> crate::CommandInfo {
use crate::SocketKind;
let mut info = crate::CommandInfo {
name: name.to_string(),
description: self.node_description(name),
..Default::default()
};
if name == crate::widget::gantz::NESTED_GRAPH_TYPE {
return info;
}
let get_node = |c: &ca::ContentAddr| self.node(c);
let meta_ctx = node::MetaCtx::new(&get_node);
let collect =
|n: usize,
kind: SocketKind,
f: &dyn Fn(SocketKind, usize) -> Option<crate::SocketDoc>| {
(0..n)
.map(|ix| f(kind, ix).unwrap_or_else(|| crate::SocketDoc::ty("any")))
.collect::<Vec<_>>()
};
let parsed: ca::Name = name.parse().expect("infallible");
if let Some(graph_addr) = head_graph_addr(self.registry, &parsed) {
if let Some(graph) = self.graphs.get(&graph_addr) {
let ca: ca::ContentAddr = graph_addr.into();
let socket = |kind: SocketKind, ix: usize| self.socket_doc(&ca, kind, ix);
info.inputs = collect(graph.n_inputs(meta_ctx), SocketKind::Input, &socket);
info.outputs = collect(graph.n_outputs(meta_ctx), SocketKind::Output, &socket);
}
} else if let Some(builtin) = self
.builtins
.content_addr(name)
.and_then(|ca| self.instances.get(&ca))
{
let socket = |kind: SocketKind, ix: usize| builtin.socket_doc(self, kind, ix);
info.inputs = collect(builtin.n_inputs(meta_ctx), SocketKind::Input, &socket);
info.outputs = collect(builtin.n_outputs(meta_ctx), SocketKind::Output, &socket);
}
info
}
pub fn gui_markers(&self, ca: &ca::ContentAddr) -> Vec<(node::Id, crate::node::Gui)> {
let graph_ca = ca::GraphAddr::from(*ca);
self.registry
.graph(&graph_ca)
.map(crate::node::gui::markers)
.unwrap_or_default()
}
pub fn ref_target(&self, ca: &ca::ContentAddr, id: node::Id) -> Option<ca::ContentAddr> {
let graph_ca = ca::GraphAddr::from(*ca);
let g = self.registry.graph(&graph_ca)?;
crate::node::gui::ref_target_of(g.node_weight(petgraph::graph::NodeIndex::new(id))?)
}
pub fn node_at(&self, ca: &ca::ContentAddr, rel_path: &[node::Id]) -> Option<&dyn Node> {
let (&last, hops) = rel_path.split_last()?;
let mut ca = *ca;
for &hop in hops {
ca = self.ref_target(&ca, hop)?;
}
let g = self.graphs.get(&ca::GraphAddr::from(ca))?;
let weight = g.node_weight(petgraph::graph::NodeIndex::new(last))?;
Some(&**weight as &dyn Node)
}
pub fn merge_preview(
&self,
ours: &ca::Head,
source: &str,
resolutions: ca::Resolutions,
) -> Option<crate::merge::MergePreview> {
crate::merge::merge_preview(self.registry, ours, source, resolutions)
}
pub fn node_description(&self, name: &str) -> Option<Cow<'static, str>> {
if name == crate::widget::gantz::NESTED_GRAPH_TYPE {
return Some(Cow::Borrowed(
"Create a new nested graph. Its inlets and outlets become this node's sockets.",
));
}
let parsed: ca::Name = name.parse().expect("infallible");
if self.registry.head(&parsed).is_some() {
return crate::section::description(self.registry, &parsed).map(Cow::Owned);
}
self.builtins
.content_addr(name)
.and_then(|ca| self.instances.get(&ca))
.and_then(|n| n.description())
.map(Cow::Borrowed)
}
}
pub fn head_graph_addr(reg: &ca::Registry, name: &ca::Name) -> Option<ca::GraphAddr> {
let head_ca = reg.head(name)?;
reg.commits().get(&head_ca).map(|commit| commit.graph)
}
pub fn names(reg: &ca::Registry) -> Vec<(ca::Name, ca::CommitAddr)> {
reg.heads().map(|(name, ca)| (name.clone(), ca)).collect()
}
pub fn n_outputs_at(env: &Env<'_>, ca: &ca::ContentAddr, rel_path: &[node::Id]) -> Option<usize> {
let node = env.node_at(ca, rel_path)?;
let get_node = |c: &ca::ContentAddr| env.node(c);
Some(node.n_outputs(node::MetaCtx::new(&get_node)))
}
pub fn resolve_ref_chain(
env: &Env<'_>,
start: ca::ContentAddr,
chain: &[node::Id],
) -> Option<(ca::ContentAddr, node::Id)> {
let mut ca = start;
for &hop in chain {
ca = env.ref_target(&ca, hop)?;
}
let (id, _) = env
.gui_markers(&ca)
.into_iter()
.find(|(_, gui)| gui.role == crate::node::GuiRole::Body)?;
Some((ca, id))
}