Skip to main content

moltbook_cli/
config.rs

1use crate::api::error::ApiError;
2use dirs::home_dir;
3use serde::{Deserialize, Serialize};
4use std::fs;
5use std::path::PathBuf;
6
7const CONFIG_DIR: &str = ".config/moltbook";
8const CONFIG_FILE: &str = "credentials.json";
9
10#[derive(Serialize, Deserialize, Debug)]
11pub struct Config {
12    pub api_key: String,
13    pub agent_name: String,
14}
15
16impl Config {
17    pub fn load() -> Result<Self, ApiError> {
18        let config_path = Self::get_config_path()?;
19
20        if !config_path.exists() {
21            return Err(ApiError::ConfigError(format!(
22                "Config file not found at: {}\nPlease create it with your API key.",
23                config_path.display()
24            )));
25        }
26
27        let content = fs::read_to_string(&config_path)
28            .map_err(|e| ApiError::ConfigError(format!("Failed to read config: {}", e)))?;
29
30        let config: Config = serde_json::from_str(&content)
31            .map_err(|e| ApiError::ConfigError(format!("Failed to parse config: {}", e)))?;
32
33        Ok(config)
34    }
35
36    fn get_config_path() -> Result<PathBuf, ApiError> {
37        if let Ok(config_dir) = std::env::var("MOLTBOOK_CONFIG_DIR") {
38            return Ok(PathBuf::from(config_dir).join(CONFIG_FILE));
39        }
40
41        let home = home_dir().ok_or_else(|| {
42            ApiError::ConfigError("Could not determine home directory".to_string())
43        })?;
44
45        Ok(home.join(CONFIG_DIR).join(CONFIG_FILE))
46    }
47
48    pub fn save(&self) -> Result<(), ApiError> {
49        let config_path = Self::get_config_path()?;
50        let config_dir = config_path.parent().unwrap();
51
52        if !config_dir.exists() {
53            fs::create_dir_all(config_dir).map_err(|e| {
54                ApiError::ConfigError(format!("Failed to create config dir: {}", e))
55            })?;
56        }
57
58        let content = serde_json::to_string_pretty(self)
59            .map_err(|e| ApiError::ConfigError(format!("Failed to serialize config: {}", e)))?;
60
61        fs::write(&config_path, content)
62            .map_err(|e| ApiError::ConfigError(format!("Failed to write config: {}", e)))?;
63
64        #[cfg(unix)]
65        {
66            use std::os::unix::fs::PermissionsExt;
67            let mut perms = fs::metadata(&config_path)
68                .map_err(|e| ApiError::ConfigError(format!("Failed to get metadata: {}", e)))?
69                .permissions();
70            perms.set_mode(0o600);
71            fs::set_permissions(&config_path, perms)
72                .map_err(|e| ApiError::ConfigError(format!("Failed to set permissions: {}", e)))?;
73        }
74
75        Ok(())
76    }
77}
78
79#[cfg(test)]
80mod tests {
81    use super::*;
82
83    #[test]
84    fn test_config_deserialization() {
85        let json = r#"{"api_key": "test_key", "agent_name": "test_agent"}"#;
86        let config: Config = serde_json::from_str(json).unwrap();
87        assert_eq!(config.api_key, "test_key");
88        assert_eq!(config.agent_name, "test_agent");
89    }
90
91    #[test]
92    fn test_missing_fields() {
93        let json = r#"{"api_key": "test_key"}"#;
94        let result: Result<Config, _> = serde_json::from_str(json);
95        assert!(result.is_err());
96    }
97}