Skip to main content

wrkflw_models/
lib.rs

1pub struct ValidationResult {
2    pub is_valid: bool,
3    pub issues: Vec<String>,
4}
5
6impl Default for ValidationResult {
7    fn default() -> Self {
8        Self::new()
9    }
10}
11
12impl ValidationResult {
13    pub fn new() -> Self {
14        ValidationResult {
15            is_valid: true,
16            issues: Vec::new(),
17        }
18    }
19
20    pub fn add_issue(&mut self, issue: String) {
21        self.is_valid = false;
22        self.issues.push(issue);
23    }
24}
25
26// GitLab pipeline models
27pub mod gitlab {
28    use serde::{Deserialize, Serialize};
29    use std::collections::HashMap;
30
31    /// Represents a GitLab CI/CD pipeline configuration
32    #[derive(Debug, Serialize, Deserialize, Clone)]
33    pub struct Pipeline {
34        /// Default image for all jobs
35        #[serde(skip_serializing_if = "Option::is_none")]
36        pub image: Option<Image>,
37
38        /// Global variables available to all jobs
39        #[serde(skip_serializing_if = "Option::is_none")]
40        pub variables: Option<HashMap<String, String>>,
41
42        /// Pipeline stages in execution order
43        #[serde(skip_serializing_if = "Option::is_none")]
44        pub stages: Option<Vec<String>>,
45
46        /// Default before_script for all jobs
47        #[serde(skip_serializing_if = "Option::is_none")]
48        pub before_script: Option<Vec<String>>,
49
50        /// Default after_script for all jobs
51        #[serde(skip_serializing_if = "Option::is_none")]
52        pub after_script: Option<Vec<String>>,
53
54        /// Job definitions (name => job)
55        #[serde(flatten)]
56        pub jobs: HashMap<String, Job>,
57
58        /// Workflow rules for the pipeline
59        #[serde(skip_serializing_if = "Option::is_none")]
60        pub workflow: Option<Workflow>,
61
62        /// Includes for pipeline configuration
63        #[serde(skip_serializing_if = "Option::is_none")]
64        pub include: Option<Vec<Include>>,
65    }
66
67    /// A job in a GitLab CI/CD pipeline
68    #[derive(Debug, Serialize, Deserialize, Clone)]
69    pub struct Job {
70        /// The stage this job belongs to
71        #[serde(skip_serializing_if = "Option::is_none")]
72        pub stage: Option<String>,
73
74        /// Docker image to use for this job
75        #[serde(skip_serializing_if = "Option::is_none")]
76        pub image: Option<Image>,
77
78        /// Script commands to run
79        #[serde(skip_serializing_if = "Option::is_none")]
80        pub script: Option<Vec<String>>,
81
82        /// Commands to run before the main script
83        #[serde(skip_serializing_if = "Option::is_none")]
84        pub before_script: Option<Vec<String>>,
85
86        /// Commands to run after the main script
87        #[serde(skip_serializing_if = "Option::is_none")]
88        pub after_script: Option<Vec<String>>,
89
90        /// When to run the job (on_success, on_failure, always, manual)
91        #[serde(skip_serializing_if = "Option::is_none")]
92        pub when: Option<String>,
93
94        /// Allow job failure
95        #[serde(skip_serializing_if = "Option::is_none")]
96        pub allow_failure: Option<bool>,
97
98        /// Services to run alongside the job
99        #[serde(skip_serializing_if = "Option::is_none")]
100        pub services: Option<Vec<Service>>,
101
102        /// Tags to define which runners can execute this job
103        #[serde(skip_serializing_if = "Option::is_none")]
104        pub tags: Option<Vec<String>>,
105
106        /// Job-specific variables
107        #[serde(skip_serializing_if = "Option::is_none")]
108        pub variables: Option<HashMap<String, String>>,
109
110        /// Job dependencies
111        #[serde(skip_serializing_if = "Option::is_none")]
112        pub dependencies: Option<Vec<String>>,
113
114        /// Artifacts to store after job execution
115        #[serde(skip_serializing_if = "Option::is_none")]
116        pub artifacts: Option<Artifacts>,
117
118        /// Cache configuration
119        #[serde(skip_serializing_if = "Option::is_none")]
120        pub cache: Option<Cache>,
121
122        /// Rules for when this job should run
123        #[serde(skip_serializing_if = "Option::is_none")]
124        pub rules: Option<Vec<Rule>>,
125
126        /// Only run on specified refs
127        #[serde(skip_serializing_if = "Option::is_none")]
128        pub only: Option<Only>,
129
130        /// Exclude specified refs
131        #[serde(skip_serializing_if = "Option::is_none")]
132        pub except: Option<Except>,
133
134        /// Retry configuration
135        #[serde(skip_serializing_if = "Option::is_none")]
136        pub retry: Option<Retry>,
137
138        /// Timeout for the job in seconds
139        #[serde(skip_serializing_if = "Option::is_none")]
140        pub timeout: Option<String>,
141
142        /// Mark job as parallel and specify instance count
143        #[serde(skip_serializing_if = "Option::is_none")]
144        pub parallel: Option<usize>,
145
146        /// Flag to indicate this is a template job
147        #[serde(skip_serializing_if = "Option::is_none")]
148        pub template: Option<bool>,
149
150        /// List of jobs this job extends from
151        #[serde(skip_serializing_if = "Option::is_none")]
152        pub extends: Option<Vec<String>>,
153    }
154
155    /// Docker image configuration
156    #[derive(Debug, Serialize, Deserialize, Clone)]
157    #[serde(untagged)]
158    pub enum Image {
159        /// Simple image name as string
160        Simple(String),
161        /// Detailed image configuration
162        Detailed {
163            /// Image name
164            name: String,
165            /// Entrypoint to override in the image
166            #[serde(skip_serializing_if = "Option::is_none")]
167            entrypoint: Option<Vec<String>>,
168        },
169    }
170
171    /// Service container to run alongside a job
172    #[derive(Debug, Serialize, Deserialize, Clone)]
173    #[serde(untagged)]
174    pub enum Service {
175        /// Simple service name as string
176        Simple(String),
177        /// Detailed service configuration
178        Detailed {
179            /// Service name/image
180            name: String,
181            /// Command to run in the service container
182            #[serde(skip_serializing_if = "Option::is_none")]
183            command: Option<Vec<String>>,
184            /// Entrypoint to override in the image
185            #[serde(skip_serializing_if = "Option::is_none")]
186            entrypoint: Option<Vec<String>>,
187        },
188    }
189
190    /// Artifacts configuration
191    #[derive(Debug, Serialize, Deserialize, Clone)]
192    pub struct Artifacts {
193        /// Paths to include as artifacts
194        #[serde(skip_serializing_if = "Option::is_none")]
195        pub paths: Option<Vec<String>>,
196        /// Artifact expiration duration
197        #[serde(skip_serializing_if = "Option::is_none")]
198        pub expire_in: Option<String>,
199        /// When to upload artifacts (on_success, on_failure, always)
200        #[serde(skip_serializing_if = "Option::is_none")]
201        pub when: Option<String>,
202    }
203
204    /// Cache configuration
205    #[derive(Debug, Serialize, Deserialize, Clone)]
206    pub struct Cache {
207        /// Cache key
208        #[serde(skip_serializing_if = "Option::is_none")]
209        pub key: Option<String>,
210        /// Paths to cache
211        #[serde(skip_serializing_if = "Option::is_none")]
212        pub paths: Option<Vec<String>>,
213        /// When to save cache (on_success, on_failure, always)
214        #[serde(skip_serializing_if = "Option::is_none")]
215        pub when: Option<String>,
216        /// Cache policy
217        #[serde(skip_serializing_if = "Option::is_none")]
218        pub policy: Option<String>,
219    }
220
221    /// Rule for conditional job execution
222    #[derive(Debug, Serialize, Deserialize, Clone)]
223    pub struct Rule {
224        /// If condition expression
225        #[serde(skip_serializing_if = "Option::is_none")]
226        pub if_: Option<String>,
227        /// When to run if condition is true
228        #[serde(skip_serializing_if = "Option::is_none")]
229        pub when: Option<String>,
230        /// Variables to set if condition is true
231        #[serde(skip_serializing_if = "Option::is_none")]
232        pub variables: Option<HashMap<String, String>>,
233    }
234
235    /// Only/except configuration
236    #[derive(Debug, Serialize, Deserialize, Clone)]
237    #[serde(untagged)]
238    pub enum Only {
239        /// Simple list of refs
240        Refs(Vec<String>),
241        /// Detailed configuration
242        Complex {
243            /// Refs to include
244            #[serde(skip_serializing_if = "Option::is_none")]
245            refs: Option<Vec<String>>,
246            /// Branch patterns to include
247            #[serde(skip_serializing_if = "Option::is_none")]
248            branches: Option<Vec<String>>,
249            /// Tags to include
250            #[serde(skip_serializing_if = "Option::is_none")]
251            tags: Option<Vec<String>>,
252            /// Pipeline types to include
253            #[serde(skip_serializing_if = "Option::is_none")]
254            variables: Option<Vec<String>>,
255            /// Changes to files that trigger the job
256            #[serde(skip_serializing_if = "Option::is_none")]
257            changes: Option<Vec<String>>,
258        },
259    }
260
261    /// Except configuration
262    #[derive(Debug, Serialize, Deserialize, Clone)]
263    #[serde(untagged)]
264    pub enum Except {
265        /// Simple list of refs
266        Refs(Vec<String>),
267        /// Detailed configuration
268        Complex {
269            /// Refs to exclude
270            #[serde(skip_serializing_if = "Option::is_none")]
271            refs: Option<Vec<String>>,
272            /// Branch patterns to exclude
273            #[serde(skip_serializing_if = "Option::is_none")]
274            branches: Option<Vec<String>>,
275            /// Tags to exclude
276            #[serde(skip_serializing_if = "Option::is_none")]
277            tags: Option<Vec<String>>,
278            /// Pipeline types to exclude
279            #[serde(skip_serializing_if = "Option::is_none")]
280            variables: Option<Vec<String>>,
281            /// Changes to files that don't trigger the job
282            #[serde(skip_serializing_if = "Option::is_none")]
283            changes: Option<Vec<String>>,
284        },
285    }
286
287    /// Workflow configuration
288    #[derive(Debug, Serialize, Deserialize, Clone)]
289    pub struct Workflow {
290        /// Rules for when to run the pipeline
291        pub rules: Vec<Rule>,
292    }
293
294    /// Retry configuration
295    #[derive(Debug, Serialize, Deserialize, Clone)]
296    #[serde(untagged)]
297    pub enum Retry {
298        /// Simple max attempts
299        MaxAttempts(u32),
300        /// Detailed retry configuration
301        Detailed {
302            /// Maximum retry attempts
303            max: u32,
304            /// When to retry
305            #[serde(skip_serializing_if = "Option::is_none")]
306            when: Option<Vec<String>>,
307        },
308    }
309
310    /// Include configuration for external pipeline files
311    #[derive(Debug, Serialize, Deserialize, Clone)]
312    #[serde(untagged)]
313    pub enum Include {
314        /// Simple string include
315        Local(String),
316        /// Detailed include configuration
317        Detailed {
318            /// Local file path
319            #[serde(skip_serializing_if = "Option::is_none")]
320            local: Option<String>,
321            /// Remote file URL
322            #[serde(skip_serializing_if = "Option::is_none")]
323            remote: Option<String>,
324            /// Include from project
325            #[serde(skip_serializing_if = "Option::is_none")]
326            project: Option<String>,
327            /// Include specific file from project
328            #[serde(skip_serializing_if = "Option::is_none")]
329            file: Option<String>,
330            /// Include template
331            #[serde(skip_serializing_if = "Option::is_none")]
332            template: Option<String>,
333            /// Ref to use when including from project
334            #[serde(skip_serializing_if = "Option::is_none")]
335            ref_: Option<String>,
336        },
337    }
338}