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}