diff --git a/node-graph/libraries/core-types/src/record/mod.rs b/node-graph/libraries/core-types/src/record/mod.rs index 6ee3fe0a14..069fa7822e 100644 --- a/node-graph/libraries/core-types/src/record/mod.rs +++ b/node-graph/libraries/core-types/src/record/mod.rs @@ -31,6 +31,6 @@ pub use layout::{ pub use owned::{OwnedRecord, deepen_field_value, has_deep_element_glue, register_deep_element_clone, register_deep_field_value, replay_field_value}; pub use promote::{Promotion, assert_promoted, register_element_promote, register_field_promote, register_retained_heap}; pub use route::{RecordSource, SourcePlan}; -pub use run::{Group, GroupItem, RunBuilder, RunColumn, RunView}; +pub use run::{Group, GroupItem, RunBuilder, RunColumn, RunView, run_to_owned_list}; pub use serve::{FrameClaim, MaterializedSpan, Served, SlotRun, serve_input}; pub use testkit::{LiftedSource, ServedRecord, capture, test_frames}; diff --git a/node-graph/libraries/core-types/src/record/run.rs b/node-graph/libraries/core-types/src/record/run.rs index 7c9bf4443e..0d04ecf0fe 100644 --- a/node-graph/libraries/core-types/src/record/run.rs +++ b/node-graph/libraries/core-types/src/record/run.rs @@ -480,6 +480,25 @@ impl<'e> GroupItem<'e> { } } +/// One typed run as an owned legacy list, elements cloned and every attribute +/// copied through its erased read. The owned result outlives the evaluation the +/// run was materialized in, so an async source can carry it across its await. +pub fn run_to_owned_list(item: &GroupItem) -> Option> { + let lanes = item.typed_lanes::()?; + let mut list = crate::list::List::new(); + for lane in 0..lanes.len() { + list.push(crate::list::Item::new_from_element(lanes.element_ref(lane).clone())); + } + for field in &item.layout().fields { + for lane in 0..lanes.len() { + // SAFETY: the offset comes from the item's own layout. + let value = unsafe { (field.read_erased)(item.lanes().get(lane).rec().ptr().add(field.offset)) }; + list.set_attribute_value_dyn(field.name, lane, crate::list::AttributeValueDyn(value)); + } + } + Some(list) +} + /// A run read at its element type. Record fields hold each marker's value /// verbatim, so lane reads are plain typed reads at a hoisted offset. pub struct RunView<'a, T> { diff --git a/node-graph/libraries/graphic-types/src/graphic/legacy.rs b/node-graph/libraries/graphic-types/src/graphic/legacy.rs index ab6f745e7a..50a2578153 100644 --- a/node-graph/libraries/graphic-types/src/graphic/legacy.rs +++ b/node-graph/libraries/graphic-types/src/graphic/legacy.rs @@ -4,7 +4,7 @@ use super::walk::push_lane_paint_into_interiors; use super::{Graphic, detable_items}; use crate::markers::{ATTR_FILL, ATTR_STROKE}; use core_types::Color; -use core_types::list::{AttributeValueDyn, Item, List}; +use core_types::list::{Item, List}; use raster_types::{CPU, GPU, Raster}; use vector_types::{GradientStops, Vector}; @@ -12,19 +12,7 @@ use vector_types::{GradientStops, Vector}; /// through its erased read. Content keeps its native form; the legacy /// conversions layer their mapping on top. pub fn run_to_list(item: &core_types::record::GroupItem) -> Option> { - let lanes = item.typed_lanes::()?; - let mut list = List::new(); - for lane in 0..lanes.len() { - list.push(Item::new_from_element(lanes.element_ref(lane).clone())); - } - for field in &item.layout().fields { - for lane in 0..lanes.len() { - // SAFETY: the offset comes from the item's own layout. - let value = unsafe { (field.read_erased)(item.lanes().get(lane).rec().ptr().add(field.offset)) }; - list.set_attribute_value_dyn(field.name, lane, AttributeValueDyn(value)); - } - } - Some(list) + core_types::record::run_to_owned_list(item) } /// Converts the group content of the list's paint attribute values to legacy