aam_core/pipeline/tasks.rs
1//! Task enums and lazy evaluation structures for Command Pattern implementation.
2//!
3//! This module defines the declarative task types that replace direct AAML mutations.
4//! Tasks are generated by Validator, Parser, and other stages, then executed by
5//! dedicated executors (`ValidateExecutor`, `ParserExecutor`, Executer).
6
7use crate::pipeline::parser::ValueNode;
8
9/// A declarative validation task generated by the Validator.
10///
11/// Validation tasks represent specific checks that need to be performed on AST nodes.
12/// They are generated during the validation phase and executed by `ValidateExecutor`.
13#[derive(Debug, Clone, PartialEq, Eq)]
14pub enum ValidationTask<'a> {
15 /// Check if a value matches a registered type's constraints.
16 ///
17 /// # Fields
18 /// - `key`: The configuration key being validated
19 /// - `value`: The value to validate
20 /// - `type_name`: Name of the type to validate against (e.g., "i32", "string", "vector2")
21 /// - `line`: Source line number for diagnostics
22 CheckTypeMatch {
23 key: std::borrow::Cow<'a, str>,
24 value: std::borrow::Cow<'a, str>,
25 type_name: std::borrow::Cow<'a, str>,
26 line: usize,
27 },
28
29 /// Verify that a schema referenced in the configuration is defined.
30 ///
31 /// # Fields
32 /// - `schema_name`: Name of the schema to check
33 /// - `line`: Source line number
34 VerifySchemaExists {
35 schema_name: std::borrow::Cow<'a, str>,
36 line: usize,
37 },
38
39 /// Verify that a file referenced by an import directive exists.
40 ///
41 /// # Fields
42 /// - `path`: Path to the file
43 /// - `line`: Source line number
44 VerifyFileExists {
45 path: std::borrow::Cow<'a, str>,
46 line: usize,
47 },
48
49 /// Validate that an assignment complies with a registered schema.
50 ///
51 /// # Fields
52 /// - `schema_name`: Name of the schema to validate against
53 /// - `key`: Key being validated
54 /// - `value`: Value being validated
55 /// - `line`: Source line number
56 ValidateAgainstSchema {
57 schema_name: std::borrow::Cow<'a, str>,
58 key: std::borrow::Cow<'a, str>,
59 value: std::borrow::Cow<'a, str>,
60 line: usize,
61 },
62
63 /// Check that all required fields of a schema are present.
64 ///
65 /// # Fields
66 /// - `schema_name`: Schema name
67 /// - `missing_fields`: Required fields not found
68 /// - `line`: Source line number
69 CheckSchemaCompleteness {
70 schema_name: std::borrow::Cow<'a, str>,
71 missing_fields: Vec<std::borrow::Cow<'a, str>>,
72 line: usize,
73 },
74
75 /// Verify no circular references in lookup chains.
76 ///
77 /// # Fields
78 /// - `key`: Key being looked up
79 /// - `line`: Source line number
80 CheckNoCircularReference {
81 key: std::borrow::Cow<'a, str>,
82 line: usize,
83 },
84
85 /// Verify that an object deriving from schemas implements all required fields.
86 ///
87 /// # Fields
88 /// - `derive_path`: Derivation path (`file.aam::Schema1::Schema2`)
89 /// - `current_key`: The context key doing the derivation
90 /// - `line`: Source line number
91 CheckDeriveCompleteness {
92 derive_path: std::borrow::Cow<'a, str>,
93 current_key: std::borrow::Cow<'a, str>,
94 line: usize,
95 },
96
97 /// Validate an inline list matches expected element types.
98 ///
99 /// # Fields
100 /// - `key`: Configuration key
101 /// - `items`: List items as strings
102 /// - `element_type`: Expected type of each element
103 /// - `line`: Source line number
104 ValidateListElements {
105 key: std::borrow::Cow<'a, str>,
106 items: std::sync::Arc<[ValueNode<'a>]>,
107 element_type: std::borrow::Cow<'a, str>,
108 line: usize,
109 },
110
111 /// Validate an inline object's keys and values.
112 ///
113 /// # Fields
114 /// - `key`: Configuration key
115 /// - `pairs`: Key-value pairs in the object
116 /// - `line`: Source line number
117 ValidateObjectStructure {
118 key: std::borrow::Cow<'a, str>,
119 pairs: std::sync::Arc<[(std::borrow::Cow<'a, str>, ValueNode<'a>)]>,
120 line: usize,
121 },
122}
123
124impl ValidationTask<'_> {
125 /// Returns the line number associated with this task for error reporting.
126 #[must_use]
127 pub const fn line(&self) -> usize {
128 match self {
129 ValidationTask::CheckTypeMatch { line, .. }
130 | ValidationTask::VerifySchemaExists { line, .. }
131 | ValidationTask::VerifyFileExists { line, .. }
132 | ValidationTask::ValidateAgainstSchema { line, .. }
133 | ValidationTask::CheckSchemaCompleteness { line, .. }
134 | ValidationTask::CheckDeriveCompleteness { line, .. }
135 | ValidationTask::CheckNoCircularReference { line, .. }
136 | ValidationTask::ValidateListElements { line, .. }
137 | ValidationTask::ValidateObjectStructure { line, .. } => *line,
138 }
139 }
140
141 /// Returns a human-readable description of this task.
142 #[must_use]
143 pub fn description(&self) -> String {
144 match self {
145 ValidationTask::CheckTypeMatch { type_name, .. } => {
146 format!("Check type match against '{type_name}'")
147 }
148 ValidationTask::VerifySchemaExists { schema_name, .. } => {
149 format!("Verify schema '{schema_name}' exists")
150 }
151 ValidationTask::VerifyFileExists { path, .. } => {
152 format!("Verify file '{path}' exists")
153 }
154 ValidationTask::ValidateAgainstSchema { schema_name, .. } => {
155 format!("Validate against schema '{schema_name}'")
156 }
157 ValidationTask::CheckSchemaCompleteness { schema_name, .. } => {
158 format!("Check completeness of schema '{schema_name}'")
159 }
160 ValidationTask::CheckDeriveCompleteness { derive_path, .. } => {
161 format!("Check derive completeness for '{derive_path}'")
162 }
163 ValidationTask::CheckNoCircularReference { .. } => {
164 "Check for circular references".to_string()
165 }
166 ValidationTask::ValidateListElements { element_type, .. } => {
167 format!("Validate list elements are type '{element_type}'")
168 }
169 ValidationTask::ValidateObjectStructure { .. } => {
170 "Validate inline object structure".to_string()
171 }
172 }
173 }
174}
175
176/// A declarative parsing task generated by the Parser.
177///
178/// Parse tasks represent operations that need to be performed on parsed modules,
179/// variables, and scopes. They enable deferred processing and analysis.
180#[derive(Debug, Clone, PartialEq, Eq)]
181pub enum ParseTask<'a> {
182 /// Resolve a module reference in a scope.
183 ///
184 /// # Fields
185 /// - `module_name`: Name of the module to resolve
186 /// - `scope`: Current scope path
187 /// - `line`: Source line number
188 ResolveModuleReference {
189 module_name: std::borrow::Cow<'a, str>,
190 scope: std::borrow::Cow<'a, str>,
191 line: usize,
192 },
193
194 /// Resolve an imported file and apply its types and schemas to the context.
195 ///
196 /// # Fields
197 /// - `derive_path`: Raw derive path (e.g. `file.aam::SchemaName`)
198 /// - `line`: Source line number
199 ResolveDeriveImport {
200 derive_path: std::borrow::Cow<'a, str>,
201 line: usize,
202 },
203
204 /// Process variable assignments in a scope.
205 ///
206 /// # Fields
207 /// - `variable_name`: Name of the variable
208 /// - `value`: Variable value
209 /// - `scope`: Scope path
210 /// - `line`: Source line number
211 ProcessVariable {
212 variable_name: std::borrow::Cow<'a, str>,
213 value: std::borrow::Cow<'a, str>,
214 scope: std::borrow::Cow<'a, str>,
215 line: usize,
216 },
217
218 /// Manage the scope lifecycle (entry/exit).
219 ///
220 /// # Fields
221 /// - `scope`: Scope path
222 /// - `is_entry`: true for scope entry, false for exit
223 /// - `line`: Source line number
224 ManageScope {
225 scope: std::borrow::Cow<'a, str>,
226 is_entry: bool,
227 line: usize,
228 },
229
230 /// Execute a directive (e.g., @import, @derive).
231 ///
232 /// # Fields
233 /// - `directive_name`: Name of the directive
234 /// - `arguments`: Directive arguments
235 /// - `line`: Source line number
236 ExecuteDirective {
237 directive_name: std::borrow::Cow<'a, str>,
238 arguments: std::borrow::Cow<'a, str>,
239 line: usize,
240 },
241
242 /// Register a type definition from @type directive.
243 ///
244 /// # Fields
245 /// - `type_name`: Name of the type being registered
246 /// - `type_spec`: Type specification
247 /// - `line`: Source line number
248 RegisterType {
249 type_name: std::borrow::Cow<'a, str>,
250 type_spec: std::borrow::Cow<'a, str>,
251 line: usize,
252 },
253
254 /// Register a schema definition from the @schema directive.
255 ///
256 /// # Fields
257 /// - `schema_name`: Name of the schema
258 /// - `fields`: Field definitions
259 /// - `line`: Source line number
260 RegisterSchema {
261 schema_name: std::borrow::Cow<'a, str>,
262 fields: std::borrow::Cow<'a, str>,
263 line: usize,
264 },
265}
266
267impl ParseTask<'_> {
268 /// Returns the line number associated with this task.
269 #[must_use]
270 pub const fn line(&self) -> usize {
271 match self {
272 ParseTask::ResolveModuleReference { line, .. }
273 | ParseTask::ResolveDeriveImport { line, .. }
274 | ParseTask::ProcessVariable { line, .. }
275 | ParseTask::ManageScope { line, .. }
276 | ParseTask::ExecuteDirective { line, .. }
277 | ParseTask::RegisterType { line, .. }
278 | ParseTask::RegisterSchema { line, .. } => *line,
279 }
280 }
281
282 /// Returns a human-readable description.
283 #[must_use]
284 pub fn description(&self) -> String {
285 match self {
286 ParseTask::ResolveModuleReference { module_name, .. } => {
287 format!("Resolve module reference '{module_name}'")
288 }
289 ParseTask::ResolveDeriveImport { derive_path, .. } => {
290 format!("Resolve derive import '{derive_path}'")
291 }
292 ParseTask::ProcessVariable { variable_name, .. } => {
293 format!("Process variable '{variable_name}'")
294 }
295 ParseTask::ManageScope {
296 scope, is_entry, ..
297 } => {
298 let action = if *is_entry { "Enter" } else { "Exit" };
299 format!("{action} scope '{scope}'")
300 }
301 ParseTask::ExecuteDirective { directive_name, .. } => {
302 format!("Execute directive '@{directive_name}'")
303 }
304 ParseTask::RegisterType { type_name, .. } => {
305 format!("Register type '{type_name}'")
306 }
307 ParseTask::RegisterSchema { schema_name, .. } => {
308 format!("Register schema '{schema_name}'")
309 }
310 }
311 }
312
313 /// Performs checks that do not require mutable execution context.
314 ///
315 /// # Errors
316 ///
317 /// Returns `Err(TaskError)` when the task is invalid in a stateless context
318 /// (for example, executing a non-whitelisted directive without execution
319 /// context).
320 pub fn validate_stateless(&self) -> Result<(), TaskError> {
321 if let ParseTask::ExecuteDirective {
322 directive_name,
323 line,
324 ..
325 } = self
326 && !matches!(directive_name.as_ref(), "import" | "derive")
327 {
328 return Err(TaskError {
329 line: *line,
330 message: format!("Unknown directive: @{directive_name}"),
331 task_description: self.description(),
332 aaml_error: None,
333 });
334 }
335
336 Ok(())
337 }
338}
339
340/// A declarative execution task for the final execution phase.
341///
342/// Execution tasks represent the final operations to be performed to materialize
343/// the configuration, independent of AAML struct manipulation.
344#[derive(Debug, Clone)]
345pub enum ExecutionTask<'a> {
346 /// Set a configuration value in the output.
347 ///
348 /// # Fields
349 /// - `key`: Configuration key
350 /// - `value`: Value to set
351 /// - `line`: Source line number
352 SetValue {
353 key: std::borrow::Cow<'a, str>,
354 value: std::borrow::Cow<'a, str>,
355 line: usize,
356 },
357
358 /// Merge a value with an existing one (+=).
359 ///
360 /// # Fields
361 /// - `key`: Key to merge
362 /// - `value`: Value to merge in
363 /// - `line`: Source line number
364 MergeValue {
365 key: std::borrow::Cow<'a, str>,
366 value: std::borrow::Cow<'a, str>,
367 line: usize,
368 },
369
370 /// Apply schema validation to a subtree.
371 ///
372 /// # Fields
373 /// - `schema_name`: Schema to apply
374 /// - `root_keys`: Keys that are part of this schema
375 /// - `line`: Source line number
376 ApplySchema {
377 schema_name: std::borrow::Cow<'a, str>,
378 root_keys: Vec<std::borrow::Cow<'a, str>>,
379 line: usize,
380 },
381
382 /// Execute inheritance from a parent configuration.
383 ///
384 /// # Fields
385 /// - `parent_key`: Parent configuration key
386 /// - `child_key`: Child configuration key
387 /// - `line`: Source line number
388 ExecuteInheritance {
389 derive_path: std::borrow::Cow<'a, str>,
390 child_key: std::borrow::Cow<'a, str>,
391 line: usize,
392 },
393
394 /// Import configuration from an external file.
395 ///
396 /// # Fields
397 /// - `file_path`: Path to import
398 /// - `merge_strategy`: How to merge ("override" or "merge")
399 /// - `line`: Source line number
400 ImportFile {
401 file_path: std::borrow::Cow<'a, str>,
402 merge_strategy: std::borrow::Cow<'a, str>,
403 line: usize,
404 },
405
406 /// Resolve a reference to another key.
407 ///
408 /// # Fields
409 /// - `source_key`: Key containing the reference
410 /// - `target_key`: Key being referenced
411 /// - `line`: Source line number
412 ResolveReference {
413 source_key: std::borrow::Cow<'a, str>,
414 target_key: std::borrow::Cow<'a, str>,
415 line: usize,
416 },
417}
418
419impl ExecutionTask<'_> {
420 /// Returns the line number associated with this task.
421 #[must_use]
422 pub const fn line(&self) -> usize {
423 match self {
424 ExecutionTask::SetValue { line, .. }
425 | ExecutionTask::MergeValue { line, .. }
426 | ExecutionTask::ApplySchema { line, .. }
427 | ExecutionTask::ExecuteInheritance { line, .. }
428 | ExecutionTask::ImportFile { line, .. }
429 | ExecutionTask::ResolveReference { line, .. } => *line,
430 }
431 }
432
433 /// Returns a human-readable description.
434 #[must_use]
435 pub fn description(&self) -> String {
436 match self {
437 ExecutionTask::SetValue { key, .. } => format!("Set value for key '{key}'"),
438 ExecutionTask::MergeValue { key, .. } => format!("Merge value for key '{key}'"),
439 ExecutionTask::ApplySchema { schema_name, .. } => {
440 format!("Apply schema '{schema_name}'")
441 }
442 ExecutionTask::ExecuteInheritance {
443 derive_path,
444 child_key,
445 ..
446 } => {
447 format!("Execute inheritance: '{child_key}' <- '{derive_path}'")
448 }
449 ExecutionTask::ImportFile { file_path, .. } => {
450 format!("Import file '{file_path}'")
451 }
452 ExecutionTask::ResolveReference {
453 source_key,
454 target_key,
455 ..
456 } => {
457 format!("Resolve reference: '{source_key}' -> '{target_key}'")
458 }
459 }
460 }
461}
462
463/// Result of executing a batch of tasks, with error aggregation support for LSP.
464#[derive(Debug, Clone)]
465pub struct TaskExecutionResult {
466 /// Whether all tasks executed successfully
467 pub success: bool,
468
469 /// Collected errors with line/context information
470 pub errors: Vec<TaskError>,
471
472 /// Execution statistics
473 pub stats: ExecutionStats,
474}
475
476/// A single task execution error with full diagnostic context.
477#[derive(Debug, Clone)]
478pub struct TaskError {
479 /// Associated task line number
480 pub line: usize,
481
482 /// Error message
483 pub message: String,
484
485 /// Task description for context
486 pub task_description: String,
487
488 /// AAML error details
489 pub aaml_error: Option<String>,
490}
491
492/// Execution statistics for performance monitoring.
493#[derive(Debug, Clone, Default)]
494pub struct ExecutionStats {
495 /// Total tasks executed
496 pub total_tasks: usize,
497
498 /// Successfully executed tasks
499 pub successful_tasks: usize,
500
501 /// Failed tasks
502 pub failed_tasks: usize,
503
504 /// Time spent in the validation phase (milliseconds)
505 pub validation_ms: u64,
506
507 /// Time spent in the parsing phase (milliseconds)
508 pub parsing_ms: u64,
509
510 /// Time spent in the execution phase (milliseconds)
511 pub execution_ms: u64,
512}
513
514#[cfg(test)]
515mod tests {
516 use super::*;
517 use std::borrow::Cow;
518
519 #[test]
520 fn test_validation_task_line_numbers() {
521 let task = ValidationTask::CheckTypeMatch {
522 key: Cow::from("foo".to_string()),
523 value: Cow::from("bar"),
524 type_name: Cow::from("string".to_string()),
525 line: 42,
526 };
527 assert_eq!(task.line(), 42);
528 }
529
530 #[test]
531 fn test_parse_task_description() {
532 let task = ParseTask::ExecuteDirective {
533 directive_name: Cow::from("import"),
534 arguments: Cow::from("base.aam"),
535 line: 10,
536 };
537 assert_eq!(task.description(), "Execute directive '@import'");
538 }
539
540 #[test]
541 fn test_execution_task_inheritance() {
542 let task = ExecutionTask::ExecuteInheritance {
543 derive_path: Cow::from("base.aam::Parent"),
544 child_key: Cow::from("child"),
545 line: 5,
546 };
547 assert_eq!(
548 task.description(),
549 "Execute inheritance: 'child' <- 'base.aam::Parent'"
550 );
551 }
552}