Skip to main content

devboy_github/
types.rs

1//! GitHub API response types.
2//!
3//! These types represent the raw JSON responses from GitHub API.
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 GitHubUser {
14    pub id: u64,
15    pub login: String,
16    #[serde(default)]
17    pub name: Option<String>,
18    #[serde(default)]
19    pub email: Option<String>,
20    #[serde(default)]
21    pub avatar_url: Option<String>,
22}
23
24// =============================================================================
25// Issue
26// =============================================================================
27
28#[derive(Debug, Clone, Serialize, Deserialize)]
29pub struct GitHubIssue {
30    pub id: u64,
31    pub number: u64,
32    pub title: String,
33    #[serde(default)]
34    pub body: Option<String>,
35    pub state: String,
36    pub html_url: String,
37    #[serde(default)]
38    pub user: Option<GitHubUser>,
39    #[serde(default)]
40    pub assignees: Vec<GitHubUser>,
41    #[serde(default)]
42    pub labels: Vec<GitHubLabel>,
43    pub created_at: String,
44    pub updated_at: String,
45    #[serde(default)]
46    pub closed_at: Option<String>,
47    /// PRs are also returned by /issues endpoint, this field distinguishes them
48    #[serde(default)]
49    pub pull_request: Option<serde_json::Value>,
50}
51
52#[derive(Debug, Clone, Serialize, Deserialize)]
53pub struct GitHubLabel {
54    pub id: u64,
55    pub name: String,
56    #[serde(default)]
57    pub color: Option<String>,
58    #[serde(default)]
59    pub description: Option<String>,
60}
61
62// =============================================================================
63// Pull Request
64// =============================================================================
65
66#[derive(Debug, Clone, Serialize, Deserialize)]
67pub struct GitHubPullRequest {
68    pub id: u64,
69    pub number: u64,
70    pub title: String,
71    #[serde(default)]
72    pub body: Option<String>,
73    pub state: String,
74    pub html_url: String,
75    #[serde(default)]
76    pub draft: bool,
77    #[serde(default)]
78    pub merged: bool,
79    #[serde(default)]
80    pub merged_at: Option<String>,
81    #[serde(default)]
82    pub user: Option<GitHubUser>,
83    #[serde(default)]
84    pub assignees: Vec<GitHubUser>,
85    #[serde(default)]
86    pub requested_reviewers: Vec<GitHubUser>,
87    #[serde(default)]
88    pub labels: Vec<GitHubLabel>,
89    pub head: GitHubBranchRef,
90    pub base: GitHubBranchRef,
91    pub created_at: String,
92    pub updated_at: String,
93}
94
95#[derive(Debug, Clone, Serialize, Deserialize)]
96pub struct GitHubBranchRef {
97    #[serde(rename = "ref")]
98    pub ref_name: String,
99    pub sha: String,
100}
101
102// =============================================================================
103// Comments
104// =============================================================================
105
106/// GitHub issue/PR comment (general comments, not code review).
107#[derive(Debug, Clone, Serialize, Deserialize)]
108pub struct GitHubComment {
109    pub id: u64,
110    pub body: String,
111    #[serde(default)]
112    pub user: Option<GitHubUser>,
113    pub created_at: String,
114    #[serde(default)]
115    pub updated_at: Option<String>,
116}
117
118/// GitHub review comment (code review comment).
119#[derive(Debug, Clone, Serialize, Deserialize)]
120pub struct GitHubReviewComment {
121    pub id: u64,
122    pub body: String,
123    #[serde(default)]
124    pub user: Option<GitHubUser>,
125    pub created_at: String,
126    #[serde(default)]
127    pub updated_at: Option<String>,
128    pub path: String,
129    /// Line number (can be null for outdated comments)
130    #[serde(default)]
131    pub line: Option<u32>,
132    /// Original line (for moved/changed lines)
133    #[serde(default)]
134    pub original_line: Option<u32>,
135    /// Position in diff (deprecated but still used)
136    #[serde(default)]
137    pub position: Option<u32>,
138    /// Side: LEFT (old) or RIGHT (new)
139    #[serde(default)]
140    pub side: Option<String>,
141    #[serde(default)]
142    pub diff_hunk: Option<String>,
143    /// Commit SHA
144    #[serde(default)]
145    pub commit_id: Option<String>,
146    /// Original commit SHA
147    #[serde(default)]
148    pub original_commit_id: Option<String>,
149    #[serde(default)]
150    pub in_reply_to_id: Option<u64>,
151}
152
153// =============================================================================
154// Reviews
155// =============================================================================
156
157#[derive(Debug, Clone, Serialize, Deserialize)]
158pub struct GitHubReview {
159    pub id: u64,
160    #[serde(default)]
161    pub user: Option<GitHubUser>,
162    #[serde(default)]
163    pub body: Option<String>,
164    /// APPROVED, CHANGES_REQUESTED, COMMENTED, PENDING, DISMISSED
165    pub state: String,
166    #[serde(default)]
167    pub submitted_at: Option<String>,
168}
169
170// =============================================================================
171// Files (Diffs)
172// =============================================================================
173
174/// GitHub pull request file (diff).
175#[derive(Debug, Clone, Serialize, Deserialize)]
176pub struct GitHubFile {
177    pub sha: String,
178    pub filename: String,
179    /// added, removed, modified, renamed, copied, changed, unchanged
180    pub status: String,
181    pub additions: u32,
182    pub deletions: u32,
183    pub changes: u32,
184    #[serde(default)]
185    pub patch: Option<String>,
186    #[serde(default)]
187    pub previous_filename: Option<String>,
188}
189
190// =============================================================================
191// Create/Update types
192// =============================================================================
193
194#[derive(Debug, Clone, Serialize)]
195pub struct CreateIssueRequest {
196    pub title: String,
197    #[serde(skip_serializing_if = "Option::is_none")]
198    pub body: Option<String>,
199    #[serde(skip_serializing_if = "Vec::is_empty")]
200    pub labels: Vec<String>,
201    #[serde(skip_serializing_if = "Vec::is_empty")]
202    pub assignees: Vec<String>,
203}
204
205#[derive(Debug, Clone, Serialize, Default)]
206pub struct UpdateIssueRequest {
207    #[serde(skip_serializing_if = "Option::is_none")]
208    pub title: Option<String>,
209    #[serde(skip_serializing_if = "Option::is_none")]
210    pub body: Option<String>,
211    #[serde(skip_serializing_if = "Option::is_none")]
212    pub state: Option<String>,
213    #[serde(skip_serializing_if = "Option::is_none")]
214    pub labels: Option<Vec<String>>,
215    #[serde(skip_serializing_if = "Option::is_none")]
216    pub assignees: Option<Vec<String>>,
217}
218
219#[derive(Debug, Clone, Serialize)]
220pub struct CreatePullRequestRequest {
221    pub title: String,
222    #[serde(skip_serializing_if = "Option::is_none")]
223    pub body: Option<String>,
224    /// Source branch (head)
225    pub head: String,
226    /// Target branch (base)
227    pub base: String,
228    #[serde(skip_serializing_if = "Option::is_none")]
229    pub draft: Option<bool>,
230}
231
232#[derive(Debug, Clone, Serialize, Default)]
233pub struct UpdatePullRequestRequest {
234    #[serde(skip_serializing_if = "Option::is_none")]
235    pub title: Option<String>,
236    #[serde(skip_serializing_if = "Option::is_none")]
237    pub body: Option<String>,
238    #[serde(skip_serializing_if = "Option::is_none")]
239    pub state: Option<String>,
240    #[serde(skip_serializing_if = "Option::is_none")]
241    pub draft: Option<bool>,
242}
243
244#[derive(Debug, Clone, Serialize)]
245pub struct CreateCommentRequest {
246    pub body: String,
247}
248
249#[derive(Debug, Clone, Serialize)]
250pub struct CreateReviewCommentRequest {
251    pub body: String,
252    pub commit_id: String,
253    pub path: String,
254    #[serde(skip_serializing_if = "Option::is_none")]
255    pub line: Option<u32>,
256    #[serde(skip_serializing_if = "Option::is_none")]
257    pub side: Option<String>,
258    #[serde(skip_serializing_if = "Option::is_none")]
259    pub in_reply_to: Option<u64>,
260}