flyer 3.0.1

HTTP framework for rust
Documentation
use std::{
    collections::HashMap,
    sync::{Arc, LazyLock, RwLock},
};

use anyhow::{anyhow, Result};
use futures::future::BoxFuture;

use crate::{request::form::File, utils::future::SendFuture};

pub mod local;

pub mod aws;

pub const DEFAULT_STORAGE: &'static str = "default";

static GLOBAL_STORAGE: LazyLock<RwLock<HashMap<String, Arc<dyn StorageErasure>>>> =
    LazyLock::new(|| RwLock::new(HashMap::new()));

#[allow(async_fn_in_trait)]
pub trait Storage: Send + Sync {
    async fn save_as(&self, folder: impl Into<String>, name: impl Into<String>, file: File) -> Result<String>;
    async fn save(&self, folder: impl Into<String>, file: File) -> Result<String>;
    async fn delete(&self, filename: impl Into<String>) -> Result<()>;
    async fn exists(&self, filename: impl Into<String>) -> Result<bool>;
    async fn get(&self, filename: impl Into<String>) -> Result<File>;
}

trait StorageErasure: Send + Sync {
    fn save_as<'a>(&'a self, folder: String, name: String, file: File) -> BoxFuture<'a, Result<String>>;
    fn save<'a>(&'a self, folder: String, file: File) -> BoxFuture<'a, Result<String>>;
    fn delete<'a>(&'a self, filename: String) -> BoxFuture<'a, Result<()>>;
    fn exists<'a>(&'a self, filename: String) -> BoxFuture<'a, Result<bool>>;
    fn get<'a>(&'a self, filename: String) -> BoxFuture<'a, Result<File>>;
}

impl<T: Storage + 'static> StorageErasure for T {
    fn save_as<'a>(&'a self, folder: String, name: String, file: File) -> BoxFuture<'a, Result<String>> {
        Box::pin(SendFuture(Storage::save_as(self, folder, name, file)))
    }

    fn save<'a>(&'a self, folder: String, file: File) -> BoxFuture<'a, Result<String>> {
        Box::pin(SendFuture(Storage::save(self, folder, file)))
    }

    fn delete<'a>(&'a self, filename: String) -> BoxFuture<'a, Result<()>> {
        Box::pin(SendFuture(Storage::delete(self, filename)))
    }

    fn exists<'a>(&'a self, filename: String) -> BoxFuture<'a, Result<bool>> {
        Box::pin(SendFuture(Storage::exists(self, filename)))
    }

    fn get<'a>(&'a self, filename: String) -> BoxFuture<'a, Result<File>> {
        Box::pin(SendFuture(Storage::get(self, filename)))
    }
}

pub fn add(name: impl Into<String>, storage: impl Storage + 'static) {
    GLOBAL_STORAGE
        .write()
        .expect("Storage registry lock poisoned")
        .insert(name.into(), Arc::new(storage));
}

fn get_storage(name: &str) -> Result<Arc<dyn StorageErasure>> {
    GLOBAL_STORAGE
        .read()
        .expect("Storage registry lock poisoned")
        .get(name)
        .cloned()
        .ok_or_else(|| anyhow!("Storage instance '{name}' not found"))
}

pub async fn save_as(storage: &str, folder: impl Into<String>, name: impl Into<String>, file: File) -> Result<String> {
    get_storage(storage)?
        .save_as(folder.into(), name.into(), file)
        .await
}

pub async fn save(storage: &str, folder: impl Into<String>, file: File) -> Result<String> {
    get_storage(storage)?
        .save(folder.into(), file)
        .await
}

pub async fn delete(storage: &str, filename: impl Into<String>) -> Result<()> {
    get_storage(storage)?
        .delete(filename.into())
        .await
}

pub async fn exists(storage: &str, filename: impl Into<String>) -> Result<bool> {
    get_storage(storage)?
        .exists(filename.into())
        .await
}

pub async fn get(storage: &str, filename: impl Into<String>) -> Result<File> {
    get_storage(storage)?
        .get(filename.into())
        .await
}