e1ec9fbf Andy McCall 1 month ago ↶
1
2
3
use super :: { now_ms, Pool} ;
4
use sqlx:: FromRow;
5
6
# [ derive ( Clone, Debug, FromRow ) ]
7
pub struct Project {
8
pub id : i64 ,
9
pub owner : String,
10
pub number : i64 ,
11
pub title : String,
12
pub description : String,
13
pub public : bool ,
14
pub closed : bool ,
15
pub created_ms : i64 ,
16
pub updated_ms : i64 ,
17
}
18
19
# [ derive ( Clone, Debug, FromRow ) ]
20
pub struct Column {
21
pub id : i64 ,
22
pub project_id : i64 ,
23
pub name : String,
24
pub position : i64 ,
439ecd87 Andy McCall 11 days ago ↶
25
26
pub archive_after_days : Option< i64 > ,
e1ec9fbf Andy McCall 1 month ago ↶
27
}
28
29
# [ derive ( Clone, Debug, FromRow ) ]
30
pub struct Card {
31
pub id : i64 ,
32
pub project_id : i64 ,
33
pub column_id : i64 ,
34
pub position : i64 ,
35
pub kind : String,
36
pub repo_id : Option< i64 > ,
37
pub number : Option< i64 > ,
38
pub note : String,
39
pub added_by : String,
40
pub created_ms : i64 ,
439ecd87 Andy McCall 11 days ago ↶
41
42
pub archived_ms : Option< i64 > ,
43
44
pub moved_ms : i64 ,
e1ec9fbf Andy McCall 1 month ago ↶
45
}
46
439ecd87 Andy McCall 11 days ago ↶
47
48
const DAY_MS : i64 = 86_400_000 ;
49
e1ec9fbf Andy McCall 1 month ago ↶
50
pub const DEFAULT_COLUMNS : [ & str ; 3 ] = [ " Backlog" , " Doing" , " Done" ] ;
51
52
pub async fn create ( pool : & Pool, owner : & str , title : & str , description : & str , public : bool ) -> sqlx:: Result< Project> {
941abdd6 Andy McCall 1 month ago ↶
53
let mut tx = super :: begin_write( pool) .await? ;
e1ec9fbf Andy McCall 1 month ago ↶
54
let number: i64 =
5d90e0e0 Andy McCall 20 days ago
55
sqlx:: query_scalar( " SELECT COALESCE(MAX(number), 0) + 1 FROM projects WHERE owner = $1" ) .bind ( owner) .fetch_one ( & mut * tx) .await? ;
e1ec9fbf Andy McCall 1 month ago ↶
56
let now = now_ms ( ) ;
57
let p: Project = sqlx:: query_as(
5d90e0e0 Andy McCall 20 days ago
58
" INSERT INTO projects (owner, number, title, description, public, created_ms, updated_ms) VALUES ($1, $2, $3, $4, $5, $6, $7) RETURNING *" ,
e1ec9fbf Andy McCall 1 month ago ↶
59
)
60
.bind ( owner)
61
.bind ( number)
62
.bind ( title)
63
.bind ( description)
64
.bind ( public)
65
.bind ( now)
66
.bind ( now)
67
.fetch_one ( & mut * tx)
68
.await? ;
b1e92a62 Andy McCall 26 days ago ↶
69
5d90e0e0 Andy McCall 20 days ago
70
sqlx:: query( " DELETE FROM project_redirects WHERE owner = $1 AND number = $2" ) .bind ( owner) .bind ( number) .execute ( & mut * tx) .await? ;
e1ec9fbf Andy McCall 1 month ago ↶
71
for ( i, name) in DEFAULT_COLUMNS .iter ( ) .enumerate ( ) {
5d90e0e0 Andy McCall 20 days ago
72
sqlx:: query( " INSERT INTO project_columns (project_id, name, position) VALUES ($1, $2, $3)" )
e1ec9fbf Andy McCall 1 month ago ↶
73
.bind ( p.id)
74
.bind ( name)
75
.bind ( i as i64 )
76
.execute ( & mut * tx)
77
.await? ;
78
}
79
tx.commit ( ) .await? ;
80
Ok ( p)
81
}
82
83
pub async fn of_owner ( pool : & Pool, owner : & str ) -> sqlx:: Result< Vec< Project> > {
5d90e0e0 Andy McCall 20 days ago
84
sqlx:: query_as( " SELECT * FROM projects WHERE owner = $1 ORDER BY closed, updated_ms DESC" ) .bind ( owner) .fetch_all ( pool) .await
e1ec9fbf Andy McCall 1 month ago ↶
85
}
86
b1e92a62 Andy McCall 26 days ago ↶
87
88
89
pub async fn transfer ( pool : & Pool, id : i64 , to : & str ) -> sqlx:: Result< Project> {
90
let mut tx = super :: begin_write( pool) .await? ;
5d90e0e0 Andy McCall 20 days ago
91
let old: Project = sqlx:: query_as( " SELECT * FROM projects WHERE id = $1" ) .bind ( id) .fetch_one ( & mut * tx) .await? ;
b1e92a62 Andy McCall 26 days ago ↶
92
let number: i64 =
5d90e0e0 Andy McCall 20 days ago
93
sqlx:: query_scalar( " SELECT COALESCE(MAX(number), 0) + 1 FROM projects WHERE owner = $1" ) .bind ( to) .fetch_one ( & mut * tx) .await? ;
d1bf2d69 Andy McCall 20 days ago ↶
94
sqlx:: query( " INSERT INTO project_redirects (owner, number, project_id) VALUES ($1, $2, $3) ON CONFLICT (owner, number) DO UPDATE SET project_id = excluded.project_id" )
b1e92a62 Andy McCall 26 days ago ↶
95
.bind ( & old.owner)
96
.bind ( old.number)
97
.bind ( id)
98
.execute ( & mut * tx)
99
.await? ;
5d90e0e0 Andy McCall 20 days ago
100
sqlx:: query( " DELETE FROM project_redirects WHERE owner = $1 AND number = $2" ) .bind ( to) .bind ( number) .execute ( & mut * tx) .await? ;
101
let moved: Project = sqlx:: query_as( " UPDATE projects SET owner = $1, number = $2, updated_ms = $3 WHERE id = $4 RETURNING *" )
b1e92a62 Andy McCall 26 days ago ↶
102
.bind ( to)
103
.bind ( number)
104
.bind ( now_ms ( ) )
105
.bind ( id)
106
.fetch_one ( & mut * tx)
107
.await? ;
108
tx.commit ( ) .await? ;
109
Ok ( moved)
110
}
111
112
113
114
pub async fn redirected ( pool : & Pool, owner : & str , number : i64 ) -> sqlx:: Result< Option< Project> > {
5d90e0e0 Andy McCall 20 days ago
115
sqlx:: query_as( " SELECT p.* FROM project_redirects r JOIN projects p ON p.id = r.project_id WHERE r.owner = $1 AND r.number = $2" )
b1e92a62 Andy McCall 26 days ago ↶
116
.bind ( owner)
117
.bind ( number)
118
.fetch_optional ( pool)
119
.await
120
}
121
e1ec9fbf Andy McCall 1 month ago ↶
122
pub async fn get ( pool : & Pool, owner : & str , number : i64 ) -> sqlx:: Result< Option< Project> > {
5d90e0e0 Andy McCall 20 days ago
123
sqlx:: query_as( " SELECT * FROM projects WHERE owner = $1 AND number = $2" ) .bind ( owner) .bind ( number) .fetch_optional ( pool) .await
e1ec9fbf Andy McCall 1 month ago ↶
124
}
125
126
pub async fn set ( pool : & Pool, id : i64 , title : & str , description : & str , public : bool , closed : bool ) -> sqlx:: Result< ( ) > {
5d90e0e0 Andy McCall 20 days ago
127
sqlx:: query( " UPDATE projects SET title = $1, description = $2, public = $3, closed = $4, updated_ms = $5 WHERE id = $6" )
e1ec9fbf Andy McCall 1 month ago ↶
128
.bind ( title)
129
.bind ( description)
130
.bind ( public)
131
.bind ( closed)
132
.bind ( now_ms ( ) )
133
.bind ( id)
134
.execute ( pool)
135
.await? ;
136
Ok ( ( ) )
137
}
138
139
pub async fn delete ( pool : & Pool, id : i64 ) -> sqlx:: Result< ( ) > {
5d90e0e0 Andy McCall 20 days ago
140
sqlx:: query( " DELETE FROM projects WHERE id = $1" ) .bind ( id) .execute ( pool) .await? ;
e1ec9fbf Andy McCall 1 month ago ↶
141
Ok ( ( ) )
142
}
143
144
async fn touch ( pool : & Pool, id : i64 ) -> sqlx:: Result< ( ) > {
5d90e0e0 Andy McCall 20 days ago
145
sqlx:: query( " UPDATE projects SET updated_ms = $1 WHERE id = $2" ) .bind ( now_ms ( ) ) .bind ( id) .execute ( pool) .await? ;
e1ec9fbf Andy McCall 1 month ago ↶
146
Ok ( ( ) )
147
}
148
149
pub async fn columns ( pool : & Pool, project_id : i64 ) -> sqlx:: Result< Vec< Column> > {
5d90e0e0 Andy McCall 20 days ago
150
sqlx:: query_as( " SELECT * FROM project_columns WHERE project_id = $1 ORDER BY position, id" ) .bind ( project_id) .fetch_all ( pool) .await
e1ec9fbf Andy McCall 1 month ago ↶
151
}
152
153
pub async fn cards ( pool : & Pool, project_id : i64 ) -> sqlx:: Result< Vec< Card> > {
5d90e0e0 Andy McCall 20 days ago
154
sqlx:: query_as( " SELECT * FROM project_cards WHERE project_id = $1 ORDER BY position, id" ) .bind ( project_id) .fetch_all ( pool) .await
e1ec9fbf Andy McCall 1 month ago ↶
155
}
156
157
pub async fn add_column ( pool : & Pool, project_id : i64 , name : & str ) -> sqlx:: Result< ( ) > {
5d90e0e0 Andy McCall 20 days ago
158
let pos: i64 = sqlx:: query_scalar( " SELECT COALESCE(MAX(position), -1) + 1 FROM project_columns WHERE project_id = $1" )
e1ec9fbf Andy McCall 1 month ago ↶
159
.bind ( project_id)
160
.fetch_one ( pool)
161
.await? ;
5d90e0e0 Andy McCall 20 days ago
162
sqlx:: query( " INSERT INTO project_columns (project_id, name, position) VALUES ($1, $2, $3)" )
e1ec9fbf Andy McCall 1 month ago ↶
163
.bind ( project_id)
164
.bind ( name)
165
.bind ( pos)
166
.execute ( pool)
167
.await? ;
168
touch ( pool, project_id) .await
169
}
170
171
pub async fn rename_column ( pool : & Pool, project_id : i64 , column_id : i64 , name : & str ) -> sqlx:: Result< ( ) > {
5d90e0e0 Andy McCall 20 days ago
172
sqlx:: query( " UPDATE project_columns SET name = $1 WHERE id = $2 AND project_id = $3" )
e1ec9fbf Andy McCall 1 month ago ↶
173
.bind ( name)
174
.bind ( column_id)
175
.bind ( project_id)
176
.execute ( pool)
177
.await? ;
178
touch ( pool, project_id) .await
179
}
180
181
182
pub async fn shift_column ( pool : & Pool, project_id : i64 , column_id : i64 , by : i64 ) -> sqlx:: Result< ( ) > {
183
let cols = columns ( pool, project_id) .await? ;
184
let Some ( i) = cols.iter ( ) .position ( | c | c.id = = column_id ) else { return Ok ( ( ) ) } ;
185
let j = i as i64 + by;
186
if j < 0 | | j as usize > = cols.len ( ) {
187
return Ok ( ( ) ) ;
188
}
189
let mut ids: Vec< i64 > = cols.iter ( ) .map ( | c | c.id ) .collect ( ) ;
190
ids.swap ( i, j as usize ) ;
191
for ( pos, id) in ids.iter ( ) .enumerate ( ) {
5d90e0e0 Andy McCall 20 days ago
192
sqlx:: query( " UPDATE project_columns SET position = $1 WHERE id = $2" ) .bind ( pos as i64 ) .bind ( id) .execute ( pool) .await? ;
e1ec9fbf Andy McCall 1 month ago ↶
193
}
194
touch ( pool, project_id) .await
195
}
196
197
198
pub async fn remove_column ( pool : & Pool, project_id : i64 , column_id : i64 ) -> sqlx:: Result< bool > {
199
let cols = columns ( pool, project_id) .await? ;
200
let Some ( to) = cols.iter ( ) .find ( | c | c.id ! = column_id ) else { return Ok ( false ) } ;
201
if ! cols.iter ( ) .any ( | c | c.id = = column_id ) {
202
return Ok ( false ) ;
203
}
941abdd6 Andy McCall 1 month ago ↶
204
let mut tx = super :: begin_write( pool) .await? ;
5d90e0e0 Andy McCall 20 days ago
205
let base: i64 = sqlx:: query_scalar( " SELECT COALESCE(MAX(position), -1) + 1 FROM project_cards WHERE column_id = $1" )
e1ec9fbf Andy McCall 1 month ago ↶
206
.bind ( to.id)
207
.fetch_one ( & mut * tx)
208
.await? ;
5d90e0e0 Andy McCall 20 days ago
209
sqlx:: query( " UPDATE project_cards SET column_id = $1, position = position + $2 WHERE column_id = $3" )
e1ec9fbf Andy McCall 1 month ago ↶
210
.bind ( to.id)
211
.bind ( base)
212
.bind ( column_id)
213
.execute ( & mut * tx)
214
.await? ;
5d90e0e0 Andy McCall 20 days ago
215
sqlx:: query( " DELETE FROM project_columns WHERE id = $1" ) .bind ( column_id) .execute ( & mut * tx) .await? ;
e1ec9fbf Andy McCall 1 month ago ↶
216
tx.commit ( ) .await? ;
217
touch ( pool, project_id) .await? ;
218
Ok ( true )
219
}
220
221
pub struct NewCard < 'a > {
222
pub column_id : i64 ,
223
pub kind : & 'a str ,
224
pub repo_id : Option< i64 > ,
225
pub number : Option< i64 > ,
226
pub note : & 'a str ,
227
pub added_by : & 'a str ,
228
}
229
230
231
232
pub async fn add_card ( pool : & Pool, project_id : i64 , c : NewCard< '_ > ) -> sqlx:: Result< bool > {
5d90e0e0 Andy McCall 20 days ago
233
let pos: i64 = sqlx:: query_scalar( " SELECT COALESCE(MAX(position), -1) + 1 FROM project_cards WHERE column_id = $1" )
e1ec9fbf Andy McCall 1 month ago ↶
234
.bind ( c.column_id)
235
.fetch_one ( pool)
236
.await? ;
237
let r = sqlx:: query(
439ecd87 Andy McCall 11 days ago ↶
238
" INSERT INTO project_cards (project_id, column_id, position, kind, repo_id, number, note, added_by, created_ms, moved_ms) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $9) ON CONFLICT DO NOTHING" ,
e1ec9fbf Andy McCall 1 month ago ↶
239
)
240
.bind ( project_id)
241
.bind ( c.column_id)
242
.bind ( pos)
243
.bind ( c.kind)
244
.bind ( c.repo_id)
245
.bind ( c.number)
246
.bind ( c.note)
247
.bind ( c.added_by)
248
.bind ( now_ms ( ) )
249
.execute ( pool)
250
.await? ;
251
touch ( pool, project_id) .await? ;
252
Ok ( r.rows_affected ( ) = = 1 )
253
}
254
255
256
pub async fn move_card ( pool : & Pool, project_id : i64 , card_id : i64 , column_id : i64 ) -> sqlx:: Result< ( ) > {
5d90e0e0 Andy McCall 20 days ago
257
let ok: i64 = sqlx:: query_scalar( " SELECT COUNT(*) FROM project_columns WHERE id = $1 AND project_id = $2" )
e1ec9fbf Andy McCall 1 month ago ↶
258
.bind ( column_id)
259
.bind ( project_id)
260
.fetch_one ( pool)
261
.await? ;
262
if ok = = 0 {
263
return Ok ( ( ) ) ;
264
}
5d90e0e0 Andy McCall 20 days ago
265
let pos: i64 = sqlx:: query_scalar( " SELECT COALESCE(MAX(position), -1) + 1 FROM project_cards WHERE column_id = $1" )
e1ec9fbf Andy McCall 1 month ago ↶
266
.bind ( column_id)
267
.fetch_one ( pool)
268
.await? ;
439ecd87 Andy McCall 11 days ago ↶
269
sqlx:: query( " UPDATE project_cards SET moved_ms = CASE WHEN column_id = $1 THEN moved_ms ELSE $3 END, column_id = $1, position = $2 WHERE id = $4 AND project_id = $5" )
e1ec9fbf Andy McCall 1 month ago ↶
270
.bind ( column_id)
271
.bind ( pos)
439ecd87 Andy McCall 11 days ago ↶
272
.bind ( now_ms ( ) )
e1ec9fbf Andy McCall 1 month ago ↶
273
.bind ( card_id)
274
.bind ( project_id)
275
.execute ( pool)
276
.await? ;
277
touch ( pool, project_id) .await
278
}
279
456f4197 Andy McCall 12 days ago ↶
280
281
282
pub async fn move_cards ( pool : & Pool, project_id : i64 , card_ids : & [i64 ], column_id : i64 ) -> sqlx:: Result< ( ) > {
283
let mut tx = pool.begin ( ) .await? ;
284
let pos: i64 = sqlx:: query_scalar( " SELECT COALESCE(MAX(position), -1) + 1 FROM project_cards WHERE column_id = $1" )
285
.bind ( column_id)
286
.fetch_one ( & mut * tx)
287
.await? ;
439ecd87 Andy McCall 11 days ago ↶
288
let now = now_ms ( ) ;
456f4197 Andy McCall 12 days ago ↶
289
for ( i, id) in card_ids.iter ( ) .enumerate ( ) {
439ecd87 Andy McCall 11 days ago ↶
290
sqlx:: query( " UPDATE project_cards SET column_id = $1, position = $2, moved_ms = $3 WHERE id = $4 AND project_id = $5" )
456f4197 Andy McCall 12 days ago ↶
291
.bind ( column_id)
292
.bind ( pos + i as i64 )
439ecd87 Andy McCall 11 days ago ↶
293
.bind ( now)
456f4197 Andy McCall 12 days ago ↶
294
.bind ( id)
295
.bind ( project_id)
296
.execute ( & mut * tx)
297
.await? ;
298
}
439ecd87 Andy McCall 11 days ago ↶
299
sqlx:: query( " UPDATE projects SET updated_ms = $1 WHERE id = $2" ) .bind ( now) .bind ( project_id) .execute ( & mut * tx) .await? ;
456f4197 Andy McCall 12 days ago ↶
300
tx.commit ( ) .await
439ecd87 Andy McCall 11 days ago ↶
301
}
302
303
304
pub async fn archive_cards ( pool : & Pool, project_id : i64 , card_ids : & [i64 ]) -> sqlx:: Result< u64 > {
305
let mut tx = pool.begin ( ) .await? ;
306
let now = now_ms ( ) ;
307
let mut n = 0 ;
308
for id in card_ids {
309
n + = sqlx:: query( " UPDATE project_cards SET archived_ms = $1 WHERE id = $2 AND project_id = $3 AND archived_ms IS NULL" )
310
.bind ( now)
311
.bind ( id)
312
.bind ( project_id)
313
.execute ( & mut * tx)
314
.await?
315
.rows_affected ( ) ;
316
}
317
sqlx:: query( " UPDATE projects SET updated_ms = $1 WHERE id = $2" ) .bind ( now) .bind ( project_id) .execute ( & mut * tx) .await? ;
318
tx.commit ( ) .await? ;
319
Ok ( n)
320
}
321
322
323
324
325
pub async fn restore_card ( pool : & Pool, project_id : i64 , card_id : i64 ) -> sqlx:: Result< bool > {
326
let mut tx = pool.begin ( ) .await? ;
327
let pos: i64 = sqlx:: query_scalar(
328
" SELECT COALESCE(MAX(position), -1) + 1 FROM project_cards WHERE column_id = (SELECT column_id FROM project_cards WHERE id = $1)" ,
329
)
330
.bind ( card_id)
331
.fetch_one ( & mut * tx)
332
.await? ;
333
let now = now_ms ( ) ;
334
let r = sqlx:: query(
335
" UPDATE project_cards SET archived_ms = NULL, moved_ms = $1, position = $2 WHERE id = $3 AND project_id = $4 AND archived_ms IS NOT NULL" ,
336
)
337
.bind ( now)
338
.bind ( pos)
339
.bind ( card_id)
340
.bind ( project_id)
341
.execute ( & mut * tx)
342
.await? ;
343
sqlx:: query( " UPDATE projects SET updated_ms = $1 WHERE id = $2" ) .bind ( now) .bind ( project_id) .execute ( & mut * tx) .await? ;
344
tx.commit ( ) .await? ;
345
Ok ( r.rows_affected ( ) = = 1 )
346
}
347
348
349
pub fn older_than ( card : & Card, days : i64 , now : i64 ) -> bool {
350
card.moved_ms < now - days * DAY_MS
351
}
352
946fe7a0 Andy McCall 11 days ago ↶
353
354
355
439ecd87 Andy McCall 11 days ago ↶
356
pub async fn set_archive_after ( pool : & Pool, project_id : i64 , column_id : i64 , days : Option< i64 > ) -> sqlx:: Result< ( ) > {
357
sqlx:: query( " UPDATE project_columns SET archive_after_days = $1 WHERE id = $2 AND project_id = $3" )
358
.bind ( days)
359
.bind ( column_id)
360
.bind ( project_id)
361
.execute ( pool)
362
.await? ;
363
touch ( pool, project_id) .await
364
}
365
946fe7a0 Andy McCall 11 days ago ↶
366
367
368
pub const FINISHED_KEPT_DAYS : i64 = 14 ;
369
370
371
pub const NEVER : i64 = - 1 ;
372
373
374
375
376
377
439ecd87 Andy McCall 11 days ago ↶
378
pub async fn archive_due ( pool : & Pool, now : i64 ) -> sqlx:: Result< u64 > {
946fe7a0 Andy McCall 11 days ago ↶
379
let set = sqlx:: query(
439ecd87 Andy McCall 11 days ago ↶
380
" UPDATE project_cards SET archived_ms = $1 WHERE archived_ms IS NULL AND EXISTS \
946fe7a0 Andy McCall 11 days ago ↶
381
(SELECT 1 FROM project_columns c WHERE c.id = project_cards.column_id AND c.archive_after_days >= 0 \
439ecd87 Andy McCall 11 days ago ↶
382
AND project_cards.moved_ms < $1 - c.archive_after_days * $2)" ,
383
)
384
.bind ( now)
385
.bind ( DAY_MS )
946fe7a0 Andy McCall 11 days ago ↶
386
.execute ( pool)
387
.await? ;
388
let finished = sqlx:: query(
389
" UPDATE project_cards SET archived_ms = $1 WHERE archived_ms IS NULL AND moved_ms < $2 \
390
AND EXISTS (SELECT 1 FROM project_columns c WHERE c.id = project_cards.column_id AND c.archive_after_days IS NULL) \
391
AND ((kind = 'issue' AND EXISTS (SELECT 1 FROM issues i WHERE i.repo_id = project_cards.repo_id \
392
AND i.number = project_cards.number AND i.state = 'closed')) \
393
OR (kind = 'proposal' AND EXISTS (SELECT 1 FROM proposals p WHERE p.repo_id = project_cards.repo_id \
394
AND p.number = project_cards.number AND p.state IN ('closed', 'landed'))))" ,
395
)
396
.bind ( now)
397
.bind ( now - FINISHED_KEPT_DAYS * DAY_MS )
439ecd87 Andy McCall 11 days ago ↶
398
.execute ( pool)
399
.await? ;
946fe7a0 Andy McCall 11 days ago ↶
400
Ok ( set.rows_affected ( ) + finished.rows_affected ( ) )
456f4197 Andy McCall 12 days ago ↶
401
}
402
e1ec9fbf Andy McCall 1 month ago ↶
403
404
pub async fn shift_card ( pool : & Pool, project_id : i64 , card_id : i64 , by : i64 ) -> sqlx:: Result< ( ) > {
5d90e0e0 Andy McCall 20 days ago
405
let Some ( card) : Option< Card> = sqlx:: query_as( " SELECT * FROM project_cards WHERE id = $1 AND project_id = $2" )
e1ec9fbf Andy McCall 1 month ago ↶
406
.bind ( card_id)
407
.bind ( project_id)
408
.fetch_optional ( pool)
409
.await?
410
else {
411
return Ok ( ( ) ) ;
412
} ;
5d90e0e0 Andy McCall 20 days ago
413
let col: Vec< Card> = sqlx:: query_as( " SELECT * FROM project_cards WHERE column_id = $1 ORDER BY position, id" )
e1ec9fbf Andy McCall 1 month ago ↶
414
.bind ( card.column_id)
415
.fetch_all ( pool)
416
.await? ;
417
let Some ( i) = col.iter ( ) .position ( | c | c.id = = card_id ) else { return Ok ( ( ) ) } ;
418
let j = i as i64 + by;
419
if j < 0 | | j as usize > = col.len ( ) {
420
return Ok ( ( ) ) ;
421
}
422
let mut ids: Vec< i64 > = col.iter ( ) .map ( | c | c.id ) .collect ( ) ;
423
ids.swap ( i, j as usize ) ;
424
for ( pos, id) in ids.iter ( ) .enumerate ( ) {
5d90e0e0 Andy McCall 20 days ago
425
sqlx:: query( " UPDATE project_cards SET position = $1 WHERE id = $2" ) .bind ( pos as i64 ) .bind ( id) .execute ( pool) .await? ;
e1ec9fbf Andy McCall 1 month ago ↶
426
}
427
touch ( pool, project_id) .await
428
}
429
430
pub async fn remove_card ( pool : & Pool, project_id : i64 , card_id : i64 ) -> sqlx:: Result< ( ) > {
5d90e0e0 Andy McCall 20 days ago
431
sqlx:: query( " DELETE FROM project_cards WHERE id = $1 AND project_id = $2" ) .bind ( card_id) .bind ( project_id) .execute ( pool) .await? ;
e1ec9fbf Andy McCall 1 month ago ↶
432
touch ( pool, project_id) .await
433
}
434
435
436
pub async fn placements ( pool : & Pool, repo_id : i64 , number : i64 ) -> sqlx:: Result< Vec< ( Project, String) > > {
439ecd87 Andy McCall 11 days ago ↶
437
let rows: Vec< ( i64 , String, Option< i64 > ) > = sqlx:: query_as(
438
" SELECT c.project_id, col.name, c.archived_ms FROM project_cards c JOIN project_columns col ON col.id = c.column_id WHERE c.repo_id = $1 AND c.number = $2 AND c.kind <> 'note'" ,
e1ec9fbf Andy McCall 1 month ago ↶
439
)
440
.bind ( repo_id)
441
.bind ( number)
442
.fetch_all ( pool)
443
.await? ;
444
let mut out = Vec :: new( ) ;
439ecd87 Andy McCall 11 days ago ↶
445
for ( pid, col, archived) in rows {
446
let col = if archived.is_some ( ) { format! ( " {col} , archived" ) } else { col } ;
5d90e0e0 Andy McCall 20 days ago
447
if let Some ( p) = sqlx:: query_as:: < _ , Project> ( " SELECT * FROM projects WHERE id = $1" ) .bind ( pid) .fetch_optional ( pool) .await? {
e1ec9fbf Andy McCall 1 month ago ↶
448
out.push ( ( p, col) ) ;
449
}
450
}
451
Ok ( out)
452
}