use crate::hub::domain::common::errors::{HubError, HubResult};
pub struct Stage {
pub name: String,
pub transform: Box<dyn Fn(Vec<f64>) -> HubResult<Vec<f64>>>,
}
pub struct Pipeline {
stages: Vec<Stage>,
}
impl Pipeline {
pub fn new() -> Self {
Self { stages: Vec::new() }
}
pub fn add_stage(
mut self,
name: &str,
transform: Box<dyn Fn(Vec<f64>) -> HubResult<Vec<f64>>>,
) -> Self {
self.stages.push(Stage {
name: name.to_string(),
transform,
});
self
}
pub fn execute(&self, input: Vec<f64>) -> HubResult<Vec<f64>> {
let mut data = input;
for stage in &self.stages {
data = (stage.transform)(data)
.map_err(|e| HubError::ComputationFailed(format!("stage '{}': {e}", stage.name)))?;
}
Ok(data)
}
pub fn stage_count(&self) -> usize {
self.stages.len()
}
pub fn stage_names(&self) -> Vec<&str> {
self.stages.iter().map(|s| s.name.as_str()).collect()
}
}
impl Default for Pipeline {
fn default() -> Self {
Self::new()
}
}
pub fn normalize_stage() -> Box<dyn Fn(Vec<f64>) -> HubResult<Vec<f64>>> {
Box::new(|data| {
if data.is_empty() {
return Err(HubError::EmptyData);
}
let min = data.iter().copied().fold(f64::INFINITY, f64::min);
let max = data.iter().copied().fold(f64::NEG_INFINITY, f64::max);
let range = max - min;
if range == 0.0 {
return Ok(vec![0.0; data.len()]);
}
Ok(data.iter().map(|&v| (v - min) / range).collect())
})
}
pub fn scale_stage(factor: f64) -> Box<dyn Fn(Vec<f64>) -> HubResult<Vec<f64>>> {
Box::new(move |data| Ok(data.iter().map(|&v| v * factor).collect()))
}
pub fn filter_positive_stage() -> Box<dyn Fn(Vec<f64>) -> HubResult<Vec<f64>>> {
Box::new(|data| {
let filtered: Vec<f64> = data.into_iter().filter(|&v| v > 0.0).collect();
if filtered.is_empty() {
return Err(HubError::EmptyData);
}
Ok(filtered)
})
}