gitlab_manager/
gitlab_client.rs

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