lmrc_gitlab/api/projects.rs
1//! Project API operations.
2//!
3//! This module provides operations at the project level, including
4//! creating pipelines, managing project settings, and more.
5
6use crate::error::{GitLabError, Result};
7use crate::models::Pipeline;
8use gitlab::Gitlab;
9use gitlab::api::{
10 Query,
11 projects::pipelines::{self, PipelineVariable},
12};
13use std::collections::HashMap;
14
15/// Builder for project-level operations.
16///
17/// Provides operations that work at the project scope rather than
18/// on specific resources.
19///
20/// # Examples
21///
22/// ```no_run
23/// use lmrc_gitlab::GitLabClient;
24///
25/// # async fn example() -> Result<(), lmrc_gitlab::GitLabError> {
26/// let client = GitLabClient::new("https://gitlab.com", "token")?;
27///
28/// // Trigger a new pipeline
29/// let pipeline = client.project("myorg/myproject")
30/// .create_pipeline()
31/// .ref_name("main")
32/// .trigger()
33/// .await?;
34/// # Ok(())
35/// # }
36/// ```
37pub struct ProjectBuilder<'a> {
38 client: &'a Gitlab,
39 project: String,
40}
41
42impl<'a> ProjectBuilder<'a> {
43 /// Creates a new project builder.
44 pub(crate) fn new(client: &'a Gitlab, project: impl Into<String>) -> Self {
45 Self {
46 client,
47 project: project.into(),
48 }
49 }
50
51 /// Create a builder for triggering a new pipeline.
52 ///
53 /// # Examples
54 ///
55 /// ```no_run
56 /// # use lmrc_gitlab::GitLabClient;
57 /// # async fn example() -> Result<(), lmrc_gitlab::GitLabError> {
58 /// # let client = GitLabClient::new("https://gitlab.com", "token")?;
59 /// let pipeline = client.project("myorg/myproject")
60 /// .create_pipeline()
61 /// .ref_name("main")
62 /// .trigger()
63 /// .await?;
64 /// # Ok(())
65 /// # }
66 /// ```
67 pub fn create_pipeline(self) -> CreatePipelineBuilder<'a> {
68 CreatePipelineBuilder::new(self.client, self.project)
69 }
70}
71
72/// Builder for creating/triggering a new pipeline.
73///
74/// # Examples
75///
76/// ```no_run
77/// use lmrc_gitlab::GitLabClient;
78/// use std::collections::HashMap;
79///
80/// # async fn example() -> Result<(), lmrc_gitlab::GitLabError> {
81/// let client = GitLabClient::new("https://gitlab.com", "token")?;
82///
83/// // Trigger pipeline with variables
84/// let mut vars = HashMap::new();
85/// vars.insert("DEPLOY_ENV".to_string(), "staging".to_string());
86///
87/// let pipeline = client.project("myorg/myproject")
88/// .create_pipeline()
89/// .ref_name("main")
90/// .variables(vars)
91/// .trigger()
92/// .await?;
93///
94/// println!("Created pipeline #{}", pipeline.id);
95/// # Ok(())
96/// # }
97/// ```
98pub struct CreatePipelineBuilder<'a> {
99 client: &'a Gitlab,
100 project: String,
101 ref_name: Option<String>,
102 variables: HashMap<String, String>,
103}
104
105impl<'a> CreatePipelineBuilder<'a> {
106 /// Creates a new pipeline creation builder.
107 pub(crate) fn new(client: &'a Gitlab, project: impl Into<String>) -> Self {
108 Self {
109 client,
110 project: project.into(),
111 ref_name: None,
112 variables: HashMap::new(),
113 }
114 }
115
116 /// Set the git reference (branch or tag) to run the pipeline on.
117 ///
118 /// # Examples
119 ///
120 /// ```no_run
121 /// # use lmrc_gitlab::GitLabClient;
122 /// # async fn example() -> Result<(), lmrc_gitlab::GitLabError> {
123 /// # let client = GitLabClient::new("https://gitlab.com", "token")?;
124 /// let pipeline = client.project("myorg/myproject")
125 /// .create_pipeline()
126 /// .ref_name("develop")
127 /// .trigger()
128 /// .await?;
129 /// # Ok(())
130 /// # }
131 /// ```
132 pub fn ref_name(mut self, ref_name: impl Into<String>) -> Self {
133 self.ref_name = Some(ref_name.into());
134 self
135 }
136
137 /// Set CI/CD variables for the pipeline.
138 ///
139 /// # Examples
140 ///
141 /// ```no_run
142 /// # use lmrc_gitlab::GitLabClient;
143 /// # use std::collections::HashMap;
144 /// # async fn example() -> Result<(), lmrc_gitlab::GitLabError> {
145 /// # let client = GitLabClient::new("https://gitlab.com", "token")?;
146 /// let mut vars = HashMap::new();
147 /// vars.insert("DEPLOY_TARGET".to_string(), "production".to_string());
148 ///
149 /// let pipeline = client.project("myorg/myproject")
150 /// .create_pipeline()
151 /// .ref_name("main")
152 /// .variables(vars)
153 /// .trigger()
154 /// .await?;
155 /// # Ok(())
156 /// # }
157 /// ```
158 pub fn variables(mut self, variables: HashMap<String, String>) -> Self {
159 self.variables = variables;
160 self
161 }
162
163 /// Add a single CI/CD variable for the pipeline.
164 ///
165 /// # Examples
166 ///
167 /// ```no_run
168 /// # use lmrc_gitlab::GitLabClient;
169 /// # async fn example() -> Result<(), lmrc_gitlab::GitLabError> {
170 /// # let client = GitLabClient::new("https://gitlab.com", "token")?;
171 /// let pipeline = client.project("myorg/myproject")
172 /// .create_pipeline()
173 /// .ref_name("main")
174 /// .variable("ENVIRONMENT", "staging")
175 /// .variable("DEBUG", "true")
176 /// .trigger()
177 /// .await?;
178 /// # Ok(())
179 /// # }
180 /// ```
181 pub fn variable(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
182 self.variables.insert(key.into(), value.into());
183 self
184 }
185
186 /// Trigger the pipeline.
187 ///
188 /// # Errors
189 ///
190 /// Returns [`GitLabError::InvalidInput`] if ref_name is not set.
191 /// Returns an error if the pipeline cannot be created.
192 ///
193 /// # Examples
194 ///
195 /// ```no_run
196 /// # use lmrc_gitlab::GitLabClient;
197 /// # async fn example() -> Result<(), lmrc_gitlab::GitLabError> {
198 /// # let client = GitLabClient::new("https://gitlab.com", "token")?;
199 /// let pipeline = client.project("myorg/myproject")
200 /// .create_pipeline()
201 /// .ref_name("main")
202 /// .trigger()
203 /// .await?;
204 /// # Ok(())
205 /// # }
206 /// ```
207 pub async fn trigger(self) -> Result<Pipeline> {
208 let ref_name = self
209 .ref_name
210 .ok_or_else(|| GitLabError::invalid_input("ref", "ref_name is required"))?;
211
212 let mut endpoint_builder = pipelines::CreatePipeline::builder();
213 endpoint_builder.project(&self.project).ref_(&ref_name);
214
215 // Add variables if provided
216 for (key, value) in self.variables {
217 let var = PipelineVariable::builder()
218 .key(key)
219 .value(value)
220 .build()
221 .map_err(|e| {
222 GitLabError::api(format!("Failed to build pipeline variable: {}", e))
223 })?;
224 endpoint_builder.variable(var);
225 }
226
227 let endpoint = endpoint_builder.build().map_err(|e| {
228 GitLabError::api(format!("Failed to build create pipeline endpoint: {}", e))
229 })?;
230
231 endpoint
232 .query(self.client)
233 .map_err(|e| GitLabError::api(format!("Failed to create pipeline: {}", e)))
234 }
235}
236
237#[cfg(test)]
238mod tests {
239 use super::*;
240
241 #[test]
242 fn test_project_builder() {
243 fn _compile_test(client: &Gitlab) {
244 let _builder = ProjectBuilder::new(client, "project");
245 }
246 }
247
248 #[test]
249 fn test_create_pipeline_builder() {
250 fn _compile_test(client: &Gitlab) {
251 let _builder = CreatePipelineBuilder::new(client, "project")
252 .ref_name("main")
253 .variable("KEY", "value");
254 }
255 }
256}