use core_types::graphene_hash::CacheHash; use core_types::list::{AttributeValueDyn, Bundle, Item, List}; use core_types::{Ctx, ExtractFootprint, ops::Convert, transform::Footprint}; use std::marker::PhantomData; // Re-export TypeNode from core-types for convenience pub use core_types::ops::TypeNode; /// Passes-through the input value without changing it. This is useful for rerouting wires for organization purposes. #[node_macro::node(category("General"), skip_impl)] fn passthrough<'i, T: 'i + Send>(_: impl Ctx, content: T) -> T { content } /// Shifts a whole wire value onto a connector's type through the std `Into` trait, serving the whole-`List` erasure onto `ListDyn` under the input adapter identifier. #[node_macro::node(category(""), skip_impl)] fn into<'i, T: 'i + Send + Into, O: 'i + Send>(_: impl Ctx, value: T, _out_ty: PhantomData) -> O { value.into() } /// Raises an `Item` wire onto a `List` connector as its one-element list. #[node_macro::node(category(""), skip_impl)] fn item_to_list<'i, T: 'i + Send>(_: impl Ctx, value: Item) -> List { value.into() } /// Boxes a ranked wire's element into a type-erased attribute value, carrying the cell's attributes through the wire. #[node_macro::node(category(""), skip_impl)] fn item_to_attribute_value<'i, T: 'i + Clone + Send + Sync + Default + std::fmt::Debug + PartialEq + CacheHash + 'static>( _: impl Ctx, value: Item, _element_ty: PhantomData, ) -> Item { let (element, attributes) = value.into_parts(); Item::from_parts(AttributeValueDyn(Box::new(element)), attributes) } /// Boxes a whole `List` wire as one type-erased attribute value, for attributes whose per-item value is itself a collection. #[node_macro::node(category(""), skip_impl)] fn list_to_attribute_value<'i, T: 'i + Clone + Send + Sync + Default + std::fmt::Debug + PartialEq + CacheHash + 'static>( _: impl Ctx, value: T, _element_ty: PhantomData, ) -> Item { Item::new_from_element(AttributeValueDyn(Box::new(value))) } /// Wraps a whole `List` onto the wire as one rank-0 `Item>` so an entire collection can feed a connector that carries it as one opaque cell. #[node_macro::node(category(""), skip_impl)] fn bundle<'i, T: 'i + Send>(_: impl Ctx, value: List) -> Item> { Item::new_from_element(Bundle(value)) } /// Unwraps a `Bundle` wire back into the whole `List` it carries, restoring the collection after it passed through a connector as one opaque cell. #[node_macro::node(category(""), skip_impl)] fn unbundle<'i, T: 'i + Send>(_: impl Ctx, value: Item>) -> List { value.into_element().0 } /// Converts an `Item` wire's element to a different element type it can produce through the std `Into` trait, /// letting a convertible wire feed an `Item` connector whose element type it does not match by identity. #[node_macro::node(category(""), skip_impl)] fn into_item<'i, T: 'i + Send + Into, E: 'i + Send>(_: impl Ctx, value: Item, _element_ty: PhantomData) -> Item { let (value, attributes) = value.into_parts(); Item::from_parts(value.into(), attributes) } /// The `List` counterpart of `into_item`, converting every element to a different element type it can produce. #[node_macro::node(category(""), skip_impl)] fn into_list<'i, T: 'i + Send + Into, E: 'i + Send>(_: impl Ctx, value: List, _element_ty: PhantomData) -> List { value .into_iter() .map(|item| { let (value, attributes) = item.into_parts(); Item::from_parts(value.into(), attributes) }) .collect() } /// The [`Convert`]-based counterpart of `into_item`, casting an `Item` wire's element to a connector's numeric element type. #[node_macro::node(category(""), skip_impl)] async fn convert_item<'i, T: 'i + Send + Convert, E: 'i + Send>(ctx: impl Ctx + ExtractFootprint, value: Item, _element_ty: PhantomData) -> Item { let footprint = *ctx.try_footprint().unwrap_or(&Footprint::DEFAULT); let (value, attributes) = value.into_parts(); Item::from_parts(value.convert(footprint, ()).await, attributes) } /// The `List` counterpart of `convert_item`, casting every element to the connector's numeric element type. #[node_macro::node(category(""), skip_impl)] async fn convert_list<'i, T: 'i + Send + Convert, E: 'i + Send>(ctx: impl Ctx + ExtractFootprint, value: List, _element_ty: PhantomData) -> List { let footprint = *ctx.try_footprint().unwrap_or(&Footprint::DEFAULT); let mut result = List::default(); for item in value.into_iter() { let (value, attributes) = item.into_parts(); result.push(Item::from_parts(value.convert(footprint, ()).await, attributes)); } result } #[cfg(test)] mod test { use super::*; #[test] pub fn passthrough_node() { assert_eq!(passthrough((), &4), &4); } }