oxigdal_workflow/templates/
mod.rs1pub mod library;
7pub mod parameterization;
8pub mod validation;
9
10use crate::engine::WorkflowDefinition;
11use crate::error::{Result, WorkflowError};
12use chrono::{DateTime, Utc};
13use serde::{Deserialize, Serialize};
14use std::collections::HashMap;
15
16pub use library::WorkflowTemplateLibrary;
17pub use parameterization::{
18 Parameter, ParameterConstraints, ParameterType, ParameterValue, TemplateParameterizer,
19};
20pub use validation::TemplateValidator;
21
22#[derive(Debug, Clone, Serialize, Deserialize)]
24pub struct WorkflowTemplate {
25 pub id: String,
27 pub name: String,
29 pub description: String,
31 pub version: String,
33 pub author: String,
35 pub tags: Vec<String>,
37 pub parameters: Vec<Parameter>,
39 pub workflow_template: String,
41 pub metadata: TemplateMetadata,
43 pub examples: Vec<HashMap<String, ParameterValue>>,
45}
46
47#[derive(Debug, Clone, Serialize, Deserialize)]
49pub struct TemplateMetadata {
50 pub created_at: DateTime<Utc>,
52 pub updated_at: DateTime<Utc>,
54 pub category: TemplateCategory,
56 pub complexity: u8,
58 pub estimated_duration: Option<String>,
60 pub required_resources: Vec<String>,
62 pub compatible_versions: Vec<String>,
64}
65
66#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
68pub enum TemplateCategory {
69 SatelliteProcessing,
71 ChangeDetection,
73 TerrainAnalysis,
75 VectorProcessing,
77 BatchProcessing,
79 Etl,
81 QualityControl,
83 MachineLearning,
85 Custom,
87}
88
89impl WorkflowTemplate {
90 pub fn new<S: Into<String>>(id: S, name: S, description: S) -> Self {
92 Self {
93 id: id.into(),
94 name: name.into(),
95 description: description.into(),
96 version: "1.0.0".to_string(),
97 author: "unknown".to_string(),
98 tags: Vec::new(),
99 parameters: Vec::new(),
100 workflow_template: String::new(),
101 metadata: TemplateMetadata {
102 created_at: Utc::now(),
103 updated_at: Utc::now(),
104 category: TemplateCategory::Custom,
105 complexity: 1,
106 estimated_duration: None,
107 required_resources: Vec::new(),
108 compatible_versions: Vec::new(),
109 },
110 examples: Vec::new(),
111 }
112 }
113
114 pub fn add_parameter(&mut self, parameter: Parameter) {
116 self.parameters.push(parameter);
117 }
118
119 pub fn set_template<S: Into<String>>(&mut self, template: S) {
121 self.workflow_template = template.into();
122 }
123
124 pub fn set_category(&mut self, category: TemplateCategory) {
126 self.metadata.category = category;
127 }
128
129 pub fn add_tag<S: Into<String>>(&mut self, tag: S) {
131 self.tags.push(tag.into());
132 }
133
134 pub fn add_example(&mut self, example: HashMap<String, ParameterValue>) {
136 self.examples.push(example);
137 }
138
139 pub fn instantiate(
141 &self,
142 params: HashMap<String, ParameterValue>,
143 ) -> Result<WorkflowDefinition> {
144 let validator = TemplateValidator::new();
146 validator.validate_parameters(&self.parameters, ¶ms)?;
147
148 let parameterizer = TemplateParameterizer::new();
150 let workflow_json = parameterizer.apply_parameters(&self.workflow_template, ¶ms)?;
151
152 let workflow: WorkflowDefinition = serde_json::from_str(&workflow_json)
154 .map_err(|e| WorkflowError::template(format!("Failed to parse workflow: {}", e)))?;
155
156 Ok(workflow)
157 }
158
159 pub fn get_required_parameters(&self) -> Vec<&Parameter> {
161 self.parameters.iter().filter(|p| p.required).collect()
162 }
163
164 pub fn get_optional_parameters(&self) -> Vec<&Parameter> {
166 self.parameters.iter().filter(|p| !p.required).collect()
167 }
168
169 pub fn validate(&self) -> Result<()> {
171 let validator = TemplateValidator::new();
172 validator.validate_template(self)
173 }
174
175 pub fn clone_as(&self, new_id: String, new_version: String) -> Self {
177 let mut cloned = self.clone();
178 cloned.id = new_id;
179 cloned.version = new_version;
180 cloned.metadata.created_at = Utc::now();
181 cloned.metadata.updated_at = Utc::now();
182 cloned
183 }
184}
185
186pub struct TemplateBuilder {
188 template: WorkflowTemplate,
189}
190
191impl TemplateBuilder {
192 pub fn new<S1: Into<String>, S2: Into<String>>(id: S1, name: S2) -> Self {
194 Self {
195 template: WorkflowTemplate::new(id.into(), name.into(), "".to_string()),
196 }
197 }
198
199 pub fn description<S: Into<String>>(mut self, description: S) -> Self {
201 self.template.description = description.into();
202 self
203 }
204
205 pub fn version<S: Into<String>>(mut self, version: S) -> Self {
207 self.template.version = version.into();
208 self
209 }
210
211 pub fn author<S: Into<String>>(mut self, author: S) -> Self {
213 self.template.author = author.into();
214 self
215 }
216
217 pub fn tag<S: Into<String>>(mut self, tag: S) -> Self {
219 self.template.tags.push(tag.into());
220 self
221 }
222
223 pub fn parameter(mut self, parameter: Parameter) -> Self {
225 self.template.parameters.push(parameter);
226 self
227 }
228
229 pub fn template<S: Into<String>>(mut self, template: S) -> Self {
231 self.template.workflow_template = template.into();
232 self
233 }
234
235 pub fn category(mut self, category: TemplateCategory) -> Self {
237 self.template.metadata.category = category;
238 self
239 }
240
241 pub fn complexity(mut self, complexity: u8) -> Self {
243 self.template.metadata.complexity = complexity.min(5);
244 self
245 }
246
247 pub fn build(self) -> Result<WorkflowTemplate> {
249 self.template.validate()?;
250 Ok(self.template)
251 }
252}
253
254#[cfg(test)]
255mod tests {
256 use super::*;
257
258 #[test]
259 fn test_template_creation() {
260 let template = WorkflowTemplate::new("test-template", "Test Template", "A test template");
261
262 assert_eq!(template.id, "test-template");
263 assert_eq!(template.name, "Test Template");
264 }
265
266 #[test]
267 fn test_template_builder() {
268 let template = TemplateBuilder::new("test", "Test")
269 .description("Description")
270 .version("1.0.0")
271 .author("Test Author")
272 .tag("test")
273 .complexity(3)
274 .build();
275
276 assert!(template.is_err());
278 }
279
280 #[test]
281 fn test_parameter_filtering() {
282 let mut template = WorkflowTemplate::new("test", "Test", "Description");
283
284 let param1 = Parameter {
285 name: "required_param".to_string(),
286 param_type: ParameterType::String,
287 description: "A required parameter".to_string(),
288 required: true,
289 default_value: None,
290 constraints: None,
291 };
292
293 let param2 = Parameter {
294 name: "optional_param".to_string(),
295 param_type: ParameterType::String,
296 description: "An optional parameter".to_string(),
297 required: false,
298 default_value: Some(ParameterValue::String("default".to_string())),
299 constraints: None,
300 };
301
302 template.add_parameter(param1);
303 template.add_parameter(param2);
304
305 assert_eq!(template.get_required_parameters().len(), 1);
306 assert_eq!(template.get_optional_parameters().len(), 1);
307 }
308
309 #[test]
310 fn test_template_categories() {
311 assert_eq!(
312 TemplateCategory::SatelliteProcessing,
313 TemplateCategory::SatelliteProcessing
314 );
315 }
316}