use crate::dag::cache::CacheStrategy;
use crate::dag::context::DagContext;
use crate::dag::error::{DagError, Result};
use crate::dag::executor::{DagExecutor, DagMonitor};
use crate::dag::graph::DagGraph;
use crate::dag::pool::{UnlimitedPool, WorkerPool};
use crate::dag::types::{DagTask, ErrorPolicy, TaskId};
pub struct DagBuilder {
graph: DagGraph,
context: DagContext,
error_policy: ErrorPolicy,
pool: Option<Box<dyn WorkerPool>>,
global_cache: Option<Box<dyn CacheStrategy>>,
monitor: Option<Box<dyn DagMonitor>>,
}
impl DagBuilder {
pub fn new() -> Self {
Self {
graph: DagGraph::new(),
context: DagContext::new(),
error_policy: ErrorPolicy::default(),
pool: None,
global_cache: None,
monitor: None,
}
}
pub fn add_task(&mut self, id: impl Into<TaskId>, task: impl DagTask) -> &mut Self {
let _ = self.graph.add_task(id.into(), Box::new(task));
self
}
pub fn add_dep(
&mut self,
dependent: impl Into<TaskId>,
dependency: impl Into<TaskId>,
) -> &mut Self {
let _ = self.graph.add_dep(&dependent.into(), &dependency.into());
self
}
pub fn concurrency_group(&mut self, name: &str, max_concurrent: usize) -> &mut Self {
self.graph.set_concurrency_group(name, max_concurrent);
self
}
pub fn assign_group(&mut self, task_id: impl Into<TaskId>, group: &str) -> &mut Self {
self.graph.assign_group(&task_id.into(), group);
self
}
pub fn error_policy(&mut self, policy: ErrorPolicy) -> &mut Self {
self.error_policy = policy;
self
}
pub fn pool(&mut self, pool: impl WorkerPool + 'static) -> &mut Self {
self.pool = Some(Box::new(pool));
self
}
pub fn context(&mut self, ctx: DagContext) -> &mut Self {
self.context = ctx;
self
}
pub fn cache(&mut self, cache: impl CacheStrategy + 'static) -> &mut Self {
self.global_cache = Some(Box::new(cache));
self
}
pub fn monitor(&mut self, monitor: impl DagMonitor + 'static) -> &mut Self {
self.monitor = Some(Box::new(monitor));
self
}
pub fn build(self) -> Result<DagExecutor> {
self.graph.validate()?;
Ok(DagExecutor {
graph: self.graph,
context: self.context,
error_policy: self.error_policy,
pool: self.pool.unwrap_or_else(|| Box::new(UnlimitedPool)),
global_cache: self.global_cache,
monitor: self.monitor,
})
}
}
impl Default for DagBuilder {
fn default() -> Self {
Self::new()
}
}
struct StageDef {
name: String,
concurrency_limit: Option<usize>,
factory: Box<dyn Fn(&str) -> Box<dyn DagTask>>,
}
pub struct StageBuilder {
resources: Vec<String>,
stages: Vec<StageDef>,
context: DagContext,
error_policy: ErrorPolicy,
pool: Option<Box<dyn WorkerPool>>,
global_cache: Option<Box<dyn CacheStrategy>>,
monitor: Option<Box<dyn DagMonitor>>,
}
impl StageBuilder {
pub fn new() -> Self {
Self {
resources: Vec::new(),
stages: Vec::new(),
context: DagContext::new(),
error_policy: ErrorPolicy::default(),
pool: None,
global_cache: None,
monitor: None,
}
}
pub fn resources(mut self, resources: Vec<String>) -> Self {
self.resources = resources;
self
}
pub fn stage<F>(mut self, name: &str, concurrency_limit: Option<usize>, factory: F) -> Self
where
F: Fn(&str) -> Box<dyn DagTask> + 'static,
{
self.stages.push(StageDef {
name: name.to_string(),
concurrency_limit,
factory: Box::new(factory),
});
self
}
pub fn error_policy(mut self, policy: ErrorPolicy) -> Self {
self.error_policy = policy;
self
}
pub fn context(mut self, ctx: DagContext) -> Self {
self.context = ctx;
self
}
pub fn pool(mut self, pool: impl WorkerPool + 'static) -> Self {
self.pool = Some(Box::new(pool));
self
}
pub fn cache(mut self, cache: impl CacheStrategy + 'static) -> Self {
self.global_cache = Some(Box::new(cache));
self
}
pub fn monitor(mut self, monitor: impl DagMonitor + 'static) -> Self {
self.monitor = Some(Box::new(monitor));
self
}
pub fn build(self) -> Result<DagExecutor> {
if self.resources.is_empty() {
return Err(DagError::EmptyDag);
}
if self.stages.is_empty() {
return Err(DagError::EmptyDag);
}
let mut graph = DagGraph::new();
for stage in &self.stages {
if let Some(limit) = stage.concurrency_limit {
graph.set_concurrency_group(&stage.name, limit);
}
}
for resource in &self.resources {
let mut prev_id: Option<TaskId> = None;
for stage in &self.stages {
let task_id = TaskId(format!("{}:{}", stage.name, resource));
let task = (stage.factory)(resource);
graph.add_task(task_id.clone(), task)?;
graph.assign_group(&task_id, &stage.name);
if let Some(ref prev) = prev_id {
graph.add_dep(&task_id, prev)?;
}
prev_id = Some(task_id);
}
}
graph.validate()?;
Ok(DagExecutor {
graph,
context: self.context,
error_policy: self.error_policy,
pool: self.pool.unwrap_or_else(|| Box::new(UnlimitedPool)),
global_cache: self.global_cache,
monitor: self.monitor,
})
}
}
impl Default for StageBuilder {
fn default() -> Self {
Self::new()
}
}