lmrc_gitlab/gitlab_client.rs
1use crate::api::jobs::{JobBuilder, JobListBuilder};
2use crate::api::pipelines::{PipelineBuilder, PipelineListBuilder};
3use crate::api::projects::ProjectBuilder;
4use crate::api::variables::VariableBuilder;
5use crate::error::{GitLabError, Result};
6use gitlab::Gitlab;
7
8/// The main client for interacting with the GitLab API.
9///
10/// This client provides access to all GitLab API operations including
11/// pipelines, jobs, runners, merge requests, and more.
12///
13/// # Examples
14///
15/// ```no_run
16/// use lmrc_gitlab::{GitLabClient, error::Result};
17///
18/// #[tokio::main]
19/// async fn main() -> Result<()> {
20/// let client = GitLabClient::new("https://gitlab.com", "your-token")?;
21///
22/// // List pipelines
23/// let pipelines = client.pipelines("myproject").list().await?;
24///
25/// // Get specific pipeline
26/// let pipeline = client.pipeline("myproject", 123).get().await?;
27///
28/// Ok(())
29/// }
30/// ```
31pub struct GitLabClient {
32 client: Gitlab,
33}
34
35impl GitLabClient {
36 /// Creates a new GitLab client with the specified URL and personal access token.
37 ///
38 /// # Arguments
39 ///
40 /// * `url` - The GitLab instance URL (e.g., "<https://gitlab.com>")
41 /// * `token` - A GitLab personal access token with appropriate permissions
42 ///
43 /// # Errors
44 ///
45 /// Returns [`GitLabError::Config`] if the URL is invalid or the token is malformed.
46 /// Returns [`GitLabError::Authentication`] if the credentials are invalid.
47 ///
48 /// # Examples
49 ///
50 /// ```no_run
51 /// use lmrc_gitlab::GitLabClient;
52 ///
53 /// let client = GitLabClient::new("https://gitlab.com", "glpat-xxxxxxxxxxxx")?;
54 /// # Ok::<(), lmrc_gitlab::GitLabError>(())
55 /// ```
56 pub fn new(url: &str, token: &str) -> Result<Self> {
57 if url.is_empty() {
58 return Err(GitLabError::config("GitLab URL cannot be empty"));
59 }
60
61 if token.is_empty() {
62 return Err(GitLabError::config("GitLab token cannot be empty"));
63 }
64
65 let client = Gitlab::new(url, token)
66 .map_err(|e| GitLabError::config(format!("Failed to create GitLab client: {}", e)))?;
67
68 Ok(Self { client })
69 }
70
71 /// Returns a reference to the underlying GitLab API client.
72 ///
73 /// This provides direct access to the `gitlab` crate's client for advanced use cases
74 /// not covered by this library's API.
75 ///
76 /// # Examples
77 ///
78 /// ```no_run
79 /// use lmrc_gitlab::GitLabClient;
80 ///
81 /// let client = GitLabClient::new("https://gitlab.com", "token")?;
82 /// let raw_client = client.client();
83 /// # Ok::<(), lmrc_gitlab::GitLabError>(())
84 /// ```
85 pub fn client(&self) -> &Gitlab {
86 &self.client
87 }
88
89 /// Creates a builder for listing pipelines in a project.
90 ///
91 /// # Arguments
92 ///
93 /// * `project` - The project path (e.g., "myorg/myproject") or ID
94 ///
95 /// # Examples
96 ///
97 /// ```no_run
98 /// use lmrc_gitlab::{GitLabClient, models::PipelineStatus};
99 ///
100 /// # async fn example() -> Result<(), lmrc_gitlab::GitLabError> {
101 /// let client = GitLabClient::new("https://gitlab.com", "token")?;
102 ///
103 /// // List all pipelines
104 /// let all = client.pipelines("myorg/myproject").list().await?;
105 ///
106 /// // List failed pipelines on main branch
107 /// let failed = client.pipelines("myorg/myproject")
108 /// .status(PipelineStatus::Failed)
109 /// .ref_name("main")
110 /// .limit(10)
111 /// .list()
112 /// .await?;
113 /// # Ok(())
114 /// # }
115 /// ```
116 pub fn pipelines(&self, project: impl Into<String>) -> PipelineListBuilder<'_> {
117 PipelineListBuilder::new(&self.client, project)
118 }
119
120 /// Creates a builder for operations on a specific pipeline.
121 ///
122 /// # Arguments
123 ///
124 /// * `project` - The project path (e.g., "myorg/myproject") or ID
125 /// * `pipeline_id` - The pipeline ID
126 ///
127 /// # Examples
128 ///
129 /// ```no_run
130 /// use lmrc_gitlab::GitLabClient;
131 ///
132 /// # async fn example() -> Result<(), lmrc_gitlab::GitLabError> {
133 /// let client = GitLabClient::new("https://gitlab.com", "token")?;
134 ///
135 /// // Get pipeline details
136 /// let pipeline = client.pipeline("myorg/myproject", 12345).get().await?;
137 ///
138 /// // Retry a failed pipeline
139 /// client.pipeline("myorg/myproject", 12345).retry().await?;
140 ///
141 /// // Cancel a running pipeline
142 /// client.pipeline("myorg/myproject", 12345).cancel().await?;
143 /// # Ok(())
144 /// # }
145 /// ```
146 pub fn pipeline(&self, project: impl Into<String>, pipeline_id: u64) -> PipelineBuilder<'_> {
147 PipelineBuilder::new(&self.client, project, pipeline_id)
148 }
149
150 /// Creates a builder for listing jobs in a project or pipeline.
151 ///
152 /// # Arguments
153 ///
154 /// * `project` - The project path (e.g., "myorg/myproject") or ID
155 ///
156 /// # Examples
157 ///
158 /// ```no_run
159 /// use lmrc_gitlab::{GitLabClient, models::JobStatus};
160 ///
161 /// # async fn example() -> Result<(), lmrc_gitlab::GitLabError> {
162 /// let client = GitLabClient::new("https://gitlab.com", "token")?;
163 ///
164 /// // List jobs in a specific pipeline
165 /// let jobs = client.jobs("myorg/myproject")
166 /// .pipeline(12345)
167 /// .list()
168 /// .await?;
169 ///
170 /// // List failed jobs
171 /// let failed = client.jobs("myorg/myproject")
172 /// .pipeline(12345)
173 /// .status(JobStatus::Failed)
174 /// .list()
175 /// .await?;
176 /// # Ok(())
177 /// # }
178 /// ```
179 pub fn jobs(&self, project: impl Into<String>) -> JobListBuilder<'_> {
180 JobListBuilder::new(&self.client, project)
181 }
182
183 /// Creates a builder for operations on a specific job.
184 ///
185 /// # Arguments
186 ///
187 /// * `project` - The project path (e.g., "myorg/myproject") or ID
188 /// * `job_id` - The job ID
189 ///
190 /// # Examples
191 ///
192 /// ```no_run
193 /// use lmrc_gitlab::GitLabClient;
194 ///
195 /// # async fn example() -> Result<(), lmrc_gitlab::GitLabError> {
196 /// let client = GitLabClient::new("https://gitlab.com", "token")?;
197 ///
198 /// // Get job details
199 /// let job = client.job("myorg/myproject", 67890).get().await?;
200 ///
201 /// // Retry a failed job
202 /// client.job("myorg/myproject", 67890).retry().await?;
203 ///
204 /// // Get job logs
205 /// let logs = client.job("myorg/myproject", 67890).logs().await?;
206 /// # Ok(())
207 /// # }
208 /// ```
209 pub fn job(&self, project: impl Into<String>, job_id: u64) -> JobBuilder<'_> {
210 JobBuilder::new(&self.client, project, job_id)
211 }
212
213 /// Creates a builder for project-level operations.
214 ///
215 /// Use this for operations at the project scope, such as creating pipelines.
216 ///
217 /// # Arguments
218 ///
219 /// * `project` - The project path (e.g., "myorg/myproject") or ID
220 ///
221 /// # Examples
222 ///
223 /// ```no_run
224 /// use lmrc_gitlab::GitLabClient;
225 ///
226 /// # async fn example() -> Result<(), lmrc_gitlab::GitLabError> {
227 /// let client = GitLabClient::new("https://gitlab.com", "token")?;
228 ///
229 /// // Trigger a new pipeline
230 /// let pipeline = client.project("myorg/myproject")
231 /// .create_pipeline()
232 /// .ref_name("main")
233 /// .variable("ENVIRONMENT", "production")
234 /// .trigger()
235 /// .await?;
236 ///
237 /// println!("Created pipeline #{}", pipeline.id);
238 /// # Ok(())
239 /// # }
240 /// ```
241 pub fn project(&self, project: impl Into<String>) -> ProjectBuilder<'_> {
242 ProjectBuilder::new(&self.client, project)
243 }
244
245 /// Creates a builder for managing project CI/CD variables.
246 ///
247 /// Use this to create, update, list, or delete CI/CD variables for a project.
248 ///
249 /// # Arguments
250 ///
251 /// * `project` - The project path (e.g., "myorg/myproject") or ID
252 ///
253 /// # Examples
254 ///
255 /// ```no_run
256 /// use lmrc_gitlab::{GitLabClient, models::VariableOptions};
257 ///
258 /// # async fn example() -> Result<(), lmrc_gitlab::GitLabError> {
259 /// let client = GitLabClient::new("https://gitlab.com", "token")?;
260 ///
261 /// // Create a new variable
262 /// let opts = VariableOptions::new()
263 /// .protected(true)
264 /// .masked(true);
265 ///
266 /// client.variables("myorg/myproject")
267 /// .create("API_KEY", "secret-value", opts)
268 /// .await?;
269 ///
270 /// // List all variables
271 /// let vars = client.variables("myorg/myproject").list().await?;
272 ///
273 /// // Update or create (upsert)
274 /// client.variables("myorg/myproject")
275 /// .set("DATABASE_URL", "postgres://...", VariableOptions::new())
276 /// .await?;
277 /// # Ok(())
278 /// # }
279 /// ```
280 pub fn variables(&self, project: impl Into<String>) -> VariableBuilder<'_> {
281 VariableBuilder::new(&self.client, project)
282 }
283}