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}