Skip to main content

devboy_gitlab/
types.rs

1//! GitLab API response and request types.
2//!
3//! These types represent the raw JSON responses from GitLab REST API v4.
4//! They are deserialized and then mapped to unified types.
5
6use serde::{Deserialize, Serialize};
7
8// =============================================================================
9// User
10// =============================================================================
11
12#[derive(Debug, Clone, Serialize, Deserialize)]
13pub struct GitLabUser {
14    pub id: u64,
15    pub username: String,
16    #[serde(default)]
17    pub name: Option<String>,
18    #[serde(default)]
19    pub avatar_url: Option<String>,
20    #[serde(default)]
21    pub web_url: Option<String>,
22}
23
24// =============================================================================
25// Issue
26// =============================================================================
27
28#[derive(Debug, Clone, Serialize, Deserialize)]
29pub struct GitLabIssue {
30    pub id: u64,
31    pub iid: u64,
32    pub title: String,
33    #[serde(default)]
34    pub description: Option<String>,
35    pub state: String,
36    #[serde(default)]
37    pub labels: Vec<String>,
38    #[serde(default)]
39    pub author: Option<GitLabUser>,
40    #[serde(default)]
41    pub assignees: Vec<GitLabUser>,
42    pub web_url: String,
43    pub created_at: String,
44    pub updated_at: String,
45}
46
47// =============================================================================
48// Merge Request
49// =============================================================================
50
51#[derive(Debug, Clone, Serialize, Deserialize)]
52pub struct GitLabMergeRequest {
53    pub id: u64,
54    pub iid: u64,
55    pub title: String,
56    #[serde(default)]
57    pub description: Option<String>,
58    pub state: String,
59    pub source_branch: String,
60    pub target_branch: String,
61    #[serde(default)]
62    pub author: Option<GitLabUser>,
63    #[serde(default)]
64    pub assignees: Vec<GitLabUser>,
65    #[serde(default)]
66    pub reviewers: Vec<GitLabUser>,
67    #[serde(default)]
68    pub labels: Vec<String>,
69    #[serde(default)]
70    pub draft: bool,
71    #[serde(default)]
72    pub work_in_progress: bool,
73    #[serde(default)]
74    pub merged_at: Option<String>,
75    pub web_url: String,
76    #[serde(default)]
77    pub sha: Option<String>,
78    #[serde(default)]
79    pub diff_refs: Option<GitLabDiffRefs>,
80    pub created_at: String,
81    pub updated_at: String,
82}
83
84/// GitLab diff refs (SHA references for code positions).
85#[derive(Debug, Clone, Serialize, Deserialize)]
86pub struct GitLabDiffRefs {
87    pub base_sha: String,
88    pub head_sha: String,
89    pub start_sha: String,
90}
91
92// =============================================================================
93// Notes and Discussions
94// =============================================================================
95
96#[derive(Debug, Clone, Serialize, Deserialize)]
97pub struct GitLabNote {
98    pub id: u64,
99    pub body: String,
100    #[serde(default)]
101    pub author: Option<GitLabUser>,
102    pub created_at: String,
103    #[serde(default)]
104    pub updated_at: Option<String>,
105    #[serde(default)]
106    pub system: bool,
107    #[serde(default)]
108    pub resolvable: bool,
109    #[serde(default)]
110    pub resolved: bool,
111    #[serde(default)]
112    pub resolved_by: Option<GitLabUser>,
113    #[serde(default)]
114    pub position: Option<GitLabNotePosition>,
115}
116
117/// GitLab discussion (thread of notes).
118#[derive(Debug, Clone, Serialize, Deserialize)]
119pub struct GitLabDiscussion {
120    pub id: String,
121    #[serde(default)]
122    pub notes: Vec<GitLabNote>,
123}
124
125/// GitLab note position (for inline code comments).
126#[derive(Debug, Clone, Serialize, Deserialize)]
127pub struct GitLabNotePosition {
128    pub position_type: String,
129    #[serde(default)]
130    pub new_path: Option<String>,
131    #[serde(default)]
132    pub old_path: Option<String>,
133    #[serde(default)]
134    pub new_line: Option<u32>,
135    #[serde(default)]
136    pub old_line: Option<u32>,
137}
138
139// =============================================================================
140// Diffs
141// =============================================================================
142
143#[derive(Debug, Clone, Serialize, Deserialize)]
144pub struct GitLabDiff {
145    pub old_path: String,
146    pub new_path: String,
147    #[serde(default)]
148    pub new_file: bool,
149    #[serde(default)]
150    pub renamed_file: bool,
151    #[serde(default)]
152    pub deleted_file: bool,
153    #[serde(default)]
154    pub diff: String,
155}
156
157/// GitLab MR changes response (MR + diffs in one call).
158#[derive(Debug, Clone, Serialize, Deserialize)]
159pub struct GitLabMergeRequestChanges {
160    #[serde(default)]
161    pub changes: Vec<GitLabDiff>,
162}
163
164// =============================================================================
165// Request types
166// =============================================================================
167
168#[derive(Debug, Clone, Serialize)]
169pub struct CreateIssueRequest {
170    pub title: String,
171    #[serde(skip_serializing_if = "Option::is_none")]
172    pub description: Option<String>,
173    /// GitLab expects comma-separated string for labels
174    #[serde(skip_serializing_if = "Option::is_none")]
175    pub labels: Option<String>,
176    #[serde(skip_serializing_if = "Option::is_none")]
177    pub assignee_ids: Option<Vec<u64>>,
178}
179
180#[derive(Debug, Clone, Serialize, Default)]
181pub struct UpdateIssueRequest {
182    #[serde(skip_serializing_if = "Option::is_none")]
183    pub title: Option<String>,
184    #[serde(skip_serializing_if = "Option::is_none")]
185    pub description: Option<String>,
186    /// GitLab uses state_event: "close" or "reopen"
187    #[serde(skip_serializing_if = "Option::is_none")]
188    pub state_event: Option<String>,
189    /// GitLab expects comma-separated string for labels
190    #[serde(skip_serializing_if = "Option::is_none")]
191    pub labels: Option<String>,
192    #[serde(skip_serializing_if = "Option::is_none")]
193    pub assignee_ids: Option<Vec<u64>>,
194}
195
196#[derive(Debug, Clone, Serialize)]
197pub struct UpdateMergeRequestRequest {
198    #[serde(skip_serializing_if = "Option::is_none")]
199    pub title: Option<String>,
200    #[serde(skip_serializing_if = "Option::is_none")]
201    pub description: Option<String>,
202    /// GitLab uses state_event: "close" or "reopen"
203    #[serde(skip_serializing_if = "Option::is_none")]
204    pub state_event: Option<String>,
205    /// Comma-separated labels
206    #[serde(skip_serializing_if = "Option::is_none")]
207    pub labels: Option<String>,
208}
209
210#[derive(Debug, Clone, Serialize)]
211pub struct CreateMergeRequestRequest {
212    pub source_branch: String,
213    pub target_branch: String,
214    pub title: String,
215    #[serde(skip_serializing_if = "Option::is_none")]
216    pub description: Option<String>,
217    /// GitLab expects comma-separated string for labels
218    #[serde(skip_serializing_if = "Option::is_none")]
219    pub labels: Option<String>,
220    #[serde(skip_serializing_if = "Option::is_none")]
221    pub reviewer_ids: Option<Vec<u64>>,
222}
223
224#[derive(Debug, Clone, Serialize)]
225pub struct CreateNoteRequest {
226    pub body: String,
227}
228
229#[derive(Debug, Clone, Serialize)]
230pub struct CreateDiscussionRequest {
231    pub body: String,
232    #[serde(skip_serializing_if = "Option::is_none")]
233    pub position: Option<DiscussionPosition>,
234}
235
236/// Position object for creating inline discussion on MR.
237#[derive(Debug, Clone, Serialize)]
238pub struct DiscussionPosition {
239    pub position_type: String,
240    pub base_sha: String,
241    pub start_sha: String,
242    pub head_sha: String,
243    #[serde(skip_serializing_if = "Option::is_none")]
244    pub new_path: Option<String>,
245    #[serde(skip_serializing_if = "Option::is_none")]
246    pub old_path: Option<String>,
247    #[serde(skip_serializing_if = "Option::is_none")]
248    pub new_line: Option<u32>,
249    #[serde(skip_serializing_if = "Option::is_none")]
250    pub old_line: Option<u32>,
251}