bws-rs 0.1.1

rust s3 backend service framework, quick build yourself s3 gateway, or object storage
Documentation
use bws_rs::service::s3::*;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    use std::sync::Arc;
    let sg = Arc::new(StaticGetter {});
    let r = axum::Router::new()
        .layer(axum::middleware::from_fn(bws_rs::http::axum::handle_fn))
        .layer(axum::middleware::from_fn(
            bws_rs::http::axum::handle_authorization_middleware,
        ))
        .layer(axum::Extension(
            sg.clone() as Arc<dyn ListBucketHandler + Send + Sync>
        ))
        .layer(axum::Extension(
            sg.clone() as Arc<dyn GetObjectHandler + Send + Sync>
        ))
        .layer(axum::Extension(
            sg.clone() as Arc<dyn GetBucketLocationHandler + Send + Sync>
        ))
        .layer(axum::Extension(
            sg.clone() as Arc<dyn HeadHandler + Send + Sync>
        ))
        .layer(axum::Extension(sg.clone()
            as Arc<
                dyn bws_rs::authorization::AccesskeyStore + Send + Sync,
            >));
    let l = tokio::net::TcpListener::bind("127.0.0.1:9900").await?;
    axum::serve(l, r).await?;
    Ok(())
}

struct StaticGetter {}
impl bws_rs::authorization::AccesskeyStore for StaticGetter {
    fn get<'a>(
        &'a self,
        accesskey: &'a str,
    ) -> std::pin::Pin<
        Box<dyn 'a + Send + Sync + std::future::Future<Output = Result<Option<String>, String>>>,
    > {
        Box::pin(async move { Ok(Some(format!("{accesskey}12345"))) })
    }
}
impl ListBucketHandler for StaticGetter {
    fn handle<'a>(
        &'a self,
        _opt: &'a bws_rs::service::s3::ListBucketsOption,
    ) -> std::pin::Pin<
        Box<
            dyn 'a
                + Send
                + std::future::Future<Output = Result<Vec<bws_rs::service::s3::Bucket>, String>>,
        >,
    > {
        Box::pin(async move {
            Ok(vec![bws_rs::service::s3::Bucket {
                name: "itest".to_string(),
                creation_date: chrono::Utc::now().to_rfc3339(),
                bucket_region: "us-east-1".to_string(),
            }])
        })
    }
}
static OBJECT_CONTENT: &str = "hello world!";
static OBJECT_MD5: &str = "5eb63bbbe01eeed093cb22bb8f5acdc3";
impl HeadHandler for StaticGetter {
    fn lookup<'a>(
        &self,
        _bucket: &str,
        _object: &str,
    ) -> std::pin::Pin<
        Box<
            dyn 'a
                + Send
                + Sync
                + std::future::Future<Output = Result<Option<HeadObjectResult>, Error>>,
        >,
    > {
        Box::pin(async move {
            let mut obj = HeadObjectResult::default();
            obj.etag = Some(OBJECT_MD5.to_string());
            obj.content_length = Some(OBJECT_CONTENT.len());
            obj.last_modified = Some(
                chrono::Utc::now()
                    .format("%a, %d %b %Y %H:%M:%S GMT")
                    .to_string(),
            );
            Ok(Some(obj))
        })
    }
}
impl GetObjectHandler for StaticGetter {
    fn handle<'a>(
        &'a self,
        _bucket: &str,
        _object: &str,
        _opt: bws_rs::service::s3::GetObjectOption,
        out: tokio::sync::Mutex<
            std::pin::Pin<Box<dyn 'a + Send + bws_rs::utils::io::PollWrite + Unpin>>,
        >,
    ) -> std::pin::Pin<Box<dyn 'a + Send + std::future::Future<Output = Result<(), String>>>> {
        Box::pin(async move {
            let _ = out.lock().await.poll_write(OBJECT_CONTENT.as_bytes()).await;
            todo!()
        })
    }
}
impl GetBucketLocationHandler for StaticGetter {}