use std::ops::Range;
use crate::errors::BntoError;
use crate::graph::{Flow, GraphError, LevelGraph, level_flow};
use crate::pipeline::{
IterationMode, PipelineDefinition, PipelineFile, PipelineNode, is_container_node,
};
use crate::pipeline_edge::PipelineEdge;
use super::auto_iteration::run_auto_iteration;
use super::closing::ChainFailure;
use super::flow_closing::close_graph;
use super::routed::Routed;
use super::segments::{as_chain, segments};
use super::{ChainPosition, NodeExecutionResult, PipelineContext, run_node_chain};
pub(super) fn root_flow(definition: &PipelineDefinition) -> Result<Flow<'_>, BntoError> {
let flow = level_flow_of(&definition.nodes, &definition.edges, None)?;
check_containers(&definition.nodes)?;
Ok(flow)
}
pub(super) fn level_flow_of<'a>(
nodes: &'a [PipelineNode],
edges: &'a [PipelineEdge],
container: Option<&str>,
) -> Result<Flow<'a>, BntoError> {
level_flow(nodes, edges).map_err(|error| refused(container, error))
}
fn check_containers(nodes: &[PipelineNode]) -> Result<(), BntoError> {
for node in nodes.iter().filter(|n| is_container_node(&n.node_type)) {
if let Some(children) = node.children.as_deref() {
level_flow_of(children, &node.edges, Some(&node.id))?;
check_containers(children)?;
}
}
Ok(())
}
fn refused(container: Option<&str>, error: GraphError) -> BntoError {
BntoError::InvalidInput(match container {
None => format!("Invalid edges: {error}"),
Some(id) => format!("Invalid edges in container '{id}': {error}"),
})
}
pub(super) fn run_level<F: Fn() -> u64 + Copy>(
ctx: &PipelineContext<F>,
flow: &Flow<'_>,
files: Vec<PipelineFile>,
file_offset: usize,
mode: IterationMode,
) -> Result<NodeExecutionResult, ChainFailure> {
match flow {
Flow::Sequence(nodes) => run_chain(ctx, nodes, files, file_offset, mode),
Flow::Graph(graph) => match as_chain(graph) {
Some(nodes) => run_chain(ctx, nodes, files, file_offset, mode),
None => run_graph(ctx, graph, files, file_offset, mode),
},
}
}
fn run_chain<F: Fn() -> u64 + Copy>(
ctx: &PipelineContext<F>,
nodes: &[&PipelineNode],
files: Vec<PipelineFile>,
file_offset: usize,
mode: IterationMode,
) -> Result<NodeExecutionResult, ChainFailure> {
let position = ChainPosition::whole(nodes.len());
run_segment(ctx, nodes, files, file_offset, position, mode)
}
fn run_segment<F: Fn() -> u64 + Copy>(
ctx: &PipelineContext<F>,
nodes: &[&PipelineNode],
files: Vec<PipelineFile>,
file_offset: usize,
position: ChainPosition,
mode: IterationMode,
) -> Result<NodeExecutionResult, ChainFailure> {
match mode {
IterationMode::Explicit => run_node_chain(ctx, nodes, files, file_offset, position),
IterationMode::Auto => run_auto_iteration(ctx, nodes, files, position),
}
}
#[expect(clippy::too_many_lines)]
fn run_graph<'a, F: Fn() -> u64 + Copy>(
ctx: &PipelineContext<F>,
graph: &LevelGraph<'a>,
files: Vec<PipelineFile>,
file_offset: usize,
mode: IterationMode,
) -> Result<NodeExecutionResult, ChainFailure> {
let mut level_ctx = ctx.layer();
let mut routed = Routed::new(graph, files);
let mut level = NodeExecutionResult::holding(Vec::new());
for range in segments(graph) {
let end = range.end;
let last: &'a PipelineNode = graph.order[end - 1];
let last = last.id.as_str();
let result =
match run_graph_segment(&level_ctx, graph, &mut routed, range, file_offset, mode) {
Ok(result) => result,
Err(failure) => {
let ctx = &mut level_ctx;
return Err(close_graph(
ctx,
graph,
&mut routed,
failure,
end,
file_offset,
));
}
};
let records = level.then(result);
level_ctx.node_outputs.add(records, ctx.count_keys);
routed.hand_over(last, std::mem::take(&mut level.output_files));
}
level.output_files = routed.take(&graph.outputs);
level.node_outputs = level_ctx.node_outputs.into_own();
Ok(level)
}
fn run_graph_segment<'a, F: Fn() -> u64 + Copy>(
ctx: &PipelineContext<F>,
graph: &LevelGraph<'a>,
routed: &mut Routed<'a>,
range: Range<usize>,
file_offset: usize,
mode: IterationMode,
) -> Result<NodeExecutionResult, ChainFailure> {
let position = ChainPosition::starting_at(range.start, graph.order.len());
let nodes = &graph.order[range];
let files = routed.take(&graph.inputs[nodes[0].id.as_str()]);
run_segment(ctx, nodes, files, file_offset, position, mode)
}