imgforge 0.17.0

Fast and secure image proxy and transformation server
Documentation
use crate::caching::error::CacheError;
use crate::constants::*;
use serde::Deserialize;
use std::env::{self, VarError};

#[derive(Debug, Clone, Deserialize)]
pub enum CacheConfig {
    Memory {
        capacity: usize,
    },
    Disk {
        path: String,
        capacity: usize,
    },
    Hybrid {
        memory_capacity: usize,
        disk_path: String,
        disk_capacity: usize,
    },
}

impl CacheConfig {
    pub fn startup_log_message(config: Option<&Self>) -> String {
        match config {
            None => "Caching disabled".to_string(),
            Some(Self::Memory { capacity }) => format!("Caching enabled: memory (capacity={capacity})"),
            Some(Self::Disk { path, capacity }) => {
                format!("Caching enabled: disk (path={path}, capacity={capacity})")
            }
            Some(Self::Hybrid {
                memory_capacity,
                disk_path,
                disk_capacity,
            }) => format!(
                "Caching enabled: hybrid (memory_capacity={memory_capacity}, disk_path={disk_path}, disk_capacity={disk_capacity})"
            ),
        }
    }

    pub fn from_env() -> Result<Option<Self>, CacheError> {
        let cache_type = match env::var(ENV_CACHE_TYPE) {
            Ok(val) => val,
            Err(VarError::NotPresent) => return Ok(None),
            Err(source) => {
                return Err(CacheError::Environment {
                    name: ENV_CACHE_TYPE,
                    source,
                });
            }
        };

        match cache_type.to_lowercase().as_str() {
            "memory" => {
                let capacity = capacity_from_env(ENV_CACHE_MEMORY_CAPACITY, "1000")?;
                Ok(Some(CacheConfig::Memory { capacity }))
            }
            "disk" => {
                let path = required_env(ENV_CACHE_DISK_PATH)?;
                let capacity = capacity_from_env(ENV_CACHE_DISK_CAPACITY, "10000")?;
                Ok(Some(CacheConfig::Disk { path, capacity }))
            }
            "hybrid" => {
                let memory_capacity = capacity_from_env(ENV_CACHE_MEMORY_CAPACITY, "1000")?;
                let disk_path = required_env(ENV_CACHE_DISK_PATH)?;
                let disk_capacity = capacity_from_env(ENV_CACHE_DISK_CAPACITY, "10000")?;
                Ok(Some(CacheConfig::Hybrid {
                    memory_capacity,
                    disk_path,
                    disk_capacity,
                }))
            }
            _ => Err(CacheError::UnsupportedType { value: cache_type }),
        }
    }
}

fn required_env(name: &'static str) -> Result<String, CacheError> {
    env::var(name).map_err(|source| CacheError::Environment { name, source })
}

fn capacity_from_env(name: &'static str, default: &str) -> Result<usize, CacheError> {
    let value = match env::var(name) {
        Ok(value) => value,
        Err(VarError::NotPresent) => default.to_owned(),
        Err(source) => return Err(CacheError::Environment { name, source }),
    };

    value
        .parse()
        .map_err(|source| CacheError::InvalidCapacity { name, value, source })
}

#[cfg(test)]
mod tests {
    use super::CacheConfig;

    #[test]
    fn startup_log_message_describes_memory_cache() {
        let config = CacheConfig::Memory { capacity: 1000 };
        assert_eq!(
            CacheConfig::startup_log_message(Some(&config)),
            "Caching enabled: memory (capacity=1000)"
        );
    }

    #[test]
    fn startup_log_message_describes_disabled_cache() {
        assert_eq!(CacheConfig::startup_log_message(None), "Caching disabled");
    }
}