Skip to main content

wrkflw_evaluator/
lib.rs

1use colored::*;
2use serde_yaml::{self, Value};
3use std::fs;
4use std::path::Path;
5
6use wrkflw_models::ValidationResult;
7use wrkflw_validators::{validate_jobs, validate_triggers};
8
9pub fn evaluate_workflow_file(path: &Path, verbose: bool) -> Result<ValidationResult, String> {
10    let content = fs::read_to_string(path).map_err(|e| format!("Failed to read file: {}", e))?;
11
12    // Parse YAML content
13    let workflow: Value =
14        serde_yaml::from_str(&content).map_err(|e| format!("Invalid YAML: {}", e))?;
15
16    let mut result = ValidationResult::new();
17
18    // Check for required structure
19    if !workflow.is_mapping() {
20        result.add_issue("Workflow file is not a valid YAML mapping".to_string());
21        return Ok(result);
22    }
23
24    // Note: The 'name' field is optional per GitHub Actions specification.
25    // When omitted, GitHub displays the workflow file path relative to the repository root.
26    // We do not validate name presence as it's not required by the schema.
27
28    // Check if jobs section exists
29    match workflow.get("jobs") {
30        Some(jobs) if jobs.is_mapping() => {
31            validate_jobs(jobs, &mut result);
32        }
33        Some(_) => {
34            result.add_issue("'jobs' section is not a mapping".to_string());
35        }
36        None => {
37            result.add_issue("Workflow is missing 'jobs' section".to_string());
38        }
39    }
40
41    // Check for valid triggers
42    match workflow.get("on") {
43        Some(on) => {
44            validate_triggers(on, &mut result);
45        }
46        None => {
47            result.add_issue("Workflow is missing 'on' section (triggers)".to_string());
48        }
49    }
50
51    if verbose && result.is_valid {
52        println!(
53            "{} Validated structure of workflow: {}",
54            "✓".green(),
55            path.display()
56        );
57    }
58
59    Ok(result)
60}