pub mod library;
pub mod parameterization;
pub mod validation;
use crate::engine::WorkflowDefinition;
use crate::error::{Result, WorkflowError};
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
pub use library::WorkflowTemplateLibrary;
pub use parameterization::{
Parameter, ParameterConstraints, ParameterType, ParameterValue, TemplateParameterizer,
};
pub use validation::TemplateValidator;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct WorkflowTemplate {
pub id: String,
pub name: String,
pub description: String,
pub version: String,
pub author: String,
pub tags: Vec<String>,
pub parameters: Vec<Parameter>,
pub workflow_template: String,
pub metadata: TemplateMetadata,
pub examples: Vec<HashMap<String, ParameterValue>>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TemplateMetadata {
pub created_at: DateTime<Utc>,
pub updated_at: DateTime<Utc>,
pub category: TemplateCategory,
pub complexity: u8,
pub estimated_duration: Option<String>,
pub required_resources: Vec<String>,
pub compatible_versions: Vec<String>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum TemplateCategory {
SatelliteProcessing,
ChangeDetection,
TerrainAnalysis,
VectorProcessing,
BatchProcessing,
Etl,
QualityControl,
MachineLearning,
Custom,
}
impl WorkflowTemplate {
pub fn new<S: Into<String>>(id: S, name: S, description: S) -> Self {
Self {
id: id.into(),
name: name.into(),
description: description.into(),
version: "1.0.0".to_string(),
author: "unknown".to_string(),
tags: Vec::new(),
parameters: Vec::new(),
workflow_template: String::new(),
metadata: TemplateMetadata {
created_at: Utc::now(),
updated_at: Utc::now(),
category: TemplateCategory::Custom,
complexity: 1,
estimated_duration: None,
required_resources: Vec::new(),
compatible_versions: Vec::new(),
},
examples: Vec::new(),
}
}
pub fn add_parameter(&mut self, parameter: Parameter) {
self.parameters.push(parameter);
}
pub fn set_template<S: Into<String>>(&mut self, template: S) {
self.workflow_template = template.into();
}
pub fn set_category(&mut self, category: TemplateCategory) {
self.metadata.category = category;
}
pub fn add_tag<S: Into<String>>(&mut self, tag: S) {
self.tags.push(tag.into());
}
pub fn add_example(&mut self, example: HashMap<String, ParameterValue>) {
self.examples.push(example);
}
pub fn instantiate(
&self,
params: HashMap<String, ParameterValue>,
) -> Result<WorkflowDefinition> {
let validator = TemplateValidator::new();
validator.validate_parameters(&self.parameters, ¶ms)?;
let parameterizer = TemplateParameterizer::new();
let workflow_json = parameterizer.apply_parameters(&self.workflow_template, ¶ms)?;
let workflow: WorkflowDefinition = serde_json::from_str(&workflow_json)
.map_err(|e| WorkflowError::template(format!("Failed to parse workflow: {}", e)))?;
Ok(workflow)
}
pub fn get_required_parameters(&self) -> Vec<&Parameter> {
self.parameters.iter().filter(|p| p.required).collect()
}
pub fn get_optional_parameters(&self) -> Vec<&Parameter> {
self.parameters.iter().filter(|p| !p.required).collect()
}
pub fn validate(&self) -> Result<()> {
let validator = TemplateValidator::new();
validator.validate_template(self)
}
pub fn clone_as(&self, new_id: String, new_version: String) -> Self {
let mut cloned = self.clone();
cloned.id = new_id;
cloned.version = new_version;
cloned.metadata.created_at = Utc::now();
cloned.metadata.updated_at = Utc::now();
cloned
}
}
pub struct TemplateBuilder {
template: WorkflowTemplate,
}
impl TemplateBuilder {
pub fn new<S1: Into<String>, S2: Into<String>>(id: S1, name: S2) -> Self {
Self {
template: WorkflowTemplate::new(id.into(), name.into(), "".to_string()),
}
}
pub fn description<S: Into<String>>(mut self, description: S) -> Self {
self.template.description = description.into();
self
}
pub fn version<S: Into<String>>(mut self, version: S) -> Self {
self.template.version = version.into();
self
}
pub fn author<S: Into<String>>(mut self, author: S) -> Self {
self.template.author = author.into();
self
}
pub fn tag<S: Into<String>>(mut self, tag: S) -> Self {
self.template.tags.push(tag.into());
self
}
pub fn parameter(mut self, parameter: Parameter) -> Self {
self.template.parameters.push(parameter);
self
}
pub fn template<S: Into<String>>(mut self, template: S) -> Self {
self.template.workflow_template = template.into();
self
}
pub fn category(mut self, category: TemplateCategory) -> Self {
self.template.metadata.category = category;
self
}
pub fn complexity(mut self, complexity: u8) -> Self {
self.template.metadata.complexity = complexity.min(5);
self
}
pub fn build(self) -> Result<WorkflowTemplate> {
self.template.validate()?;
Ok(self.template)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_template_creation() {
let template = WorkflowTemplate::new("test-template", "Test Template", "A test template");
assert_eq!(template.id, "test-template");
assert_eq!(template.name, "Test Template");
}
#[test]
fn test_template_builder() {
let template = TemplateBuilder::new("test", "Test")
.description("Description")
.version("1.0.0")
.author("Test Author")
.tag("test")
.complexity(3)
.build();
assert!(template.is_err());
}
#[test]
fn test_parameter_filtering() {
let mut template = WorkflowTemplate::new("test", "Test", "Description");
let param1 = Parameter {
name: "required_param".to_string(),
param_type: ParameterType::String,
description: "A required parameter".to_string(),
required: true,
default_value: None,
constraints: None,
};
let param2 = Parameter {
name: "optional_param".to_string(),
param_type: ParameterType::String,
description: "An optional parameter".to_string(),
required: false,
default_value: Some(ParameterValue::String("default".to_string())),
constraints: None,
};
template.add_parameter(param1);
template.add_parameter(param2);
assert_eq!(template.get_required_parameters().len(), 1);
assert_eq!(template.get_optional_parameters().len(), 1);
}
#[test]
fn test_template_categories() {
assert_eq!(
TemplateCategory::SatelliteProcessing,
TemplateCategory::SatelliteProcessing
);
}
}