1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
|
//! 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()
}
}
}
|