Skip to main content

wrkflw_parser/
schema.rs

1use jsonschema::JSONSchema;
2use serde_json::Value;
3use std::fs;
4use std::path::Path;
5
6const GITHUB_WORKFLOW_SCHEMA: &str = include_str!("github-workflow.json");
7const GITLAB_CI_SCHEMA: &str = include_str!("gitlab-ci.json");
8
9#[derive(Debug, Clone, Copy)]
10pub enum SchemaType {
11    GitHub,
12    GitLab,
13}
14
15pub struct SchemaValidator {
16    github_schema: JSONSchema,
17    gitlab_schema: JSONSchema,
18}
19
20impl SchemaValidator {
21    pub fn new() -> Result<Self, String> {
22        let github_schema_json: Value = serde_json::from_str(GITHUB_WORKFLOW_SCHEMA)
23            .map_err(|e| format!("Failed to parse GitHub workflow schema: {}", e))?;
24
25        let gitlab_schema_json: Value = serde_json::from_str(GITLAB_CI_SCHEMA)
26            .map_err(|e| format!("Failed to parse GitLab CI schema: {}", e))?;
27
28        let github_schema = JSONSchema::compile(&github_schema_json)
29            .map_err(|e| format!("Failed to compile GitHub JSON schema: {}", e))?;
30
31        let gitlab_schema = JSONSchema::compile(&gitlab_schema_json)
32            .map_err(|e| format!("Failed to compile GitLab JSON schema: {}", e))?;
33
34        Ok(Self {
35            github_schema,
36            gitlab_schema,
37        })
38    }
39
40    pub fn validate_workflow(&self, workflow_path: &Path) -> Result<(), String> {
41        // Determine the schema type based on the filename
42        let schema_type = if workflow_path.file_name().is_some_and(|name| {
43            let name_str = name.to_string_lossy();
44            name_str.ends_with(".gitlab-ci.yml") || name_str.ends_with(".gitlab-ci.yaml")
45        }) {
46            SchemaType::GitLab
47        } else {
48            SchemaType::GitHub
49        };
50
51        // Read the workflow file
52        let content = fs::read_to_string(workflow_path)
53            .map_err(|e| format!("Failed to read workflow file: {}", e))?;
54
55        // Parse YAML to JSON Value
56        let workflow_json: Value = serde_yaml::from_str(&content)
57            .map_err(|e| format!("Failed to parse workflow YAML: {}", e))?;
58
59        // Validate against the appropriate schema
60        let validation_result = match schema_type {
61            SchemaType::GitHub => self.github_schema.validate(&workflow_json),
62            SchemaType::GitLab => self.gitlab_schema.validate(&workflow_json),
63        };
64
65        // Handle validation errors
66        if let Err(errors) = validation_result {
67            let schema_name = match schema_type {
68                SchemaType::GitHub => "GitHub workflow",
69                SchemaType::GitLab => "GitLab CI",
70            };
71            let mut error_msg = format!("{} validation failed:\n", schema_name);
72            for error in errors {
73                error_msg.push_str(&format!("- {}\n", error));
74            }
75            return Err(error_msg);
76        }
77
78        Ok(())
79    }
80
81    pub fn validate_with_specific_schema(
82        &self,
83        content: &str,
84        schema_type: SchemaType,
85    ) -> Result<(), String> {
86        // Parse YAML to JSON Value
87        let workflow_json: Value =
88            serde_yaml::from_str(content).map_err(|e| format!("Failed to parse YAML: {}", e))?;
89
90        // Validate against the appropriate schema
91        let validation_result = match schema_type {
92            SchemaType::GitHub => self.github_schema.validate(&workflow_json),
93            SchemaType::GitLab => self.gitlab_schema.validate(&workflow_json),
94        };
95
96        // Handle validation errors
97        if let Err(errors) = validation_result {
98            let schema_name = match schema_type {
99                SchemaType::GitHub => "GitHub workflow",
100                SchemaType::GitLab => "GitLab CI",
101            };
102            let mut error_msg = format!("{} validation failed:\n", schema_name);
103            for error in errors {
104                error_msg.push_str(&format!("- {}\n", error));
105            }
106            return Err(error_msg);
107        }
108
109        Ok(())
110    }
111}