use crate::error::types::SupervisorError;
use crate::spec::child::TaskKind;
use crate::task::factory::TaskFactory;
use std::collections::BTreeMap;
use std::fmt::{Debug, Formatter};
use std::sync::Arc;
#[derive(Clone)]
pub struct TaskFactoryDescriptor {
pub key: String,
pub title: String,
pub description: String,
pub allowed_kinds: Vec<TaskKind>,
pub factory: Arc<dyn TaskFactory>,
}
impl TaskFactoryDescriptor {
pub fn new(
key: impl Into<String>,
title: impl Into<String>,
description: impl Into<String>,
allowed_kinds: impl IntoIterator<Item = TaskKind>,
factory: Arc<dyn TaskFactory>,
) -> Self {
Self {
key: key.into(),
title: title.into(),
description: description.into(),
allowed_kinds: allowed_kinds.into_iter().collect(),
factory,
}
}
}
impl Debug for TaskFactoryDescriptor {
fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result {
formatter
.debug_struct("TaskFactoryDescriptor")
.field("key", &self.key)
.field("title", &self.title)
.field("description", &self.description)
.field("allowed_kinds", &self.allowed_kinds)
.finish()
}
}
#[derive(Clone, Default)]
pub struct TaskFactoryRegistry {
entries: BTreeMap<String, TaskFactoryDescriptor>,
}
impl Debug for TaskFactoryRegistry {
fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result {
formatter
.debug_struct("TaskFactoryRegistry")
.field("keys", &self.keys())
.finish()
}
}
impl TaskFactoryRegistry {
pub fn new() -> Self {
Self::default()
}
pub fn register(&mut self, descriptor: TaskFactoryDescriptor) -> Result<(), SupervisorError> {
if !is_valid_factory_key(&descriptor.key) {
return Err(SupervisorError::fatal_config(format!(
"task factory key '{}' must match ^[a-zA-Z_][a-zA-Z0-9_-]*$",
descriptor.key
)));
}
if descriptor.allowed_kinds.is_empty() {
return Err(SupervisorError::fatal_config(format!(
"task factory key '{}' must allow at least one task kind",
descriptor.key
)));
}
if self.entries.contains_key(&descriptor.key) {
return Err(SupervisorError::fatal_config(format!(
"duplicate task factory key '{}'",
descriptor.key
)));
}
self.entries.insert(descriptor.key.clone(), descriptor);
Ok(())
}
pub fn resolve(
&self,
key: &str,
kind: TaskKind,
) -> Result<Arc<dyn TaskFactory>, SupervisorError> {
let descriptor = self.entries.get(key).ok_or_else(|| {
SupervisorError::fatal_config(format!("unknown task factory key '{key}'"))
})?;
if !descriptor.allowed_kinds.contains(&kind) {
return Err(SupervisorError::fatal_config(format!(
"task factory key '{key}' does not support task kind {kind:?}"
)));
}
Ok(descriptor.factory.clone())
}
pub fn descriptors(&self) -> Vec<&TaskFactoryDescriptor> {
self.entries.values().collect()
}
pub fn keys(&self) -> Vec<String> {
self.entries.keys().cloned().collect()
}
pub fn is_empty(&self) -> bool {
self.entries.is_empty()
}
}
pub fn is_valid_factory_key(key: &str) -> bool {
if key.is_empty() {
return false;
}
let first = key.chars().next().unwrap();
if !first.is_ascii_alphabetic() && first != '_' {
return false;
}
key.chars()
.all(|character| character.is_ascii_alphanumeric() || character == '_' || character == '-')
}