fn0-worker 0.3.55

Worker binary for the fn0 FaaS platform
use anyhow::{Context, Result};
use bytes::Bytes;
use fn0::StaticPageStorage;
use opendal::{ErrorKind, Operator};
use std::future::Future;
use std::pin::Pin;

#[derive(Clone)]
pub struct StaticPageStore {
    operator: Operator,
}

impl StaticPageStore {
    pub fn from_env() -> Result<Self> {
        let account_id = std::env::var("FN0_STATIC_ASSET_STORAGE_ACCOUNT_ID")
            .context("FN0_STATIC_ASSET_STORAGE_ACCOUNT_ID is required")?;
        let bucket = std::env::var("FN0_STATIC_ASSET_STORAGE_BUCKET")
            .context("FN0_STATIC_ASSET_STORAGE_BUCKET is required")?;
        let access_key_id = std::env::var("FN0_STATIC_ASSET_STORAGE_ACCESS_KEY_ID")
            .context("FN0_STATIC_ASSET_STORAGE_ACCESS_KEY_ID is required")?;
        let secret_access_key = std::env::var("FN0_STATIC_ASSET_STORAGE_SECRET_ACCESS_KEY")
            .context("FN0_STATIC_ASSET_STORAGE_SECRET_ACCESS_KEY is required")?;
        let endpoint = std::env::var("FN0_STATIC_ASSET_STORAGE_ENDPOINT")
            .unwrap_or_else(|_| format!("https://{account_id}.r2.cloudflarestorage.com"));

        let operator = Operator::new(
            opendal::services::S3::default()
                .bucket(&bucket)
                .region("auto")
                .endpoint(&endpoint)
                .access_key_id(&access_key_id)
                .secret_access_key(&secret_access_key)
                .disable_config_load()
                .disable_ec2_metadata(),
        )?
        .finish();

        Ok(Self { operator })
    }
}

impl StaticPageStorage for StaticPageStore {
    fn read<'storage>(
        &'storage self,
        key: &'storage str,
    ) -> Pin<Box<dyn Future<Output = anyhow::Result<Option<Bytes>>> + Send + 'storage>> {
        Box::pin(async move {
            match self.operator.read(key).await {
                Ok(buffer) => Ok(Some(buffer.to_bytes())),
                Err(error) if error.kind() == ErrorKind::NotFound => Ok(None),
                Err(error) => Err(error.into()),
            }
        })
    }

    fn write<'storage>(
        &'storage self,
        key: &'storage str,
        body: Bytes,
    ) -> Pin<Box<dyn Future<Output = anyhow::Result<()>> + Send + 'storage>> {
        Box::pin(async move {
            self.operator.write(key, body).await?;
            Ok(())
        })
    }
}