d23464c0 Andy McCall 1 month ago ↶
1
2
3
use super :: { now_ms, Pool} ;
4
use hub_api:: Visibility;
5
use sqlx:: FromRow;
6
e7158350 Andy McCall 24 days ago ↶
7
# [ derive ( Clone, Debug, Default, FromRow ) ]
d23464c0 Andy McCall 1 month ago ↶
8
pub struct RepoRow {
9
pub id : i64 ,
10
pub owner : String,
11
pub name : String,
12
pub description : String,
13
pub default_bookmark : String,
14
pub visibility : String,
15
pub created_ms : i64 ,
16
pub updated_ms : i64 ,
3f4b0ea6 Andy McCall 1 month ago ↶
17
#[sqlx(default)]
18
pub git_mirror : bool ,
19
#[sqlx(default)]
20
pub git_mirror_state : String,
07b1a5bb Andy McCall 1 month ago ↶
21
22
#[sqlx(default)]
23
pub forked_from : Option< i64 > ,
1553bfc9 Andy McCall 1 month ago ↶
24
25
#[sqlx(default)]
26
pub topics : String,
9008bf6c Andy McCall 1 month ago ↶
27
28
#[sqlx(default)]
29
pub flow : String,
107eaba5 Andy McCall 20 days ago ↶
30
31
32
#[sqlx(default)]
33
pub flow_priority : String,
e7158350 Andy McCall 24 days ago ↶
34
35
36
#[sqlx(default)]
37
pub diffusion : String,
f4d6ef41 Andy McCall 23 days ago ↶
38
39
40
#[sqlx(default)]
41
pub diffusion_land : String,
d3b048b2 Andy McCall 23 days ago ↶
42
43
44
#[sqlx(default)]
45
pub backup : String,
d0fbea2e Andy McCall 1 month ago ↶
46
47
#[sqlx(default)]
48
pub archived : bool ,
49
#[sqlx(default)]
50
pub archived_reason : String,
1ac84c62 Andy McCall 1 month ago ↶
51
52
#[sqlx(default)]
53
pub open_issues : i64 ,
54
#[sqlx(default)]
55
pub open_proposals : i64 ,
d23464c0 Andy McCall 1 month ago ↶
56
}
57
58
impl RepoRow {
59
pub fn visibility ( & self ) -> Visibility {
60
Visibility:: parse( & self .visibility) .unwrap_or ( Visibility:: Private)
61
}
62
1553bfc9 Andy McCall 1 month ago ↶
63
pub fn topic_list ( & self ) -> Vec< & str > {
64
self .topics.split_whitespace ( ) .collect ( )
65
}
66
d23464c0 Andy McCall 1 month ago ↶
67
pub fn slug ( & self ) -> String {
68
format! ( " {} /{} " , self .owner, self .name)
69
}
70
}
71
72
# [ derive ( Clone, Debug, FromRow ) ]
73
pub struct PushRow {
74
pub id : i64 ,
75
pub at_ms : i64 ,
76
pub reference : String,
77
pub ok : bool ,
78
pub reason : String,
62d6e969 Andy McCall 1 month ago ↶
79
pub old_state : String,
80
pub new_state : String,
81
pub heads : i64 ,
82
pub changes : i64 ,
19bb31ff Andy McCall 1 month ago ↶
83
pub pusher : String,
62d6e969 Andy McCall 1 month ago ↶
84
}
85
86
87
# [ derive ( Clone, Debug ) ]
88
pub struct PushOutcome {
89
pub reference : String,
90
pub ok : bool ,
91
pub reason : String,
92
pub old_state : String,
93
pub new_state : String,
94
pub heads : i64 ,
95
pub changes : i64 ,
d23464c0 Andy McCall 1 month ago ↶
96
}
97
98
pub async fn list ( pool : & Pool) -> sqlx:: Result< Vec< RepoRow> > {
99
sqlx:: query_as( " SELECT * FROM repositories ORDER BY updated_ms DESC, owner, name" ) .fetch_all ( pool) .await
100
}
101
19bb31ff Andy McCall 1 month ago ↶
102
pub async fn get_by_id ( pool : & Pool, id : i64 ) -> sqlx:: Result< Option< RepoRow> > {
5d90e0e0 Andy McCall 21 days ago
103
sqlx:: query_as( " SELECT * FROM repositories WHERE id = $1" ) .bind ( id) .fetch_optional ( pool) .await
19bb31ff Andy McCall 1 month ago ↶
104
}
105
d23464c0 Andy McCall 1 month ago ↶
106
pub async fn list_by_owner ( pool : & Pool, owner : & str ) -> sqlx:: Result< Vec< RepoRow> > {
5d90e0e0 Andy McCall 21 days ago
107
sqlx:: query_as( " SELECT * FROM repositories WHERE owner = $1 ORDER BY updated_ms DESC, name" ) .bind ( owner) .fetch_all ( pool) .await
d23464c0 Andy McCall 1 month ago ↶
108
}
109
1ac84c62 Andy McCall 1 month ago ↶
110
d23464c0 Andy McCall 1 month ago ↶
111
pub async fn get ( pool : & Pool, owner : & str , name : & str ) -> sqlx:: Result< Option< RepoRow> > {
1ac84c62 Andy McCall 1 month ago ↶
112
sqlx:: query_as(
113
" SELECT r.*, \
114
(SELECT COUNT(*) FROM issues i WHERE i.repo_id = r.id AND i.state = 'open') AS open_issues, \
115
(SELECT COUNT(*) FROM proposals p WHERE p.repo_id = r.id AND p.state IN ('open', 'draft')) AS open_proposals \
5d90e0e0 Andy McCall 21 days ago
116
FROM repositories r WHERE r.owner = $1 AND r.name = $2" ,
1ac84c62 Andy McCall 1 month ago ↶
117
)
118
.bind ( owner)
119
.bind ( name)
120
.fetch_optional ( pool)
121
.await
d23464c0 Andy McCall 1 month ago ↶
122
}
123
ff656953 Andy McCall 1 month ago ↶
124
125
126
d23464c0 Andy McCall 1 month ago ↶
127
pub async fn create ( pool : & Pool, owner : & str , name : & str , description : & str , visibility : Visibility) -> sqlx:: Result< RepoRow> {
128
let now = now_ms ( ) ;
129
sqlx:: query_as(
462f88d1 Andy McCall 1 month ago ↶
130
" INSERT INTO repositories (owner, name, description, visibility, created_ms, updated_ms, required_approvals) \
3b1bc27f Andy McCall 21 days ago
131
VALUES ($1, $2, $3, $4, $5, $6, (SELECT CASE WHEN COUNT(*) >= 2 THEN 1 ELSE 0 END FROM org_members m JOIN organisations o ON o.id = m.org_id WHERE o.name = $7)) RETURNING *" ,
d23464c0 Andy McCall 1 month ago ↶
132
)
133
.bind ( owner)
134
.bind ( name)
135
.bind ( description)
136
.bind ( visibility.as_str ( ) )
137
.bind ( now)
138
.bind ( now)
462f88d1 Andy McCall 1 month ago ↶
139
.bind ( owner)
d23464c0 Andy McCall 1 month ago ↶
140
.fetch_one ( pool)
141
.await
d0fbea2e Andy McCall 1 month ago ↶
142
}
143
144
145
pub async fn moved_to ( pool : & Pool, owner : & str , name : & str ) -> sqlx:: Result< Option< RepoRow> > {
5d90e0e0 Andy McCall 21 days ago
146
let id: Option< i64 > = sqlx:: query_scalar( " SELECT repo_id FROM repo_redirects WHERE old_owner = $1 AND old_name = $2" )
d0fbea2e Andy McCall 1 month ago ↶
147
.bind ( owner)
148
.bind ( name)
149
.fetch_optional ( pool)
150
.await? ;
151
match id {
152
Some ( id) => get_by_id ( pool, id) .await,
153
None => Ok ( None ) ,
154
}
155
}
156
157
158
159
160
pub async fn transfer ( pool : & Pool, id : i64 , old_owner : & str , name : & str , new_owner : & str ) -> sqlx:: Result< ( ) > {
5d90e0e0 Andy McCall 21 days ago
161
sqlx:: query( " UPDATE repositories SET owner = $1, updated_ms = $2 WHERE id = $3" )
d0fbea2e Andy McCall 1 month ago ↶
162
.bind ( new_owner)
163
.bind ( now_ms ( ) )
164
.bind ( id)
165
.execute ( pool)
166
.await? ;
5d90e0e0 Andy McCall 21 days ago
167
sqlx:: query( " DELETE FROM repo_redirects WHERE old_owner = $1 AND old_name = $2" ) .bind ( new_owner) .bind ( name) .execute ( pool) .await? ;
168
sqlx:: query( " INSERT OR REPLACE INTO repo_redirects (old_owner, old_name, repo_id, moved_ms) VALUES ($1, $2, $3, $4)" )
d0fbea2e Andy McCall 1 month ago ↶
169
.bind ( old_owner)
170
.bind ( name)
171
.bind ( id)
172
.bind ( now_ms ( ) )
173
.execute ( pool)
174
.await? ;
175
Ok ( ( ) )
176
}
177
178
179
pub async fn set_archived ( pool : & Pool, id : i64 , archived : bool , reason : & str ) -> sqlx:: Result< ( ) > {
5d90e0e0 Andy McCall 21 days ago
180
sqlx:: query( " UPDATE repositories SET archived = $1, archived_reason = $2, updated_ms = $3 WHERE id = $4" )
d0fbea2e Andy McCall 1 month ago ↶
181
.bind ( archived)
182
.bind ( if archived { reason } else { " " } )
183
.bind ( now_ms ( ) )
184
.bind ( id)
185
.execute ( pool)
186
.await? ;
187
Ok ( ( ) )
d23464c0 Andy McCall 1 month ago ↶
188
}
189
190
pub async fn delete ( pool : & Pool, id : i64 ) -> sqlx:: Result< ( ) > {
5d90e0e0 Andy McCall 21 days ago
191
sqlx:: query( " DELETE FROM repositories WHERE id = $1" ) .bind ( id) .execute ( pool) .await? ;
d23464c0 Andy McCall 1 month ago ↶
192
Ok ( ( ) )
193
}
194
195
pub async fn set_description ( pool : & Pool, id : i64 , description : & str ) -> sqlx:: Result< ( ) > {
5d90e0e0 Andy McCall 21 days ago
196
sqlx:: query( " UPDATE repositories SET description = $1 WHERE id = $2" ) .bind ( description) .bind ( id) .execute ( pool) .await? ;
d23464c0 Andy McCall 1 month ago ↶
197
Ok ( ( ) )
198
}
199
200
pub async fn set_default_bookmark ( pool : & Pool, id : i64 , bookmark : & str ) -> sqlx:: Result< ( ) > {
5d90e0e0 Andy McCall 21 days ago
201
sqlx:: query( " UPDATE repositories SET default_bookmark = $1 WHERE id = $2" ) .bind ( bookmark) .bind ( id) .execute ( pool) .await? ;
d23464c0 Andy McCall 1 month ago ↶
202
Ok ( ( ) )
203
}
204
205
pub async fn set_visibility ( pool : & Pool, id : i64 , visibility : Visibility) -> sqlx:: Result< ( ) > {
5d90e0e0 Andy McCall 21 days ago
206
sqlx:: query( " UPDATE repositories SET visibility = $1 WHERE id = $2" ) .bind ( visibility.as_str ( ) ) .bind ( id) .execute ( pool) .await? ;
d23464c0 Andy McCall 1 month ago ↶
207
Ok ( ( ) )
208
}
209
210
19bb31ff Andy McCall 1 month ago ↶
211
pub async fn record_push ( pool : & Pool, id : i64 , pusher : Option< ( i64 , & str ) > , results : & [PushOutcome]) -> sqlx:: Result< ( ) > {
2a0c09c0 Andy McCall 21 days ago ↶
212
record_push_via ( pool, id, pusher, " " , results) .await
213
}
214
215
216
pub async fn record_push_via ( pool : & Pool, id : i64 , pusher : Option< ( i64 , & str ) > , via : & str , results : & [PushOutcome]) -> sqlx:: Result< ( ) > {
d23464c0 Andy McCall 1 month ago ↶
217
let now = now_ms ( ) ;
941abdd6 Andy McCall 1 month ago ↶
218
let mut tx = super :: begin_write( pool) .await? ;
62d6e969 Andy McCall 1 month ago ↶
219
for r in results {
5d90e0e0 Andy McCall 21 days ago
220
sqlx:: query( " INSERT INTO pushes (repo_id, at_ms, reference, ok, reason, old_state, new_state, heads, changes, account_id, pusher, via) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)" )
d23464c0 Andy McCall 1 month ago ↶
221
.bind ( id)
222
.bind ( now)
62d6e969 Andy McCall 1 month ago ↶
223
.bind ( & r.reference)
224
.bind ( r.ok)
225
.bind ( & r.reason)
226
.bind ( & r.old_state)
227
.bind ( & r.new_state)
228
.bind ( r.heads)
229
.bind ( r.changes)
19bb31ff Andy McCall 1 month ago ↶
230
.bind ( pusher.map ( | p | p.0 ) .filter ( | id | * id > 0 ) )
231
.bind ( pusher.map ( | p | p.1 ) .unwrap_or ( " " ) )
2a0c09c0 Andy McCall 21 days ago ↶
232
.bind ( via)
d23464c0 Andy McCall 1 month ago ↶
233
.execute ( & mut * tx)
234
.await? ;
235
}
62d6e969 Andy McCall 1 month ago ↶
236
if results.iter ( ) .any ( | r | r.ok ) {
5d90e0e0 Andy McCall 21 days ago
237
sqlx:: query( " UPDATE repositories SET updated_ms = $1 WHERE id = $2" ) .bind ( now) .bind ( id) .execute ( & mut * tx) .await? ;
d23464c0 Andy McCall 1 month ago ↶
238
}
239
tx.commit ( ) .await
240
}
241
242
pub async fn pushes ( pool : & Pool, id : i64 , limit : i64 ) -> sqlx:: Result< Vec< PushRow> > {
5d90e0e0 Andy McCall 21 days ago
243
sqlx:: query_as( " SELECT id, at_ms, reference, ok, reason, old_state, new_state, heads, changes, pusher FROM pushes WHERE repo_id = $1 ORDER BY at_ms DESC, id DESC LIMIT $2" )
d23464c0 Andy McCall 1 month ago ↶
244
.bind ( id)
245
.bind ( limit)
246
.fetch_all ( pool)
247
.await
248
}
3f4b0ea6 Andy McCall 1 month ago ↶
249
1553bfc9 Andy McCall 1 month ago ↶
250
251
252
253
pub fn parse_topics ( input : & str ) -> Result< String, String> {
254
let mut out: Vec< String> = Vec :: new( ) ;
255
for t in input.split ( [ ' ,' , ' ' , ' \n ' , ' \t ' ] ) .map ( | t | t.trim ( ) .to_ascii_lowercase ( ) ) .filter ( | t | ! t.is_empty ( ) ) {
256
if t.len ( ) > 35 | | ! t.chars ( ) .all ( | c | c.is_ascii_alphanumeric ( ) | | c = = ' -' ) | | t.starts_with ( ' -' ) {
257
return Err ( format! ( " \" {t} \" is not a topic: use letters, digits and dashes, up to 35 characters" ) ) ;
258
}
259
if ! out.contains ( & t) {
260
out.push ( t) ;
261
}
262
}
263
if out.len ( ) > 20 {
264
return Err ( " At most 20 topics." .into ( ) ) ;
265
}
266
Ok ( out.join ( " " ) )
267
}
268
269
pub async fn set_topics ( pool : & Pool, id : i64 , topics : & str ) -> sqlx:: Result< ( ) > {
5d90e0e0 Andy McCall 21 days ago
270
sqlx:: query( " UPDATE repositories SET topics = $1 WHERE id = $2" ) .bind ( topics) .bind ( id) .execute ( pool) .await? ;
1553bfc9 Andy McCall 1 month ago ↶
271
Ok ( ( ) )
272
}
273
274
275
pub async fn toggle_star ( pool : & Pool, account_id : i64 , repo_id : i64 ) -> sqlx:: Result< bool > {
5d90e0e0 Andy McCall 21 days ago
276
let gone = sqlx:: query( " DELETE FROM stars WHERE account_id = $1 AND repo_id = $2" ) .bind ( account_id) .bind ( repo_id) .execute ( pool) .await? ;
1553bfc9 Andy McCall 1 month ago ↶
277
if gone.rows_affected ( ) > 0 {
278
return Ok ( false ) ;
279
}
5d90e0e0 Andy McCall 21 days ago
280
sqlx:: query( " INSERT INTO stars (account_id, repo_id, at_ms) VALUES ($1, $2, $3)" )
1553bfc9 Andy McCall 1 month ago ↶
281
.bind ( account_id)
282
.bind ( repo_id)
283
.bind ( now_ms ( ) )
284
.execute ( pool)
285
.await? ;
286
Ok ( true )
287
}
288
289
pub async fn starred ( pool : & Pool, account_id : i64 , repo_id : i64 ) -> sqlx:: Result< bool > {
5d90e0e0 Andy McCall 21 days ago
290
let n: i64 = sqlx:: query_scalar( " SELECT COUNT(*) FROM stars WHERE account_id = $1 AND repo_id = $2" )
1553bfc9 Andy McCall 1 month ago ↶
291
.bind ( account_id)
292
.bind ( repo_id)
293
.fetch_one ( pool)
294
.await? ;
295
Ok ( n > 0 )
296
}
297
298
299
pub async fn counts ( pool : & Pool, repo_id : i64 ) -> sqlx:: Result< ( i64 , i64 ) > {
5d90e0e0 Andy McCall 21 days ago
300
let stars: i64 = sqlx:: query_scalar( " SELECT COUNT(*) FROM stars WHERE repo_id = $1" ) .bind ( repo_id) .fetch_one ( pool) .await? ;
bff9106e Andy McCall 23 days ago ↶
301
let watchers = super :: notify:: watchers( pool, repo_id) .await? .len ( ) as i64 ;
1553bfc9 Andy McCall 1 month ago ↶
302
Ok ( ( stars, watchers) )
303
}
304
305
306
pub async fn star_counts ( pool : & Pool) -> sqlx:: Result< std:: collections:: HashMap< i64 , i64 > > {
307
let rows: Vec< ( i64 , i64 ) > = sqlx:: query_as( " SELECT repo_id, COUNT(*) FROM stars GROUP BY repo_id" ) .fetch_all ( pool) .await? ;
308
Ok ( rows.into_iter ( ) .collect ( ) )
309
}
310
07b1a5bb Andy McCall 1 month ago ↶
311
312
pub async fn set_forked_from ( pool : & Pool, id : i64 , from : i64 ) -> sqlx:: Result< ( ) > {
5d90e0e0 Andy McCall 21 days ago
313
sqlx:: query( " UPDATE repositories SET forked_from = $1 WHERE id = $2" ) .bind ( from) .bind ( id) .execute ( pool) .await? ;
07b1a5bb Andy McCall 1 month ago ↶
314
Ok ( ( ) )
315
}
316
317
318
pub async fn forks_of ( pool : & Pool, id : i64 ) -> sqlx:: Result< Vec< RepoRow> > {
5d90e0e0 Andy McCall 21 days ago
319
sqlx:: query_as( " SELECT * FROM repositories WHERE forked_from = $1 ORDER BY updated_ms DESC" ) .bind ( id) .fetch_all ( pool) .await
9008bf6c Andy McCall 1 month ago ↶
320
}
321
322
323
pub async fn set_flow ( pool : & Pool, id : i64 , setting : & str ) -> sqlx:: Result< ( ) > {
5d90e0e0 Andy McCall 21 days ago
324
sqlx:: query( " UPDATE repositories SET flow = $1 WHERE id = $2" ) .bind ( setting) .bind ( id) .execute ( pool) .await? ;
107eaba5 Andy McCall 20 days ago ↶
325
Ok ( ( ) )
326
}
327
328
pub async fn set_flow_priority ( pool : & Pool, id : i64 , priority : & str ) -> sqlx:: Result< ( ) > {
329
sqlx:: query( " UPDATE repositories SET flow_priority = $1 WHERE id = $2" ) .bind ( priority) .bind ( id) .execute ( pool) .await? ;
d3b048b2 Andy McCall 23 days ago ↶
330
Ok ( ( ) )
331
}
332
333
pub async fn set_backup ( pool : & Pool, id : i64 , setting : & str ) -> sqlx:: Result< ( ) > {
5d90e0e0 Andy McCall 21 days ago
334
sqlx:: query( " UPDATE repositories SET backup = $1 WHERE id = $2" ) .bind ( setting) .bind ( id) .execute ( pool) .await? ;
f4d6ef41 Andy McCall 23 days ago ↶
335
Ok ( ( ) )
336
}
337
338
pub async fn set_diffusion_land ( pool : & Pool, id : i64 , setting : & str ) -> sqlx:: Result< ( ) > {
5d90e0e0 Andy McCall 21 days ago
339
sqlx:: query( " UPDATE repositories SET diffusion_land = $1 WHERE id = $2" ) .bind ( setting) .bind ( id) .execute ( pool) .await? ;
e7158350 Andy McCall 24 days ago ↶
340
Ok ( ( ) )
341
}
342
343
pub async fn set_diffusion ( pool : & Pool, id : i64 , setting : & str ) -> sqlx:: Result< ( ) > {
5d90e0e0 Andy McCall 21 days ago
344
sqlx:: query( " UPDATE repositories SET diffusion = $1 WHERE id = $2" ) .bind ( setting) .bind ( id) .execute ( pool) .await? ;
9008bf6c Andy McCall 1 month ago ↶
345
Ok ( ( ) )
07b1a5bb Andy McCall 1 month ago ↶
346
}
347
3f4b0ea6 Andy McCall 1 month ago ↶
348
pub async fn set_git_mirror ( pool : & Pool, id : i64 , on : bool ) -> sqlx:: Result< ( ) > {
5d90e0e0 Andy McCall 21 days ago
349
sqlx:: query( " UPDATE repositories SET git_mirror = $1 WHERE id = $2" ) .bind ( on) .bind ( id) .execute ( pool) .await? ;
3f4b0ea6 Andy McCall 1 month ago ↶
350
Ok ( ( ) )
351
}
352
353
pub async fn set_git_mirror_state ( pool : & Pool, id : i64 , state : & str ) -> sqlx:: Result< ( ) > {
5d90e0e0 Andy McCall 21 days ago
354
sqlx:: query( " UPDATE repositories SET git_mirror_state = $1 WHERE id = $2" ) .bind ( state) .bind ( id) .execute ( pool) .await? ;
3f4b0ea6 Andy McCall 1 month ago ↶
355
Ok ( ( ) )
356
}
1553bfc9 Andy McCall 1 month ago ↶
357
358
# [ cfg ( test ) ]
359
mod topic_tests {
360
use super :: parse_topics;
361
362
# [ test ]
363
fn topics_are_normalised_and_checked ( ) {
364
assert_eq! ( parse_topics ( " Rust, vcs retro-computing,rust" ) .unwrap ( ) , " rust vcs retro-computing" ) ;
365
assert_eq! ( parse_topics ( " " ) .unwrap ( ) , " " ) ;
366
assert! ( parse_topics ( " c++" ) .is_err ( ) ) ;
367
assert! ( parse_topics ( " -lead" ) .is_err ( ) ) ;
368
assert! ( parse_topics ( & " x" .repeat ( 36 ) ) .is_err ( ) ) ;
369
assert! ( parse_topics ( & ( 0 .. 21 ) .map ( | i | format! ( " t{i} " ) ) .collect:: < Vec< _ > > ( ) .join ( " " ) ) .is_err ( ) ) ;
370
}
371
}