gitlyx / pr.v
323 lines · 284 sloc · 7.84 KB · f01d3f77ed35a21882e0e1e97310a13b0a9dfd8b
Raw
1// Copyright (c) 2019-2026 Alexander Medvednikov. All rights reserved.
2// Use of this source code is governed by a GPL license that can be found in the LICENSE file.
3module main
4
5import time
6import veb
7
8enum PrStatus {
9 open = 0
10 closed = 1
11 merged = 2
12}
13
14struct PullRequest {
15 id int @[primary; sql: serial]
16mut:
17 repo_id int
18 author_id int
19 title string
20 description string
21 head_branch string
22 base_branch string
23 status int
24 comments_count int
25 created_at int
26 merged_at int
27 merge_commit_hash string
28 repo_author string @[skip]
29 repo_name string @[skip]
30}
31
32struct PrComment {
33 id int @[primary; sql: serial]
34mut:
35 pr_id int
36 author_id int
37 created_at int
38 text string
39}
40
41struct PrReview {
42 id int @[primary; sql: serial]
43mut:
44 pr_id int
45 author_id int
46 state int // 0 comment, 1 approved, 2 changes requested
47 body string
48 created_at int
49}
50
51struct PrReviewComment {
52 id int @[primary; sql: serial]
53mut:
54 pr_id int
55 author_id int
56 review_id int // 0 if standalone (not part of a submitted review)
57 file_path string
58 line_number int
59 side string // 'old' or 'new'
60 text string
61 created_at int
62}
63
64fn (p &PullRequest) is_open() bool {
65 return p.status == int(PrStatus.open)
66}
67
68fn (p &PullRequest) is_merged() bool {
69 return p.status == int(PrStatus.merged)
70}
71
72fn (p &PullRequest) is_closed() bool {
73 return p.status == int(PrStatus.closed)
74}
75
76fn (p &PullRequest) status_label() string {
77 return match unsafe { PrStatus(p.status) } {
78 .open { 'Open' }
79 .closed { 'Closed' }
80 .merged { 'Merged' }
81 }
82}
83
84fn (p &PullRequest) status_class() string {
85 return match unsafe { PrStatus(p.status) } {
86 .open { 'pr-status--open' }
87 .closed { 'pr-status--closed' }
88 .merged { 'pr-status--merged' }
89 }
90}
91
92fn (p &PullRequest) relative_time() string {
93 return time.unix(p.created_at).relative()
94}
95
96fn (p &PullRequest) formatted_title() veb.RawHtml {
97 parts := p.title.split('`')
98 mut out := ''
99 for idx, part in parts {
100 if idx % 2 == 0 {
101 out += html_escape_text(part)
102 } else if idx == parts.len - 1 {
103 out += '`' + html_escape_text(part)
104 } else {
105 out += '<code>' + html_escape_text(part) + '</code>'
106 }
107 }
108 return out
109}
110
111fn (c &PrComment) relative() string {
112 return time.unix(c.created_at).relative()
113}
114
115fn (r &PrReview) relative() string {
116 return time.unix(r.created_at).relative()
117}
118
119fn (r &PrReview) state_label() string {
120 return match r.state {
121 1 { 'approved' }
122 2 { 'requested changes' }
123 else { 'commented' }
124 }
125}
126
127fn (r &PrReview) state_class() string {
128 return match r.state {
129 1 { 'pr-review--approved' }
130 2 { 'pr-review--changes' }
131 else { 'pr-review--comment' }
132 }
133}
134
135fn (rc &PrReviewComment) relative() string {
136 return time.unix(rc.created_at).relative()
137}
138
139fn (mut app App) add_pull_request_with_created_at(repo_id int, author_id int, title string, description string, head string, base string, created_at int) !int {
140 pr := PullRequest{
141 repo_id: repo_id
142 author_id: author_id
143 title: title
144 description: description
145 head_branch: head
146 base_branch: base
147 status: int(PrStatus.open)
148 created_at: created_at
149 }
150 sql app.db {
151 insert pr into PullRequest
152 }!
153 return db_last_insert_id(mut app.db)
154}
155
156fn (mut app App) add_pull_request(repo_id int, author_id int, title string, description string, head string, base string) !int {
157 return app.add_pull_request_with_created_at(repo_id, author_id, title, description, head, base,
158 int(time.now().unix()))
159}
160
161fn (mut app App) add_imported_pull_request(repo_id int, author_id int, title string, description string, head string, base string, created_at int) !int {
162 return app.add_pull_request_with_created_at(repo_id, author_id, title, description, head, base,
163 created_at)
164}
165
166fn (mut app App) pull_request_exists_for_head(repo_id int, head string) bool {
167 rows := sql app.db {
168 select count from PullRequest where repo_id == repo_id && head_branch == head
169 } or { 0 }
170 return rows > 0
171}
172
173fn (mut app App) find_pull_request_by_id(pr_id int) ?PullRequest {
174 rows := sql app.db {
175 select from PullRequest where id == pr_id limit 1
176 } or { []PullRequest{} }
177 if rows.len == 0 {
178 return none
179 }
180 return rows.first()
181}
182
183fn (mut app App) find_repo_pull_requests(repo_id int, pr_status PrStatus) []PullRequest {
184 wanted := int(pr_status)
185 return sql app.db {
186 select from PullRequest where repo_id == repo_id && status == wanted order by created_at desc
187 } or { []PullRequest{} }
188}
189
190fn (mut app App) find_user_pull_requests(user_id int) []PullRequest {
191 return sql app.db {
192 select from PullRequest where author_id == user_id order by created_at desc
193 } or { []PullRequest{} }
194}
195
196fn (mut app App) get_repo_open_pr_count(repo_id int) int {
197 wanted := int(PrStatus.open)
198 return sql app.db {
199 select count from PullRequest where repo_id == repo_id && status == wanted
200 } or { 0 }
201}
202
203fn (mut app App) sync_repo_open_pr_count(repo_id int) ! {
204 open_prs_count := app.get_repo_open_pr_count(repo_id)
205 sql app.db {
206 update Repo set nr_open_prs = open_prs_count where id == repo_id
207 }!
208}
209
210fn (mut app App) set_pr_status(pr_id int, new_status PrStatus) ! {
211 wanted := int(new_status)
212 sql app.db {
213 update PullRequest set status = wanted where id == pr_id
214 }!
215}
216
217fn (mut app App) set_pr_merged(pr_id int, merge_hash string) ! {
218 wanted := int(PrStatus.merged)
219 merged_at := int(time.now().unix())
220 sql app.db {
221 update PullRequest set status = wanted, merge_commit_hash = merge_hash, merged_at = merged_at
222 where id == pr_id
223 }!
224}
225
226fn (mut app App) increment_pr_comments(pr_id int) ! {
227 sql app.db {
228 update PullRequest set comments_count = comments_count + 1 where id == pr_id
229 }!
230}
231
232fn (mut app App) increment_repo_open_prs(repo_id int) ! {
233 sql app.db {
234 update Repo set nr_open_prs = nr_open_prs + 1 where id == repo_id
235 }!
236}
237
238fn (mut app App) decrement_repo_open_prs(repo_id int) ! {
239 sql app.db {
240 update Repo set nr_open_prs = nr_open_prs - 1 where id == repo_id
241 }!
242}
243
244fn (mut app App) add_pr_comment(pr_id int, author_id int, text string) ! {
245 comment := PrComment{
246 pr_id: pr_id
247 author_id: author_id
248 created_at: int(time.now().unix())
249 text: text
250 }
251 sql app.db {
252 insert comment into PrComment
253 }!
254}
255
256fn (mut app App) get_pr_comments(pr_id int) []PrComment {
257 return sql app.db {
258 select from PrComment where pr_id == pr_id order by created_at
259 } or { []PrComment{} }
260}
261
262fn (mut app App) add_pr_review(pr_id int, author_id int, state int, body string) !int {
263 review := PrReview{
264 pr_id: pr_id
265 author_id: author_id
266 state: state
267 body: body
268 created_at: int(time.now().unix())
269 }
270 sql app.db {
271 insert review into PrReview
272 }!
273 return db_last_insert_id(mut app.db)
274}
275
276fn (mut app App) get_pr_reviews(pr_id int) []PrReview {
277 return sql app.db {
278 select from PrReview where pr_id == pr_id order by created_at
279 } or { []PrReview{} }
280}
281
282fn (mut app App) add_pr_review_comment(pr_id int, author_id int, review_id int, file_path string, line_number int, side string, text string) ! {
283 c := PrReviewComment{
284 pr_id: pr_id
285 author_id: author_id
286 review_id: review_id
287 file_path: file_path
288 line_number: line_number
289 side: side
290 text: text
291 created_at: int(time.now().unix())
292 }
293 sql app.db {
294 insert c into PrReviewComment
295 }!
296}
297
298fn (mut app App) get_pr_review_comments(pr_id int) []PrReviewComment {
299 return sql app.db {
300 select from PrReviewComment where pr_id == pr_id order by created_at
301 } or { []PrReviewComment{} }
302}
303
304fn (mut app App) delete_repo_pull_requests(repo_id int) ! {
305 prs := sql app.db {
306 select from PullRequest where repo_id == repo_id
307 } or { []PullRequest{} }
308 for pr in prs {
309 pr_id := pr.id
310 sql app.db {
311 delete from PrComment where pr_id == pr_id
312 }!
313 sql app.db {
314 delete from PrReview where pr_id == pr_id
315 }!
316 sql app.db {
317 delete from PrReviewComment where pr_id == pr_id
318 }!
319 }
320 sql app.db {
321 delete from PullRequest where repo_id == repo_id
322 }!
323}
324