post3 0.1.0

Pluggable S3-compatible object storage — core library with PostgreSQL and filesystem backends
Documentation
use sqlx::PgPool;
use uuid::Uuid;

use crate::error::Post3Error;
use crate::models::BucketRow;

pub struct BucketsRepository<'a> {
    db: &'a PgPool,
}

impl<'a> BucketsRepository<'a> {
    pub fn new(db: &'a PgPool) -> Self {
        Self { db }
    }

    pub async fn create(&self, name: &str) -> Result<BucketRow, Post3Error> {
        let existing = self.get_by_name(name).await?;
        if existing.is_some() {
            return Err(Post3Error::BucketAlreadyExists(name.to_string()));
        }

        let row = sqlx::query_as::<_, BucketRow>(
            "INSERT INTO buckets (name) VALUES ($1) RETURNING *",
        )
        .bind(name)
        .fetch_one(self.db)
        .await?;

        Ok(row)
    }

    pub async fn get_by_name(&self, name: &str) -> Result<Option<BucketRow>, Post3Error> {
        let row =
            sqlx::query_as::<_, BucketRow>("SELECT * FROM buckets WHERE name = $1")
                .bind(name)
                .fetch_optional(self.db)
                .await?;

        Ok(row)
    }

    pub async fn list(&self) -> Result<Vec<BucketRow>, Post3Error> {
        let rows = sqlx::query_as::<_, BucketRow>(
            "SELECT * FROM buckets ORDER BY created_at ASC",
        )
        .fetch_all(self.db)
        .await?;

        Ok(rows)
    }

    pub async fn delete(&self, name: &str) -> Result<(), Post3Error> {
        let bucket = self
            .get_by_name(name)
            .await?
            .ok_or_else(|| Post3Error::BucketNotFound(name.to_string()))?;

        if !self.is_empty(bucket.id).await? {
            return Err(Post3Error::BucketNotEmpty(name.to_string()));
        }

        sqlx::query("DELETE FROM buckets WHERE id = $1")
            .bind(bucket.id)
            .execute(self.db)
            .await?;

        Ok(())
    }

    pub async fn is_empty(&self, bucket_id: Uuid) -> Result<bool, Post3Error> {
        let count: (i64,) = sqlx::query_as(
            "SELECT COUNT(*) FROM objects WHERE bucket_id = $1",
        )
        .bind(bucket_id)
        .fetch_one(self.db)
        .await?;

        Ok(count.0 == 0)
    }
}