use crate::NodeUi;
use gantz_ca::{ContentAddr, DataGraph, NodeData};
use gantz_core::Builtins;
use gantz_core::data::{EraseNodeError, ReifyError, ReifyNodeError};
use gantz_core::node::graph::Graph;
use petgraph::visit::EdgeRef;
use std::any::Any;
use std::collections::HashMap;
pub type DynNode = Box<dyn NodeUi>;
pub struct NodeUiInstance {
pub node: DynNode,
erase: fn(&dyn Any) -> Result<NodeData, EraseNodeError>,
}
#[derive(Clone, Copy)]
pub struct NodeCodec {
reify: fn(&NodeData) -> Result<NodeUiInstance, ReifyNodeError>,
sugars: fn() -> gantz_format::Sugars<'static>,
}
#[derive(Default)]
pub struct UiBuiltins {
map: HashMap<ContentAddr, DynNode>,
}
#[derive(Clone, Debug, thiserror::Error)]
pub enum NormalizeNodeError {
#[error(transparent)]
Reify(#[from] ReifyNodeError),
#[error(transparent)]
Erase(#[from] EraseNodeError),
}
impl gantz_core::node::AsRefNode for DynNode {
fn as_ref_node(&self) -> Option<&gantz_core::node::Ref> {
let node: &dyn gantz_core::Node = &**self;
let any: &dyn Any = node;
any.downcast_ref::<crate::node::NamedRef>()
.map(|nr| nr.ref_())
.or_else(|| any.downcast_ref::<gantz_core::node::Ref>())
}
}
impl NodeUiInstance {
pub fn new(node: DynNode, erase: fn(&dyn Any) -> Result<NodeData, EraseNodeError>) -> Self {
Self { node, erase }
}
pub fn erase(&self) -> Result<NodeData, EraseNodeError> {
let node: &dyn gantz_core::Node = &*self.node;
let any: &dyn Any = node;
(self.erase)(any)
}
}
impl UiBuiltins {
pub fn reify(builtins: &Builtins, codec: &NodeCodec) -> (Self, Vec<ReifyNodeError>) {
let mut map = HashMap::new();
let mut errs = vec![];
for name in builtins.names() {
let node_data = builtins.node_data(name).expect("named builtin");
match codec.reify_ui(node_data) {
Ok(inst) => {
map.insert(node_data.content_addr(), inst.node);
}
Err(e) => errs.push(e),
}
}
(Self { map }, errs)
}
pub fn get(&self, ca: &ContentAddr) -> Option<&DynNode> {
self.map.get(ca)
}
}
impl NodeCodec {
pub const fn new(
reify: fn(&NodeData) -> Result<NodeUiInstance, ReifyNodeError>,
sugars: fn() -> gantz_format::Sugars<'static>,
) -> Self {
Self { reify, sugars }
}
pub fn reify_ui(&self, node_data: &NodeData) -> Result<NodeUiInstance, ReifyNodeError> {
(self.reify)(node_data)
}
pub fn reify_graph(&self, dg: &DataGraph) -> Result<Graph<DynNode>, ReifyError> {
let mut out = Graph::with_capacity(dg.node_count(), dg.edge_count());
for (node_ix, node_data) in dg.node_weights().enumerate() {
let inst = self
.reify_ui(node_data)
.map_err(|source| ReifyError { node_ix, source })?;
out.add_node(inst.node);
}
for e in dg.edge_references() {
out.add_edge(e.source(), e.target(), *e.weight());
}
Ok(out)
}
pub fn normalize(&self, node_data: &NodeData) -> Result<NodeData, NormalizeNodeError> {
Ok(self.reify_ui(node_data)?.erase()?)
}
pub fn sugars(&self) -> gantz_format::Sugars<'static> {
(self.sugars)()
}
}
#[macro_export]
macro_rules! ui_node_codec {
($carrier:ty { $($ty:ty),+ $(,)? }) => {{
fn reify(
node_data: &$crate::gantz_ca::NodeData,
) -> ::std::result::Result<
$crate::node::NodeUiInstance,
$crate::gantz_core::data::ReifyNodeError,
> {
$(
if node_data.tag == <$ty as $crate::gantz_nodetag::NodeTag>::TAG {
return $crate::gantz_core::data::reify_node_concrete::<$ty>(node_data)
.map(|node| $crate::node::NodeUiInstance::new(
::std::boxed::Box::new(node),
|any: &dyn ::std::any::Any| {
let node = any
.downcast_ref::<$ty>()
.expect("tag-matched type");
$crate::gantz_core::data::erase_node_typed(node)
},
));
}
)+
::std::result::Result::Err($crate::gantz_core::data::ReifyNodeError {
tag: node_data.tag.clone(),
source: <$crate::gantz_ca::DatumError as ::serde::de::Error>::custom(
"unknown node type tag: not listed in `ui_node_codec!`",
),
})
}
$crate::node::NodeCodec::new(
reify,
<$carrier as $crate::gantz_format::NodeSugar>::sugar,
)
}};
}