Skip to main content

bunkr_client/config/
config.rs

1use serde::{Deserialize, Serialize};
2#[cfg(feature = "cli")]
3use std::fs;
4#[cfg(feature = "cli")]
5use std::path::PathBuf;
6use anyhow::Result;
7
8#[derive(Debug, Deserialize, Serialize, Clone)]
9pub struct Config {
10    pub default_batch_size: Option<usize>,
11    pub default_album_id: Option<String>,
12    pub default_album_name: Option<String>,
13    pub preprocess_videos: Option<bool>,
14}
15
16impl Default for Config {
17    fn default() -> Self {
18        Config {
19            default_batch_size: Some(1),
20            default_album_id: None,
21            default_album_name: None,
22            preprocess_videos: Some(true),
23        }
24    }
25}
26
27#[derive(Clone, Copy)]
28enum ConfigKey {
29    DefaultBatchSize,
30    DefaultAlbumId,
31    DefaultAlbumName,
32    PreprocessVideos,
33}
34
35impl ConfigKey {
36    fn as_str(&self) -> &str {
37        match self {
38            ConfigKey::DefaultBatchSize => "default_batch_size",
39            ConfigKey::DefaultAlbumId => "default_album_id",
40            ConfigKey::DefaultAlbumName => "default_album_name",
41            ConfigKey::PreprocessVideos => "preprocess_videos",
42        }
43    }
44
45    fn from_str(s: &str) -> Option<Self> {
46        match s {
47            "default_batch_size" => Some(ConfigKey::DefaultBatchSize),
48            "default_album_id" => Some(ConfigKey::DefaultAlbumId),
49            "default_album_name" => Some(ConfigKey::DefaultAlbumName),
50            "preprocess_videos" => Some(ConfigKey::PreprocessVideos),
51            _ => None,
52        }
53    }
54
55    fn get(&self, config: &Config) -> String {
56        match self {
57            ConfigKey::DefaultBatchSize => config.default_batch_size.map(|v| v.to_string()).unwrap_or_else(|| "1".to_string()),
58            ConfigKey::DefaultAlbumId => config.default_album_id.clone().unwrap_or_else(|| "none".to_string()),
59            ConfigKey::DefaultAlbumName => config.default_album_name.clone().unwrap_or_else(|| "none".to_string()),
60            ConfigKey::PreprocessVideos => config.preprocess_videos.map(|v| v.to_string()).unwrap_or_else(|| "true".to_string()),
61        }
62    }
63
64    fn set(&self, config: &mut Config, value: &str) -> Result<()> {
65        match self {
66            ConfigKey::DefaultBatchSize => {
67                config.default_batch_size = Some(value.parse()?);
68            }
69            ConfigKey::DefaultAlbumId => {
70                config.default_album_id = if value == "none" { None } else { Some(value.to_string()) };
71            }
72            ConfigKey::DefaultAlbumName => {
73                config.default_album_name = if value == "none" { None } else { Some(value.to_string()) };
74            }
75            ConfigKey::PreprocessVideos => {
76                config.preprocess_videos = Some(value.parse()?);
77            }
78        }
79        Ok(())
80    }
81
82    fn default(&self) -> String {
83        match self {
84            ConfigKey::DefaultBatchSize => "1".to_string(),
85            ConfigKey::DefaultAlbumId => "none".to_string(),
86            ConfigKey::DefaultAlbumName => "none".to_string(),
87            ConfigKey::PreprocessVideos => "true".to_string(),
88        }
89    }
90
91    const fn all() -> &'static [Self] {
92        &[
93            ConfigKey::DefaultBatchSize,
94            ConfigKey::DefaultAlbumId,
95            ConfigKey::DefaultAlbumName,
96            ConfigKey::PreprocessVideos,
97        ]
98    }
99}
100
101impl Config {
102    #[cfg(feature = "cli")]
103    pub fn load() -> Result<Self> {
104        let config_path = Self::config_path();
105        if config_path.exists() {
106            let content = fs::read_to_string(config_path)?;
107            Ok(toml::from_str(&content)?)
108        } else {
109            Ok(Self::default())
110        }
111    }
112
113    #[cfg(not(feature = "cli"))]
114    pub fn load() -> Result<Self> {
115        Ok(Self::default())
116    }
117
118    #[cfg(feature = "cli")]
119    pub fn save(&self) -> Result<()> {
120        let config_path = Self::config_path();
121        if let Some(parent) = config_path.parent() {
122            fs::create_dir_all(parent)?;
123        }
124        let content = toml::to_string(self)?;
125        fs::write(config_path, content)?;
126        Ok(())
127    }
128
129    #[cfg(not(feature = "cli"))]
130    pub fn save(&self) -> Result<()> {
131        Err(anyhow::anyhow!("CLI feature is not enabled."))
132    }
133
134    pub fn get_value(&self, key: &str) -> String {
135        if let Some(k) = ConfigKey::from_str(key) {
136            k.get(self)
137        } else {
138            "unknown key".to_string()
139        }
140    }
141
142    pub fn set_value(&mut self, key: &str, value: &str) -> Result<()> {
143        if let Some(k) = ConfigKey::from_str(key) {
144            k.set(self, value)
145        } else {
146            Err(anyhow::anyhow!("Unknown key: {}", key))
147        }
148    }
149
150    pub fn print_all(&self) {
151        println!("Key                    Value     | Default");
152        println!("─────────────────────────────────────────");
153        for key in ConfigKey::all() {
154            let current = key.get(self);
155            let default = key.default();
156            println!("{:<22} {:<9} | \x1b[3m{}\x1b[0m", key.as_str(), current, default);
157        }
158    }
159
160    #[cfg(feature = "cli")]
161    fn config_path() -> PathBuf {
162        dirs::config_dir()
163            .unwrap_or_else(|| PathBuf::from("."))
164            .join("bunkr_client.toml")
165    }
166}