1package db
2
3import (
4 "database/sql"
5 "time"
6
7 "github.com/bluesky-social/indigo/atproto/syntax"
8)
9
10type Pull struct {
11 ID int
12 OwnerDid string
13 RepoAt syntax.ATURI
14 PullAt syntax.ATURI
15 TargetBranch string
16 Patch string
17 PullId int
18 Title string
19 Body string
20 Open int
21 Created time.Time
22 Rkey string
23}
24
25type PullComment struct {
26 ID int
27 OwnerDid string
28 PullId int
29 RepoAt string
30 CommentId int
31 CommentAt string
32 Body string
33 Created time.Time
34}
35
36func NewPull(tx *sql.Tx, pull *Pull) error {
37 defer tx.Rollback()
38
39 _, err := tx.Exec(`
40 insert or ignore into repo_pull_seqs (repo_at, next_pull_id)
41 values (?, 1)
42 `, pull.RepoAt)
43 if err != nil {
44 return err
45 }
46
47 var nextId int
48 err = tx.QueryRow(`
49 update repo_pull_seqs
50 set next_pull_id = next_pull_id + 1
51 where repo_at = ?
52 returning next_pull_id - 1
53 `, pull.RepoAt).Scan(&nextId)
54 if err != nil {
55 return err
56 }
57
58 pull.PullId = nextId
59
60 _, err = tx.Exec(`
61 insert into pulls (repo_at, owner_did, pull_id, title, target_branch, body, patch, rkey)
62 values (?, ?, ?, ?, ?, ?, ?, ?)
63 `, pull.RepoAt, pull.OwnerDid, pull.PullId, pull.Title, pull.TargetBranch, pull.Body, pull.Patch, pull.Rkey)
64 if err != nil {
65 return err
66 }
67
68 if err := tx.Commit(); err != nil {
69 return err
70 }
71
72 return nil
73}
74
75func SetPullAt(e Execer, repoAt syntax.ATURI, pullId int, pullAt string) error {
76 _, err := e.Exec(`update pulls set pull_at = ? where repo_at = ? and pull_id = ?`, pullAt, repoAt, pullId)
77 return err
78}
79
80func GetPullAt(e Execer, repoAt syntax.ATURI, pullId int) (string, error) {
81 var pullAt string
82 err := e.QueryRow(`select pull_at from pulls where repo_at = ? and pull_id = ?`, repoAt, pullId).Scan(&pullAt)
83 return pullAt, err
84}
85
86func NextPullId(e Execer, repoAt syntax.ATURI) (int, error) {
87 var pullId int
88 err := e.QueryRow(`select next_pull_id from repo_pull_seqs where repo_at = ?`, repoAt).Scan(&pullId)
89 return pullId - 1, err
90}
91
92func GetPulls(e Execer, repoAt syntax.ATURI) ([]Pull, error) {
93 var pulls []Pull
94
95 rows, err := e.Query(`select owner_did, pull_id, created, title, open, target_branch, pull_at, body, patch, rkey from pulls where repo_at = ? order by created desc`, repoAt)
96 if err != nil {
97 return nil, err
98 }
99 defer rows.Close()
100
101 for rows.Next() {
102 var pull Pull
103 var createdAt string
104 err := rows.Scan(&pull.OwnerDid, &pull.PullId, &createdAt, &pull.Title, &pull.Open, &pull.TargetBranch, &pull.PullAt, &pull.Body, &pull.Patch, &pull.Rkey)
105 if err != nil {
106 return nil, err
107 }
108
109 createdTime, err := time.Parse(time.RFC3339, createdAt)
110 if err != nil {
111 return nil, err
112 }
113 pull.Created = createdTime
114
115 pulls = append(pulls, pull)
116 }
117
118 if err := rows.Err(); err != nil {
119 return nil, err
120 }
121
122 return pulls, nil
123}
124
125func GetPull(e Execer, repoAt syntax.ATURI, pullId int) (*Pull, error) {
126 query := `select owner_did, created, title, open, target_branch, pull_at, body, patch, rkey from pulls where repo_at = ? and pull_id = ?`
127 row := e.QueryRow(query, repoAt, pullId)
128
129 var pull Pull
130 var createdAt string
131 err := row.Scan(&pull.OwnerDid, &createdAt, &pull.Title, &pull.Open, &pull.TargetBranch, &pull.PullAt, &pull.Body, &pull.Patch, &pull.Rkey)
132 if err != nil {
133 return nil, err
134 }
135
136 createdTime, err := time.Parse(time.RFC3339, createdAt)
137 if err != nil {
138 return nil, err
139 }
140 pull.Created = createdTime
141
142 return &pull, nil
143}
144
145func GetPullWithComments(e Execer, repoAt syntax.ATURI, pullId int) (*Pull, []PullComment, error) {
146 query := `select owner_did, pull_id, created, title, open, target_branch, pull_at, body, patch, rkey from pulls where repo_at = ? and pull_id = ?`
147 row := e.QueryRow(query, repoAt, pullId)
148
149 var pull Pull
150 var createdAt string
151 err := row.Scan(&pull.OwnerDid, &pull.PullId, &createdAt, &pull.Title, &pull.Open, &pull.TargetBranch, &pull.PullAt, &pull.Body, &pull.Patch, &pull.Rkey)
152 if err != nil {
153 return nil, nil, err
154 }
155
156 createdTime, err := time.Parse(time.RFC3339, createdAt)
157 if err != nil {
158 return nil, nil, err
159 }
160 pull.Created = createdTime
161
162 comments, err := GetPullComments(e, repoAt, pullId)
163 if err != nil {
164 return nil, nil, err
165 }
166
167 return &pull, comments, nil
168}
169
170func NewPullComment(e Execer, comment *PullComment) error {
171 query := `insert into pull_comments (owner_did, repo_at, comment_at, pull_id, comment_id, body) values (?, ?, ?, ?, ?, ?)`
172 _, err := e.Exec(
173 query,
174 comment.OwnerDid,
175 comment.RepoAt,
176 comment.CommentAt,
177 comment.PullId,
178 comment.CommentId,
179 comment.Body,
180 )
181 return err
182}
183
184func GetPullComments(e Execer, repoAt syntax.ATURI, pullId int) ([]PullComment, error) {
185 var comments []PullComment
186
187 rows, err := e.Query(`select owner_did, pull_id, comment_id, comment_at, body, created from pull_comments where repo_at = ? and pull_id = ? order by created asc`, repoAt, pullId)
188 if err == sql.ErrNoRows {
189 return []PullComment{}, nil
190 }
191 if err != nil {
192 return nil, err
193 }
194 defer rows.Close()
195
196 for rows.Next() {
197 var comment PullComment
198 var createdAt string
199 err := rows.Scan(&comment.OwnerDid, &comment.PullId, &comment.CommentId, &comment.CommentAt, &comment.Body, &createdAt)
200 if err != nil {
201 return nil, err
202 }
203
204 createdAtTime, err := time.Parse(time.RFC3339, createdAt)
205 if err != nil {
206 return nil, err
207 }
208 comment.Created = createdAtTime
209
210 comments = append(comments, comment)
211 }
212
213 if err := rows.Err(); err != nil {
214 return nil, err
215 }
216
217 return comments, nil
218}
219
220func ClosePull(e Execer, repoAt syntax.ATURI, pullId int) error {
221 _, err := e.Exec(`update pulls set open = 0 where repo_at = ? and pull_id = ?`, repoAt, pullId)
222 return err
223}
224
225func ReopenPull(e Execer, repoAt syntax.ATURI, pullId int) error {
226 _, err := e.Exec(`update pulls set open = 1 where repo_at = ? and pull_id = ?`, repoAt, pullId)
227 return err
228}
229
230func MergePull(e Execer, repoAt syntax.ATURI, pullId int) error {
231 _, err := e.Exec(`update pulls set open = 2 where repo_at = ? and pull_id = ?`, repoAt, pullId)
232 return err
233}
234
235type PullCount struct {
236 Open int
237 Closed int
238}
239
240func GetPullCount(e Execer, repoAt syntax.ATURI) (PullCount, error) {
241 row := e.QueryRow(`
242 select
243 count(case when open = 1 then 1 end) as open_count,
244 count(case when open = 0 then 1 end) as closed_count
245 from pulls
246 where repo_at = ?`,
247 repoAt,
248 )
249
250 var count PullCount
251 if err := row.Scan(&count.Open, &count.Closed); err != nil {
252 return PullCount{0, 0}, err
253 }
254
255 return count, nil
256}