Skip to main content

the_way/
configuration.rs

1use std::io::Write;
2use std::path::{Path, PathBuf};
3use std::{env, fs, io};
4
5use clap::Parser;
6use color_eyre::Help;
7use directories_next::ProjectDirs;
8
9use crate::errors::LostTheWay;
10use crate::utils::{get_default_copy_cmd, NAME};
11
12#[derive(Parser, Debug)]
13pub enum ConfigCommand {
14    /// Prints / writes the default configuration options.
15    /// Set the generated config file as default by setting the $THE_WAY_CONFIG environment variable
16    Default {
17        /// File to save the configuration to.
18        file: Option<PathBuf>,
19    },
20    /// Prints location of currently set configuration file
21    Get,
22}
23
24#[derive(Debug, Clone, Serialize, Deserialize)]
25pub struct TheWayConfig {
26    /// Selected theme
27    pub(crate) theme: String,
28    /// Path to the directory containing the sled database files
29    pub(crate) db_dir: PathBuf,
30    /// Path to the directory containing theme files
31    pub(crate) themes_dir: PathBuf,
32    #[serde(default = "get_default_copy_cmd")]
33    pub(crate) copy_cmd: Option<String>,
34    /// Github token for the Gist API (i.e "gist" scope set)
35    pub(crate) github_access_token: Option<String>,
36    /// ID of Gist used for sync
37    pub gist_id: Option<String>,
38}
39
40/// Main project directory, cross-platform
41fn get_project_dir() -> color_eyre::Result<ProjectDirs> {
42    Ok(ProjectDirs::from("rs", "", NAME).ok_or(LostTheWay::Homeless)?)
43}
44
45impl Default for TheWayConfig {
46    fn default() -> Self {
47        let (db_dir, themes_dir, theme, copy_cmd) = {
48            let dir = get_project_dir().expect("Couldn't get project dir");
49            let data_dir = dir.data_dir();
50            if !data_dir.exists() {
51                fs::create_dir_all(data_dir).expect("Couldn't create data dir");
52            }
53            (
54                data_dir.join("the_way_db"),
55                data_dir.join("themes"),
56                String::from("base16-ocean.dark"),
57                get_default_copy_cmd(),
58            )
59        };
60        let config = Self {
61            theme,
62            db_dir,
63            themes_dir,
64            copy_cmd,
65            github_access_token: None,
66            gist_id: None,
67        };
68        config.make_dirs().unwrap();
69        config
70    }
71}
72
73impl TheWayConfig {
74    /// Write default configuration file
75    pub(crate) fn default_config(file: Option<&Path>) -> color_eyre::Result<()> {
76        let writer: Box<dyn Write> = match file {
77            Some(file) => Box::new(fs::File::create(file)?),
78            None => Box::new(io::stdout()),
79        };
80        let mut buffered = io::BufWriter::new(writer);
81        let copy_cmd = get_default_copy_cmd().ok_or(LostTheWay::NoDefaultCopyCommand)?;
82        let contents =
83            format!(
84                "theme = 'base16-ocean.dark'\ndb_dir = 'the_way_db'\nthemes_dir = 'the_way_themes'\ncopy_cmd = '{}'",
85                copy_cmd
86            );
87        write!(&mut buffered, "{contents}")?;
88        Ok(())
89    }
90
91    /// Print the filename of the currently set configuration file
92    pub(crate) fn print_config_location() -> color_eyre::Result<()> {
93        println!("{}", Self::get()?.to_string_lossy());
94        Ok(())
95    }
96
97    /// Make database and theme directories
98    fn make_dirs(&self) -> color_eyre::Result<()> {
99        if !self.db_dir.exists() {
100            fs::create_dir_all(&self.db_dir).map_err(|e: io::Error| LostTheWay::ConfigError {
101                message: format!("Couldn't create db dir {:?}, {e}", self.db_dir),
102            })?;
103        }
104        if !self.themes_dir.exists() {
105            fs::create_dir_all(&self.themes_dir).map_err(|e: io::Error| {
106                LostTheWay::ConfigError {
107                    message: format!("Couldn't create themes dir {:?}, {e}", self.themes_dir),
108                }
109            })?;
110        }
111        Ok(())
112    }
113
114    /// Get default configuration file location according to XDG specification
115    fn get_default_config_file() -> color_eyre::Result<PathBuf> {
116        Ok(confy::get_configuration_file_path(NAME, None)?)
117    }
118
119    /// Gets the current config file location
120    fn get() -> color_eyre::Result<PathBuf> {
121        let config_file = env::var("THE_WAY_CONFIG").ok();
122        match config_file {
123            Some(file) => {
124                let path = Path::new(&file).to_owned();
125                if path.exists() {
126                    Ok(path)
127                } else {
128                    let error: color_eyre::Result<PathBuf> = Err(LostTheWay::ConfigError {
129                        message: format!("No such file {file}"),
130                    }
131                    .into());
132                    error.suggestion(format!(
133                        "Use `the-way config default {file}` to write out the default configuration",
134                    ))
135                }
136            }
137            None => Self::get_default_config_file(),
138        }
139    }
140
141    /// Read config from default location
142    pub fn load() -> color_eyre::Result<Self> {
143        // Reads THE_WAY_CONFIG environment variable to get config file location
144        let config_file = env::var("THE_WAY_CONFIG").ok();
145        match config_file {
146            Some(file) => {
147                let path = Path::new(&file).to_owned();
148                if path.exists() {
149                    let config: Self = confy::load_path(Path::new(&file))?;
150                    config.make_dirs()?;
151                    Ok(config)
152                } else {
153                    let error: color_eyre::Result<Self> = Err(LostTheWay::ConfigError {
154                        message: format!("No such file {file}"),
155                    }
156                        .into());
157                    error.suggestion(format!(
158                        "Use `the-way config default {file}` to write out the default configuration",
159                    ))
160                }
161            }
162            None => {
163                Ok(confy::load(NAME, None).suggestion(LostTheWay::ConfigError {
164                    message: "Couldn't load from the default config location, maybe you don't have access? \
165                    Try running `the-way config default config_file.toml`, modify the generated file if necessary, \
166                then `export THE_WAY_CONFIG=<full/path/to/config_file.toml>`".into()
167                })?)
168            },
169        }
170    }
171
172    /// Write possibly modified config
173    pub(crate) fn store(&self) -> color_eyre::Result<()> {
174        // Reads THE_WAY_CONFIG environment variable to get config file location
175        let config_file = env::var("THE_WAY_CONFIG").ok();
176        match config_file {
177            Some(file) => confy::store_path(Path::new(&file), (*self).clone()).suggestion(LostTheWay::ConfigError {
178                message: "The current config_file location does not seem to have write access. \
179                   Use `export THE_WAY_CONFIG=<full/path/to/config_file.toml>` to set a new location".into()
180            })?,
181            None => confy::store(NAME, None, (*self).clone()).suggestion(LostTheWay::ConfigError {
182                message: "The current config_file location does not seem to have write access. \
183                    Use `export THE_WAY_CONFIG=<full/path/to/config_file.toml>` to set a new location".into()
184            })?,
185        };
186        Ok(())
187    }
188}