use super::orchestration::{ReasoningEngine, EngineResult, ProcessingOptions, EngineError};
use fukurow_store::store::RdfStore;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use chrono::{DateTime, Utc};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PipelineStage {
pub name: String,
pub description: String,
pub engine: PipelineEngine,
pub conditions: Vec<PipelineCondition>,
pub on_success: Vec<String>, pub on_failure: Vec<String>, }
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum PipelineEngine {
Rdfs,
OwlLite,
OwlDl,
Custom(String),
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum PipelineCondition {
MinTriples(usize),
MaxTriples(usize),
HasViolations(bool),
Custom(String),
}
pub struct ProcessingPipeline {
stages: HashMap<String, PipelineStage>,
current_stage: Option<String>,
execution_history: Vec<PipelineExecution>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PipelineExecution {
pub stage_name: String,
pub started_at: DateTime<Utc>,
pub completed_at: Option<DateTime<Utc>>,
pub result: Option<EngineResult>,
pub error: Option<String>,
pub next_stages: Vec<String>,
}
impl ProcessingPipeline {
pub fn new() -> Self {
Self {
stages: HashMap::new(),
current_stage: None,
execution_history: Vec::new(),
}
}
pub fn add_stage(&mut self, stage: PipelineStage) {
self.stages.insert(stage.name.clone(), stage);
}
pub fn set_entry_point(&mut self, stage_name: String) {
self.current_stage = Some(stage_name);
}
pub async fn execute(&mut self, store: &RdfStore) -> Result<PipelineResult, PipelineError> {
let mut results = Vec::new();
let mut current_stages = match &self.current_stage {
Some(stage) => vec![stage.clone()],
None => return Err(PipelineError::NoEntryPoint),
};
while !current_stages.is_empty() {
let mut next_stages = Vec::new();
for stage_name in current_stages {
let stage = self.stages.get(&stage_name)
.ok_or_else(|| PipelineError::StageNotFound(stage_name.clone()))?;
if !self.check_conditions(&stage.conditions, store).await {
continue;
}
let execution = self.execute_stage(&stage, store).await;
self.execution_history.push(execution.clone());
match execution.result {
Some(result) => {
results.push(result);
next_stages.extend(execution.next_stages);
}
None => {
if !stage.on_failure.is_empty() {
next_stages.extend(stage.on_failure.clone());
}
}
}
}
current_stages = next_stages;
}
Ok(PipelineResult {
stage_results: results,
execution_history: self.execution_history.clone(),
})
}
async fn execute_stage(&self, stage: &PipelineStage, store: &RdfStore) -> PipelineExecution {
let started_at = Utc::now();
let mut execution = PipelineExecution {
stage_name: stage.name.clone(),
started_at,
completed_at: None,
result: None,
error: None,
next_stages: Vec::new(),
};
let engine_result: Result<ReasoningEngine, PipelineError> = match &stage.engine {
PipelineEngine::Rdfs => {
Err(PipelineError::EngineNotImplemented("RDFS".to_string()))
}
PipelineEngine::OwlLite => {
Err(PipelineError::EngineNotImplemented("OWL Lite".to_string()))
}
PipelineEngine::OwlDl => {
Err(PipelineError::EngineNotImplemented("OWL DL".to_string()))
}
PipelineEngine::Custom(name) => {
Err(PipelineError::EngineNotImplemented(format!("Custom: {}", name)))
}
};
execution.completed_at = Some(Utc::now());
match engine_result {
Ok(engine) => {
match engine.process(store).await {
Ok(result) => {
execution.result = Some(result);
execution.next_stages = stage.on_success.clone();
}
Err(e) => {
execution.error = Some(e.to_string());
execution.next_stages = stage.on_failure.clone();
}
}
}
Err(e) => {
execution.error = Some(e.to_string());
}
}
execution
}
async fn check_conditions(&self, conditions: &[PipelineCondition], store: &RdfStore) -> bool {
for condition in conditions {
match condition {
PipelineCondition::MinTriples(min) => {
if store.statistics().total_triples < *min {
return false;
}
}
PipelineCondition::MaxTriples(max) => {
if store.statistics().total_triples > *max {
return false;
}
}
PipelineCondition::HasViolations(expected) => {
if *expected {
return false;
}
}
PipelineCondition::Custom(_) => {
return true;
}
}
}
true
}
pub fn execution_history(&self) -> &[PipelineExecution] {
&self.execution_history
}
pub fn stages(&self) -> &HashMap<String, PipelineStage> {
&self.stages
}
}
#[derive(Debug, Clone)]
pub struct PipelineResult {
pub stage_results: Vec<EngineResult>,
pub execution_history: Vec<PipelineExecution>,
}
#[derive(Debug, thiserror::Error)]
pub enum PipelineError {
#[error("No entry point defined")]
NoEntryPoint,
#[error("Stage not found: {0}")]
StageNotFound(String),
#[error("Engine not implemented: {0}")]
EngineNotImplemented(String),
#[error("Pipeline execution failed: {0}")]
ExecutionError(String),
}
impl Default for ProcessingPipeline {
fn default() -> Self {
Self::new()
}
}
pub struct PipelineBuilder {
pipeline: ProcessingPipeline,
}
impl PipelineBuilder {
pub fn new() -> Self {
Self {
pipeline: ProcessingPipeline::new(),
}
}
pub fn add_rdfs_stage(mut self, name: &str, description: &str) -> Self {
let stage = PipelineStage {
name: name.to_string(),
description: description.to_string(),
engine: PipelineEngine::Rdfs,
conditions: vec![],
on_success: vec![],
on_failure: vec![],
};
self.pipeline.add_stage(stage);
self
}
pub fn add_owl_lite_stage(mut self, name: &str, description: &str) -> Self {
let stage = PipelineStage {
name: name.to_string(),
description: description.to_string(),
engine: PipelineEngine::OwlLite,
conditions: vec![],
on_success: vec![],
on_failure: vec![],
};
self.pipeline.add_stage(stage);
self
}
pub fn add_owl_dl_stage(mut self, name: &str, description: &str) -> Self {
let stage = PipelineStage {
name: name.to_string(),
description: description.to_string(),
engine: PipelineEngine::OwlDl,
conditions: vec![],
on_success: vec![],
on_failure: vec![],
};
self.pipeline.add_stage(stage);
self
}
pub fn entry_point(mut self, stage_name: &str) -> Self {
self.pipeline.set_entry_point(stage_name.to_string());
self
}
pub fn build(self) -> ProcessingPipeline {
self.pipeline
}
}
impl Default for PipelineBuilder {
fn default() -> Self {
Self::new()
}
}