summaryrefslogblamecommitdiff
path: root/makima/src/daemon/cli/config.rs
blob: 8199b888b22187ddb85a86651dd65e91f6949105 (plain) (tree)































































































































                                                                                        
//! CLI configuration management.
//!
//! Handles loading and saving CLI configuration from ~/.makima/config.toml.
//! This is separate from daemon configuration and is used for interactive CLI commands.

use clap::Args;
use serde::{Deserialize, Serialize};
use std::fs;
use std::path::PathBuf;

/// Arguments for setting the API key
#[derive(Args, Debug, Clone)]
pub struct SetKeyArgs {
    /// The API key to save
    pub api_key: String,
}

/// Arguments for setting the API URL
#[derive(Args, Debug, Clone)]
pub struct SetUrlArgs {
    /// The API URL to save
    pub api_url: String,
}

/// CLI configuration stored in ~/.makima/config.toml
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct CliConfig {
    /// API URL for the makima server
    #[serde(default = "default_api_url")]
    pub api_url: String,

    /// API key for authentication
    #[serde(default)]
    pub api_key: Option<String>,
}

fn default_api_url() -> String {
    "https://api.makima.jp".to_string()
}

impl CliConfig {
    /// Get the config directory path (~/.makima)
    pub fn config_dir() -> Option<PathBuf> {
        dirs::home_dir().map(|h| h.join(".makima"))
    }

    /// Get the config file path (~/.makima/config.toml)
    pub fn config_path() -> Option<PathBuf> {
        Self::config_dir().map(|d| d.join("config.toml"))
    }

    /// Load CLI config from ~/.makima/config.toml
    /// Returns default config if file doesn't exist
    pub fn load() -> Self {
        let Some(path) = Self::config_path() else {
            return Self::default();
        };

        if !path.exists() {
            return Self::default();
        }

        match fs::read_to_string(&path) {
            Ok(contents) => {
                toml::from_str(&contents).unwrap_or_else(|e| {
                    eprintln!("Warning: Failed to parse {}: {}", path.display(), e);
                    Self::default()
                })
            }
            Err(e) => {
                eprintln!("Warning: Failed to read {}: {}", path.display(), e);
                Self::default()
            }
        }
    }

    /// Save CLI config to ~/.makima/config.toml
    pub fn save(&self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
        let Some(dir) = Self::config_dir() else {
            return Err("Could not determine home directory".into());
        };

        let Some(path) = Self::config_path() else {
            return Err("Could not determine config path".into());
        };

        // Create config directory if it doesn't exist
        if !dir.exists() {
            fs::create_dir_all(&dir)?;
        }

        let contents = toml::to_string_pretty(self)
            .map_err(|e| format!("Failed to serialize config: {}", e))?;
        fs::write(&path, contents)?;

        Ok(())
    }

    /// Get API key, preferring environment variable over config file
    pub fn get_api_key(&self) -> Option<String> {
        // Environment variable takes precedence
        if let Ok(key) = std::env::var("MAKIMA_API_KEY") {
            if !key.is_empty() {
                return Some(key);
            }
        }

        // Fall back to config file
        self.api_key.clone()
    }

    /// Get API URL, preferring environment variable over config file
    pub fn get_api_url(&self) -> String {
        // Environment variable takes precedence
        if let Ok(url) = std::env::var("MAKIMA_API_URL") {
            if !url.is_empty() {
                return url;
            }
        }

        // Fall back to config file, or default if empty
        if self.api_url.is_empty() {
            default_api_url()
        } else {
            self.api_url.clone()
        }
    }
}