bunkr-client 0.2.1

A Rust library and CLI tool for uploading files to Bunkr.cr
Documentation
use serde::{Deserialize, Serialize};
#[cfg(feature = "cli")]
use std::fs;
#[cfg(feature = "cli")]
use std::path::PathBuf;
use anyhow::Result;

#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct Config {
    pub default_batch_size: Option<usize>,
    pub default_album_id: Option<String>,
    pub default_album_name: Option<String>,
    pub preprocess_videos: Option<bool>,
}

impl Default for Config {
    fn default() -> Self {
        Config {
            default_batch_size: Some(1),
            default_album_id: None,
            default_album_name: None,
            preprocess_videos: Some(true),
        }
    }
}

#[derive(Clone, Copy)]
enum ConfigKey {
    DefaultBatchSize,
    DefaultAlbumId,
    DefaultAlbumName,
    PreprocessVideos,
}

impl ConfigKey {
    fn as_str(&self) -> &str {
        match self {
            ConfigKey::DefaultBatchSize => "default_batch_size",
            ConfigKey::DefaultAlbumId => "default_album_id",
            ConfigKey::DefaultAlbumName => "default_album_name",
            ConfigKey::PreprocessVideos => "preprocess_videos",
        }
    }

    fn from_str(s: &str) -> Option<Self> {
        match s {
            "default_batch_size" => Some(ConfigKey::DefaultBatchSize),
            "default_album_id" => Some(ConfigKey::DefaultAlbumId),
            "default_album_name" => Some(ConfigKey::DefaultAlbumName),
            "preprocess_videos" => Some(ConfigKey::PreprocessVideos),
            _ => None,
        }
    }

    fn get(&self, config: &Config) -> String {
        match self {
            ConfigKey::DefaultBatchSize => config.default_batch_size.map(|v| v.to_string()).unwrap_or_else(|| "1".to_string()),
            ConfigKey::DefaultAlbumId => config.default_album_id.clone().unwrap_or_else(|| "none".to_string()),
            ConfigKey::DefaultAlbumName => config.default_album_name.clone().unwrap_or_else(|| "none".to_string()),
            ConfigKey::PreprocessVideos => config.preprocess_videos.map(|v| v.to_string()).unwrap_or_else(|| "true".to_string()),
        }
    }

    fn set(&self, config: &mut Config, value: &str) -> Result<()> {
        match self {
            ConfigKey::DefaultBatchSize => {
                config.default_batch_size = Some(value.parse()?);
            }
            ConfigKey::DefaultAlbumId => {
                config.default_album_id = if value == "none" { None } else { Some(value.to_string()) };
            }
            ConfigKey::DefaultAlbumName => {
                config.default_album_name = if value == "none" { None } else { Some(value.to_string()) };
            }
            ConfigKey::PreprocessVideos => {
                config.preprocess_videos = Some(value.parse()?);
            }
        }
        Ok(())
    }

    fn default(&self) -> String {
        match self {
            ConfigKey::DefaultBatchSize => "1".to_string(),
            ConfigKey::DefaultAlbumId => "none".to_string(),
            ConfigKey::DefaultAlbumName => "none".to_string(),
            ConfigKey::PreprocessVideos => "true".to_string(),
        }
    }

    const fn all() -> &'static [Self] {
        &[
            ConfigKey::DefaultBatchSize,
            ConfigKey::DefaultAlbumId,
            ConfigKey::DefaultAlbumName,
            ConfigKey::PreprocessVideos,
        ]
    }
}

impl Config {
    #[cfg(feature = "cli")]
    pub fn load() -> Result<Self> {
        let config_path = Self::config_path();
        if config_path.exists() {
            let content = fs::read_to_string(config_path)?;
            Ok(toml::from_str(&content)?)
        } else {
            Ok(Self::default())
        }
    }

    #[cfg(not(feature = "cli"))]
    pub fn load() -> Result<Self> {
        Ok(Self::default())
    }

    #[cfg(feature = "cli")]
    pub fn save(&self) -> Result<()> {
        let config_path = Self::config_path();
        if let Some(parent) = config_path.parent() {
            fs::create_dir_all(parent)?;
        }
        let content = toml::to_string(self)?;
        fs::write(config_path, content)?;
        Ok(())
    }

    #[cfg(not(feature = "cli"))]
    pub fn save(&self) -> Result<()> {
        Err(anyhow::anyhow!("CLI feature is not enabled."))
    }

    pub fn get_value(&self, key: &str) -> String {
        if let Some(k) = ConfigKey::from_str(key) {
            k.get(self)
        } else {
            "unknown key".to_string()
        }
    }

    pub fn set_value(&mut self, key: &str, value: &str) -> Result<()> {
        if let Some(k) = ConfigKey::from_str(key) {
            k.set(self, value)
        } else {
            Err(anyhow::anyhow!("Unknown key: {}", key))
        }
    }

    pub fn print_all(&self) {
        println!("Key                    Value     | Default");
        println!("─────────────────────────────────────────");
        for key in ConfigKey::all() {
            let current = key.get(self);
            let default = key.default();
            println!("{:<22} {:<9} | \x1b[3m{}\x1b[0m", key.as_str(), current, default);
        }
    }

    #[cfg(feature = "cli")]
    fn config_path() -> PathBuf {
        dirs::config_dir()
            .unwrap_or_else(|| PathBuf::from("."))
            .join("bunkr_client.toml")
    }
}