serves3 1.2.0-beta.2

A very simple proxy to browse files from private S3 buckets
// SPDX-FileCopyrightText: © Matteo Settenvini <matteo.settenvini@montecristosoftware.eu>
// SPDX-License-Identifier: EUPL-1.2

use {
    object_store::{BackoffConfig, ObjectStore, RetryConfig, aws},
    rocket::serde::Deserialize,
    serde::de::Error,
    std::time::Duration,
};

#[derive(Deserialize)]
#[serde(crate = "rocket::serde")]
pub struct Settings {
    #[serde(deserialize_with = "deserialize_s3_bucket")]
    pub s3_bucket: Box<dyn ObjectStore>,
}

fn deserialize_s3_bucket<'de, D>(deserializer: D) -> Result<Box<dyn ObjectStore>, D::Error>
where
    D: serde::Deserializer<'de>,
{
    let config = S3Config::deserialize(deserializer)?;
    config.try_into().map_err(D::Error::custom)
}

#[derive(Deserialize)]
pub struct S3Config {
    pub name: String,
    pub endpoint: String,
    pub region: String,

    #[serde(default)]
    pub path_style: bool,

    pub access_key_id: String,
    pub secret_access_key: String,
}

impl TryInto<Box<dyn ObjectStore>> for S3Config {
    type Error = anyhow::Error;

    fn try_into(self) -> Result<Box<dyn ObjectStore>, Self::Error> {
        // TODO: support object stores other than than AWS
        let object_store = aws::AmazonS3Builder::new()
            .with_region(self.region)
            .with_endpoint(&self.endpoint)
            .with_bucket_name(&self.name)
            .with_access_key_id(self.access_key_id)
            .with_secret_access_key(self.secret_access_key)
            .with_virtual_hosted_style_request(!self.path_style)
            .with_allow_http(true)
            .with_retry(RetryConfig {
                max_retries: 1,
                backoff: BackoffConfig::default(),
                retry_timeout: Duration::from_millis(500),
            })
            .build()?;

        log::info!(
            "Serving contents from bucket {} at {}",
            self.endpoint,
            self.name,
        );

        Ok(Box::new(object_store))
    }
}