y-sweet 0.0.9

A standalone Yjs CRDT server with built-in persistence and auth.
use anyhow::Result;
use async_trait::async_trait;
use s3::bucket::Bucket;
use s3::creds::Credentials;
use s3::error::S3Error;
use s3::region::Region;
use y_sweet_core::store::Store;

pub struct S3Store {
    bucket: Bucket,
    prefix: String,
}

impl S3Store {
    pub fn new(region: Region, bucket: String, prefix: String) -> Result<Self, anyhow::Error> {
        let credentials = Credentials::default()?;
        let bucket = Bucket::new(&bucket, region, credentials)?;
        Ok(Self { bucket, prefix })
    }

    fn make_key(&self, key: &str) -> String {
        format!("{}/{}", &self.prefix, key)
    }
}

#[async_trait]
impl Store for S3Store {
    async fn get(&self, key: &str) -> Result<Option<Vec<u8>>> {
        let response = self.bucket.get_object(&self.make_key(key)).await;

        match response {
            Ok(result) => Ok(Some(result.to_vec())),
            Err(S3Error::Http(404, _)) => Ok(None),
            Err(e) => Err(e.into()),
        }
    }

    async fn set(&self, key: &str, value: Vec<u8>) -> Result<()> {
        let _code = self.bucket.put_object(&self.make_key(key), &value).await?;
        Ok(())
    }

    async fn remove(&self, key: &str) -> Result<()> {
        let _code = self.bucket.delete_object(&self.make_key(key)).await?;
        Ok(())
    }

    async fn exists(&self, key: &str) -> Result<bool> {
        let response = self.bucket.head_object(&self.make_key(key)).await;

        match response {
            Ok(_) => Ok(true),
            Err(S3Error::Http(404, _)) => Ok(false),
            Err(e) => Err(e.into()),
        }
    }
}