plz / pr.v
299 lines · 264 sloc · 6.91 KB · d4104e7627c1594b9de8e026959b196932f293c8
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(repo_id int, author_id int, title string, description string, head string, base string) !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: int(time.now().unix())
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) find_pull_request_by_id(pr_id int) ?PullRequest {
157 rows := sql app.db {
158 select from PullRequest where id == pr_id limit 1
159 } or { []PullRequest{} }
160 if rows.len == 0 {
161 return none
162 }
163 return rows.first()
164}
165
166fn (mut app App) find_repo_pull_requests(repo_id int, pr_status PrStatus) []PullRequest {
167 wanted := int(pr_status)
168 return sql app.db {
169 select from PullRequest where repo_id == repo_id && status == wanted order by created_at desc
170 } or { []PullRequest{} }
171}
172
173fn (mut app App) find_user_pull_requests(user_id int) []PullRequest {
174 return sql app.db {
175 select from PullRequest where author_id == user_id order by created_at desc
176 } or { []PullRequest{} }
177}
178
179fn (mut app App) get_repo_open_pr_count(repo_id int) int {
180 wanted := int(PrStatus.open)
181 return sql app.db {
182 select count from PullRequest where repo_id == repo_id && status == wanted
183 } or { 0 }
184}
185
186fn (mut app App) set_pr_status(pr_id int, new_status PrStatus) ! {
187 wanted := int(new_status)
188 sql app.db {
189 update PullRequest set status = wanted where id == pr_id
190 }!
191}
192
193fn (mut app App) set_pr_merged(pr_id int, merge_hash string) ! {
194 wanted := int(PrStatus.merged)
195 merged_at := int(time.now().unix())
196 sql app.db {
197 update PullRequest set status = wanted, merge_commit_hash = merge_hash, merged_at = merged_at
198 where id == pr_id
199 }!
200}
201
202fn (mut app App) increment_pr_comments(pr_id int) ! {
203 sql app.db {
204 update PullRequest set comments_count = comments_count + 1 where id == pr_id
205 }!
206}
207
208fn (mut app App) increment_repo_open_prs(repo_id int) ! {
209 sql app.db {
210 update Repo set nr_open_prs = nr_open_prs + 1 where id == repo_id
211 }!
212}
213
214fn (mut app App) decrement_repo_open_prs(repo_id int) ! {
215 sql app.db {
216 update Repo set nr_open_prs = nr_open_prs - 1 where id == repo_id
217 }!
218}
219
220fn (mut app App) add_pr_comment(pr_id int, author_id int, text string) ! {
221 comment := PrComment{
222 pr_id: pr_id
223 author_id: author_id
224 created_at: int(time.now().unix())
225 text: text
226 }
227 sql app.db {
228 insert comment into PrComment
229 }!
230}
231
232fn (mut app App) get_pr_comments(pr_id int) []PrComment {
233 return sql app.db {
234 select from PrComment where pr_id == pr_id order by created_at
235 } or { []PrComment{} }
236}
237
238fn (mut app App) add_pr_review(pr_id int, author_id int, state int, body string) !int {
239 review := PrReview{
240 pr_id: pr_id
241 author_id: author_id
242 state: state
243 body: body
244 created_at: int(time.now().unix())
245 }
246 sql app.db {
247 insert review into PrReview
248 }!
249 return db_last_insert_id(mut app.db)
250}
251
252fn (mut app App) get_pr_reviews(pr_id int) []PrReview {
253 return sql app.db {
254 select from PrReview where pr_id == pr_id order by created_at
255 } or { []PrReview{} }
256}
257
258fn (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) ! {
259 c := PrReviewComment{
260 pr_id: pr_id
261 author_id: author_id
262 review_id: review_id
263 file_path: file_path
264 line_number: line_number
265 side: side
266 text: text
267 created_at: int(time.now().unix())
268 }
269 sql app.db {
270 insert c into PrReviewComment
271 }!
272}
273
274fn (mut app App) get_pr_review_comments(pr_id int) []PrReviewComment {
275 return sql app.db {
276 select from PrReviewComment where pr_id == pr_id order by created_at
277 } or { []PrReviewComment{} }
278}
279
280fn (mut app App) delete_repo_pull_requests(repo_id int) ! {
281 prs := sql app.db {
282 select from PullRequest where repo_id == repo_id
283 } or { []PullRequest{} }
284 for pr in prs {
285 pr_id := pr.id
286 sql app.db {
287 delete from PrComment where pr_id == pr_id
288 }!
289 sql app.db {
290 delete from PrReview where pr_id == pr_id
291 }!
292 sql app.db {
293 delete from PrReviewComment where pr_id == pr_id
294 }!
295 }
296 sql app.db {
297 delete from PullRequest where repo_id == repo_id
298 }!
299}
300