Dataset Hooks
Dataset hooks fire during the load and save operations inside Node::call(). Each hook receives the owning node and the dataset reference.
Methods
fn before_dataset_loaded(&self, n: &dyn PipelineInfo, ds: &DatasetRef) {}
fn after_dataset_loaded(&self, n: &dyn PipelineInfo, ds: &DatasetRef) {}
fn before_dataset_saved(&self, n: &dyn PipelineInfo, ds: &DatasetRef) {}
fn after_dataset_saved(&self, n: &dyn PipelineInfo, ds: &DatasetRef) {}
Arguments
n— the node that is loading/saving the dataset. Usen.name()to get the node name.ds— the dataset reference:ds.id— unique pointer-based identifierds.name— resolved name from the catalog (e.g.Some("readings")), orNoneinno_stdds.meta.is_param()— whether this is a parameter datasetds.meta.type_string()— the Rust type name (e.g."pondrs::datasets::memory::MemoryDataset<f64>")ds.meta.html()— (stdonly) returns an optional HTML snippet for the dataset’s current contents. Datasets likePlotlyDatasetoverride this to produce rich visualizations; file-backed datasets render their contents as formatted text. Used by the viz dashboard.ds.meta.yaml()— (stdonly) returns the dataset’s configuration serialized as YAML. This is produced automatically via theSerializesupertrait and is used by the viz dashboard to display dataset settings.
Firing order
For a node with two inputs and one output, hooks fire in this order:
before_dataset_loaded(input 0)after_dataset_loaded(input 0)before_dataset_loaded(input 1)after_dataset_loaded(input 1)- node function executes
before_dataset_saved(output 0)after_dataset_saved(output 0)
Dataset hooks fire inside the before_node_run / after_node_run window. The sequence is always: before_node_run → dataset loads → function call → dataset saves → after_node_run.
Example: tracking I/O time
use std::sync::Mutex;
use std::collections::HashMap;
use std::time::Instant;
struct IoTimingHook {
starts: Mutex<HashMap<usize, Instant>>,
}
impl Hook for IoTimingHook {
fn before_dataset_loaded(&self, _n: &dyn PipelineInfo, ds: &DatasetRef) {
self.starts.lock().unwrap().insert(ds.id, Instant::now());
}
fn after_dataset_loaded(&self, _n: &dyn PipelineInfo, ds: &DatasetRef) {
if let Some(start) = self.starts.lock().unwrap().remove(&ds.id) {
let name = ds.name.unwrap_or("<unknown>");
println!(" loaded {} in {:.1}ms", name, start.elapsed().as_secs_f64() * 1000.0);
}
}
}
Name resolution
In std builds, the runner resolves dataset names from the catalog indexer before dispatching to hooks. This is why ds.name is Option<&str> — it’s Some("readings") when the catalog indexer can map the pointer to a field name, and None in no_std builds where the indexer isn’t available.