speki_backend/
config.rs

1use std::{
2    fs::File,
3    io::{Read, Write},
4    path::PathBuf,
5};
6
7use serde::{Deserialize, Deserializer, Serialize, Serializer};
8
9use crate::{common::open_file_with_vim, paths::get_share_path};
10
11#[derive(Clone, Debug, Serialize, Deserialize)]
12
13pub struct Config {
14    pub play_audio: bool,
15    pub show_images: bool,
16    pub download_media: bool,
17    #[serde(
18        serialize_with = "option_string_to_empty_string",
19        deserialize_with = "empty_string_to_option"
20    )]
21    pub git_remote: Option<String>,
22    #[serde(
23        serialize_with = "option_string_to_empty_string",
24        deserialize_with = "empty_string_to_option"
25    )]
26    pub gpt_key: Option<String>,
27}
28
29impl Config {
30    fn _config_path() -> PathBuf {
31        dirs::home_dir()
32            .unwrap()
33            .join(".config")
34            .join("speki")
35            .join("config.toml")
36    }
37
38    fn config_path() -> PathBuf {
39        get_share_path().join("config.toml")
40    }
41
42    pub fn edit_with_vim() {
43        open_file_with_vim(Self::config_path().as_path()).unwrap();
44    }
45
46    pub fn read_git_remote(&self) -> &Option<String> {
47        &self.git_remote
48    }
49
50    // Save the config to a file
51    pub fn save(&self) -> std::io::Result<()> {
52        let toml = toml::to_string(&self).expect("Failed to serialize config");
53        let mut file = File::create(Self::config_path())?;
54        file.write_all(toml.as_bytes())?;
55        Ok(())
56    }
57
58    // Load the config from a file
59    pub fn load() -> std::io::Result<Config> {
60        let mut file = match File::open(Self::config_path()) {
61            Ok(file) => file,
62            Err(_) => {
63                let _ =
64                    std::fs::rename(Self::config_path(), get_share_path().join("invalid_config"));
65                Self::default().save()?;
66                File::open(Self::config_path())?
67            }
68        };
69
70        let mut contents = String::new();
71        file.read_to_string(&mut contents)?;
72        let config: Config = toml::from_str(&contents).expect("Failed to deserialize config");
73        Ok(config)
74    }
75}
76
77impl Default for Config {
78    fn default() -> Self {
79        Self {
80            play_audio: true,
81            show_images: true,
82            download_media: true,
83            git_remote: None,
84            gpt_key: None,
85        }
86    }
87}
88
89fn option_string_to_empty_string<S>(
90    value: &Option<String>,
91    serializer: S,
92) -> Result<S::Ok, S::Error>
93where
94    S: Serializer,
95{
96    match value {
97        Some(v) => serializer.serialize_str(v),
98        None => serializer.serialize_str(""),
99    }
100}
101
102fn empty_string_to_option<'de, D>(deserializer: D) -> Result<Option<String>, D::Error>
103where
104    D: Deserializer<'de>,
105{
106    let s: String = Deserialize::deserialize(deserializer)?;
107    Ok(if s.is_empty() { None } else { Some(s) })
108}
109
110#[cfg(test)]
111mod tests {
112    use super::*;
113
114    #[test]
115    fn foo() {
116        let x = Config::config_path();
117        dbg!(x);
118    }
119}