1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
use crate::prelude::*;
#[cfg(feature = "ts_rs")]
use ts_rs::TS;

#[derive(Debug, Serialize)]
#[cfg_attr(feature = "ts_rs", derive(TS))]
pub struct RepositoryExercise {
    pub id: Uuid,
    pub repository_id: Uuid,
    pub part: String,
    pub name: String,
    pub repository_url: String,
    pub checksum: Vec<u8>,
    pub download_url: String,
}

pub async fn new(
    conn: &mut PgConnection,
    id: Uuid,
    repository_id: Uuid,
    part: &str,
    name: &str,
    checksum: &[u8],
    download_url: &str,
) -> ModelResult<()> {
    sqlx::query!(
        "
INSERT INTO repository_exercises (
    id,
    repository_id,
    part,
    name,
    checksum,
    download_url
)
VALUES ($1, $2, $3, $4, $5, $6)
",
        id,
        repository_id,
        part,
        name,
        checksum,
        download_url,
    )
    .execute(conn)
    .await?;
    Ok(())
}

pub async fn update_checksum(
    conn: &mut PgConnection,
    exercise: Uuid,
    checksum: &[u8],
) -> ModelResult<()> {
    sqlx::query!(
        "
UPDATE repository_exercises
SET checksum = $1
WHERE id = $2
",
        checksum,
        exercise
    )
    .execute(conn)
    .await?;
    Ok(())
}

pub async fn update_part_and_name(
    conn: &mut PgConnection,
    exercise: Uuid,
    part: &str,
    name: &str,
) -> ModelResult<()> {
    sqlx::query!(
        "
UPDATE repository_exercises
SET part = $1,
  name = $2
WHERE id = $3
",
        part,
        name,
        exercise
    )
    .execute(conn)
    .await?;
    Ok(())
}

pub async fn delete_for_repository(
    conn: &mut PgConnection,
    repository: Uuid,
) -> ModelResult<Vec<Uuid>> {
    let res = sqlx::query!(
        "
UPDATE repository_exercises
SET deleted_at = now()
WHERE repository_id = $1
AND deleted_at IS NULL
RETURNING id
",
        repository
    )
    .fetch_all(conn)
    .await?
    .into_iter()
    .map(|r| r.id)
    .collect();
    Ok(res)
}

pub async fn get_for_repository(
    conn: &mut PgConnection,
    repository: Uuid,
) -> ModelResult<Vec<RepositoryExercise>> {
    let exercises = sqlx::query_as!(
        RepositoryExercise,
        "
SELECT re.id,
  er.id AS repository_id,
  re.part,
  re.name,
  er.url AS repository_url,
  re.checksum,
  re.download_url
FROM repository_exercises AS re
JOIN exercise_repositories AS er ON er.id = re.repository_id
WHERE repository_id = $1
AND re.deleted_at IS NULL
",
        repository
    )
    .fetch_all(conn)
    .await?;
    Ok(exercises)
}

pub async fn get_for_course(
    conn: &mut PgConnection,
    course: Uuid,
) -> ModelResult<Vec<RepositoryExercise>> {
    let exercises = sqlx::query_as!(
        RepositoryExercise,
        "
SELECT re.id,
er.id AS repository_id,
  re.part,
  re.name,
  er.url AS repository_url,
  re.checksum,
  re.download_url
FROM repository_exercises AS re
JOIN exercise_repositories AS er ON er.id = re.repository_id
WHERE er.course_id = $1
AND re.deleted_at IS NULL
and er.deleted_at IS NULL
",
        course
    )
    .fetch_all(conn)
    .await?;
    Ok(exercises)
}

pub async fn delete_from_repository(
    conn: &mut PgConnection,
    repository_id: Uuid,
) -> ModelResult<()> {
    sqlx::query!(
        "
UPDATE repository_exercises
SET deleted_at = now()
WHERE repository_id = $1
AND deleted_at IS NULL
",
        repository_id
    )
    .execute(conn)
    .await?;
    Ok(())
}