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}