OXYGENHUB

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

72 of 95 lines run, 16 of 48 functions · run #464 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
3135×
pub async fn list(pool: &Pool, repo_id: i64) -> sqlx::Result<Vec<Release>> {
3235×
    sqlx::query_as("SELECT * FROM releases WHERE repo_id = $1 ORDER BY created_ms DESC, id DESC").bind(repo_id).fetch_all(pool).await
3335×
}
34
3513×
pub async fn get(pool: &Pool, repo_id: i64, tag: &str) -> sqlx::Result<Option<Release>> {
3613×
    sqlx::query_as("SELECT * FROM releases WHERE repo_id = $1 AND tag = $2").bind(repo_id).bind(tag).fetch_optional(pool).await
3713×
}
38
39
/// The newest release that is not a pre-release: the "latest" marker.
4065×
pub async fn latest(pool: &Pool, repo_id: i64) -> sqlx::Result<Option<Release>> {
4165×
    sqlx::query_as("SELECT * FROM releases WHERE repo_id = $1 AND prerelease = FALSE ORDER BY created_ms DESC, id DESC LIMIT 1")
4265×
        .bind(repo_id)
4365×
        .fetch_optional(pool)
4465×
        .await
4565×
}
46
478×
pub async fn create(
488×
    pool: &Pool,
498×
    repo_id: i64,
508×
    tag: &str,
518×
    title: &str,
528×
    notes: &str,
538×
    prerelease: bool,
548×
    author: Option<(i64, &str)>,
558×
) -> sqlx::Result<Release> {
568×
    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 *")
578×
        .bind(repo_id)
588×
        .bind(tag)
598×
        .bind(title)
608×
        .bind(notes)
618×
        .bind(prerelease)
628×
        .bind(author.map(|a| a.0))
638×
        .bind(author.map(|a| a.1).unwrap_or(""))
648×
        .bind(now_ms())
658×
        .fetch_one(pool)
668×
        .await
678×
}
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
8524×
pub async fn assets(pool: &Pool, release_id: i64) -> sqlx::Result<Vec<Asset>> {
8624×
    sqlx::query_as("SELECT * FROM release_assets WHERE release_id = $1 ORDER BY name").bind(release_id).fetch_all(pool).await
8724×
}
88
890×
pub async fn asset(pool: &Pool, release_id: i64, name: &str) -> sqlx::Result<Option<Asset>> {
900×
    sqlx::query_as("SELECT * FROM release_assets WHERE release_id = $1 AND name = $2")
910×
        .bind(release_id)
920×
        .bind(name)
930×
        .fetch_optional(pool)
940×
        .await
950×
}
96
978×
pub async fn add_asset(pool: &Pool, release_id: i64, name: &str, size: i64, hash: &str, content_type: &str) -> sqlx::Result<Asset> {
988×
    sqlx::query_as(
998×
        "INSERT INTO release_assets (release_id, name, size, hash, content_type, created_ms) VALUES ($1, $2, $3, $4, $5, $6) \
1008×
         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 *",
1018×
    )
1028×
    .bind(release_id)
1038×
    .bind(name)
1048×
    .bind(size)
1058×
    .bind(hash)
1068×
    .bind(content_type)
1078×
    .bind(now_ms())
1088×
    .fetch_one(pool)
1098×
    .await
1108×
}
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×
}