OXYGENHUB

Coverage / crates/hub/src/db/releases.rs 84.2%

80 of 95 lines run, 18 of 48 functions · run #542 on main · run missed

1
//! Releases and their assets (hub spec §9).
2
3
use super::{now_ms, Pool};
4
use sqlx::FromRow;
5
6
#[derive(Clone, Debug, FromRow)]
7
pub struct Release {
8
    pub id: i64,
9
    pub repo_id: i64,
10
    pub tag: String,
11
    pub title: String,
12
    pub notes: String,
13
    pub prerelease: bool,
14
    pub author_id: Option<i64>,
15
    pub author: String,
16
    pub created_ms: i64,
17
}
18
19
#[derive(Clone, Debug, FromRow)]
20
pub struct Asset {
21
    pub id: i64,
22
    pub release_id: i64,
23
    pub name: String,
24
    pub size: i64,
25
    pub hash: String,
26
    pub content_type: String,
27
    pub downloads: i64,
28
    pub created_ms: i64,
29
}
30
3191×
pub async fn list(pool: &Pool, repo_id: i64) -> sqlx::Result<Vec<Release>> {
3291×
    sqlx::query_as("SELECT * FROM releases WHERE repo_id = $1 ORDER BY created_ms DESC, id DESC").bind(repo_id).fetch_all(pool).await
3391×
}
34
3515×
pub async fn get(pool: &Pool, repo_id: i64, tag: &str) -> sqlx::Result<Option<Release>> {
3615×
    sqlx::query_as("SELECT * FROM releases WHERE repo_id = $1 AND tag = $2").bind(repo_id).bind(tag).fetch_optional(pool).await
3715×
}
38
39
/// The newest release that is not a pre-release: the "latest" marker.
4067×
pub async fn latest(pool: &Pool, repo_id: i64) -> sqlx::Result<Option<Release>> {
4167×
    sqlx::query_as("SELECT * FROM releases WHERE repo_id = $1 AND prerelease = FALSE ORDER BY created_ms DESC, id DESC LIMIT 1")
4267×
        .bind(repo_id)
4367×
        .fetch_optional(pool)
4467×
        .await
4567×
}
46
4710×
pub async fn create(
4810×
    pool: &Pool,
4910×
    repo_id: i64,
5010×
    tag: &str,
5110×
    title: &str,
5210×
    notes: &str,
5310×
    prerelease: bool,
5410×
    author: Option<(i64, &str)>,
5510×
) -> sqlx::Result<Release> {
5610×
    sqlx::query_as("INSERT INTO releases (repo_id, tag, title, notes, prerelease, author_id, author, created_ms) VALUES ($1, $2, $3, $4, $5, $6, $7, $8) RETURNING *")
5710×
        .bind(repo_id)
5810×
        .bind(tag)
5910×
        .bind(title)
6010×
        .bind(notes)
6110×
        .bind(prerelease)
6210×
        .bind(author.map(|a| a.0))
6310×
        .bind(author.map(|a| a.1).unwrap_or(""))
6410×
        .bind(now_ms())
6510×
        .fetch_one(pool)
6610×
        .await
6710×
}
68
691×
pub async fn edit(pool: &Pool, id: i64, title: &str, notes: &str, prerelease: bool) -> sqlx::Result<()> {
701×
    sqlx::query("UPDATE releases SET title = $1, notes = $2, prerelease = $3 WHERE id = $4")
711×
        .bind(title)
721×
        .bind(notes)
731×
        .bind(prerelease)
741×
        .bind(id)
751×
        .execute(pool)
761×
        .await?;
771×
    Ok(())
781×
}
79
800×
pub async fn delete(pool: &Pool, id: i64) -> sqlx::Result<()> {
810×
    sqlx::query("DELETE FROM releases WHERE id = $1").bind(id).execute(pool).await?;
820×
    Ok(())
830×
}
84
8551×
pub async fn assets(pool: &Pool, release_id: i64) -> sqlx::Result<Vec<Asset>> {
8651×
    sqlx::query_as("SELECT * FROM release_assets WHERE release_id = $1 ORDER BY name").bind(release_id).fetch_all(pool).await
8751×
}
88
892×
pub async fn asset(pool: &Pool, release_id: i64, name: &str) -> sqlx::Result<Option<Asset>> {
902×
    sqlx::query_as("SELECT * FROM release_assets WHERE release_id = $1 AND name = $2")
912×
        .bind(release_id)
922×
        .bind(name)
932×
        .fetch_optional(pool)
942×
        .await
952×
}
96
9710×
pub async fn add_asset(pool: &Pool, release_id: i64, name: &str, size: i64, hash: &str, content_type: &str) -> sqlx::Result<Asset> {
9810×
    sqlx::query_as(
9910×
        "INSERT INTO release_assets (release_id, name, size, hash, content_type, created_ms) VALUES ($1, $2, $3, $4, $5, $6) \
10010×
         ON CONFLICT (release_id, name) DO UPDATE SET size = excluded.size, hash = excluded.hash, content_type = excluded.content_type, created_ms = excluded.created_ms RETURNING *",
10110×
    )
10210×
    .bind(release_id)
10310×
    .bind(name)
10410×
    .bind(size)
10510×
    .bind(hash)
10610×
    .bind(content_type)
10710×
    .bind(now_ms())
10810×
    .fetch_one(pool)
10910×
    .await
11010×
}
111
1120×
pub async fn delete_asset(pool: &Pool, id: i64) -> sqlx::Result<()> {
1130×
    sqlx::query("DELETE FROM release_assets WHERE id = $1").bind(id).execute(pool).await?;
1140×
    Ok(())
1150×
}
116
1171×
pub async fn count_download(pool: &Pool, id: i64) -> sqlx::Result<()> {
1181×
    sqlx::query("UPDATE release_assets SET downloads = downloads + 1 WHERE id = $1").bind(id).execute(pool).await?;
1191×
    Ok(())
1201×
}
121
122
/// Whether any asset of any release still names this content.
1230×
pub async fn hash_in_use(pool: &Pool, hash: &str) -> sqlx::Result<bool> {
1240×
    let n: i64 = sqlx::query_scalar("SELECT COUNT(*) FROM release_assets WHERE hash = $1").bind(hash).fetch_one(pool).await?;
1250×
    Ok(n > 0)
1260×
}