1use serde::{Deserialize, Serialize};
2
3#[derive(Debug, Deserialize)]
4pub struct User {
5 pub uuid: Option<String>,
6 pub display_name: Option<String>,
7 pub nickname: Option<String>,
8}
9
10#[derive(Debug, Deserialize)]
11pub struct BranchName {
12 pub name: Option<String>,
13}
14
15#[derive(Debug, Deserialize)]
16pub struct Endpoint {
17 pub branch: Option<BranchName>,
18}
19
20#[derive(Debug, Deserialize)]
21pub struct Link {
22 pub href: Option<String>,
23}
24
25#[derive(Debug, Deserialize)]
26pub struct Links {
27 pub html: Option<Link>,
28}
29
30#[derive(Debug, Deserialize)]
31pub struct Participant {
32 pub user: Option<User>,
33 pub state: Option<String>,
34}
35
36#[derive(Debug, Deserialize)]
37pub struct PullRequest {
38 pub id: u64,
39 pub title: Option<String>,
40 pub state: Option<String>,
41 pub author: Option<User>,
42 pub source: Option<Endpoint>,
43 pub destination: Option<Endpoint>,
44 pub links: Option<Links>,
45 #[serde(default)]
46 pub reviewers: Vec<User>,
47 #[serde(default)]
48 pub participants: Vec<Participant>,
49}
50
51impl PullRequest {
52 pub fn source_branch(&self) -> &str {
53 self.source
54 .as_ref()
55 .and_then(|e| e.branch.as_ref())
56 .and_then(|b| b.name.as_deref())
57 .unwrap_or("-")
58 }
59
60 pub fn destination_branch(&self) -> &str {
61 self.destination
62 .as_ref()
63 .and_then(|e| e.branch.as_ref())
64 .and_then(|b| b.name.as_deref())
65 .unwrap_or("-")
66 }
67
68 pub fn html_url(&self) -> &str {
69 self.links
70 .as_ref()
71 .and_then(|l| l.html.as_ref())
72 .and_then(|l| l.href.as_deref())
73 .unwrap_or("-")
74 }
75
76 pub fn author_name(&self) -> &str {
77 self.author
78 .as_ref()
79 .and_then(|a| a.nickname.as_deref().or(a.display_name.as_deref()))
80 .unwrap_or("-")
81 }
82}
83
84#[derive(Debug, Deserialize)]
85pub struct CommentContent {
86 pub raw: Option<String>,
87}
88
89#[derive(Debug, Deserialize)]
90pub struct Inline {
91 pub path: Option<String>,
92 pub from: Option<u64>,
93 pub to: Option<u64>,
94}
95
96#[derive(Debug, Deserialize)]
97pub struct Comment {
98 pub id: u64,
99 pub content: Option<CommentContent>,
100 pub user: Option<User>,
101 pub created_on: Option<String>,
102 pub inline: Option<Inline>,
103 #[serde(default)]
104 pub deleted: bool,
105 pub resolution: Option<serde_json::Value>,
107}
108
109impl Comment {
110 pub fn is_inline(&self) -> bool {
111 self.inline
112 .as_ref()
113 .and_then(|i| i.path.as_deref())
114 .is_some_and(|p| !p.is_empty())
115 }
116
117 pub fn is_resolved(&self) -> bool {
118 self.resolution.is_some()
119 }
120
121 pub fn body(&self) -> String {
122 if self.deleted {
123 return "[deleted]".to_string();
124 }
125 self.content
126 .as_ref()
127 .and_then(|c| c.raw.clone())
128 .unwrap_or_default()
129 }
130
131 pub fn author(&self) -> &str {
132 self.user
133 .as_ref()
134 .and_then(|u| u.display_name.as_deref())
135 .unwrap_or("Unknown")
136 }
137}
138
139#[derive(Debug, Deserialize)]
140pub struct CommitAuthor {
141 pub user: Option<User>,
142 pub raw: Option<String>,
143}
144
145#[derive(Debug, Deserialize)]
146pub struct CommitTarget {
147 pub author: Option<CommitAuthor>,
148 pub date: Option<String>,
149}
150
151#[derive(Debug, Deserialize)]
152pub struct BranchRef {
153 pub name: String,
154 pub target: Option<CommitTarget>,
155}
156
157impl BranchRef {
158 pub fn owner(&self) -> String {
159 self.target
160 .as_ref()
161 .and_then(|t| t.author.as_ref())
162 .and_then(|a| {
163 a.user
164 .as_ref()
165 .and_then(|u| u.display_name.clone())
166 .or_else(|| a.raw.clone())
167 })
168 .unwrap_or_else(|| "-".to_string())
169 }
170}
171
172#[derive(Debug, Deserialize)]
173pub struct CommitSummary {
174 pub raw: Option<String>,
175}
176
177#[derive(Debug, Deserialize)]
178pub struct Commit {
179 pub hash: Option<String>,
180 pub summary: Option<CommitSummary>,
181}
182
183#[derive(Debug, Deserialize)]
184pub struct DiffStatEntry {
185 pub status: Option<String>,
186 #[serde(rename = "new")]
187 pub new_file: Option<PathEntry>,
188 #[serde(rename = "old")]
189 pub old_file: Option<PathEntry>,
190}
191
192#[derive(Debug, Deserialize)]
193pub struct PathEntry {
194 pub path: Option<String>,
195}
196
197impl DiffStatEntry {
198 pub fn path(&self) -> &str {
199 self.new_file
200 .as_ref()
201 .and_then(|p| p.path.as_deref())
202 .or_else(|| self.old_file.as_ref().and_then(|p| p.path.as_deref()))
203 .unwrap_or("-")
204 }
205}
206
207#[derive(Debug, Serialize)]
208pub struct ReviewerRef {
209 pub uuid: String,
210}