leenfetch_core/config/
mod.rs

1pub mod defaults;
2pub mod settings;
3
4use self::{
5    defaults::DEFAULT_CONFIG,
6    settings::{Config, Flags, LayoutItem},
7};
8use dirs::config_dir;
9use json5;
10use std::io::Write;
11use std::path::PathBuf;
12use std::{
13    collections::HashMap,
14    fs::{self, File},
15};
16
17/// Loads the unified configuration from `config.jsonc`.
18fn load_config() -> Config {
19    let path = config_file("config.jsonc");
20    let data = fs::read_to_string(path).expect("Failed to read config.jsonc");
21    load_config_from_str(&data)
22}
23
24fn load_config_from_str(data: &str) -> Config {
25    json5::from_str(data).expect("Invalid JSONC in config.jsonc")
26}
27
28/// Loads configuration from a custom path when provided.
29pub fn load_config_at(path: Option<&str>) -> Result<Config, String> {
30    match path {
31        Some(custom_path) => {
32            let data = fs::read_to_string(custom_path)
33                .map_err(|err| format!("Failed to read config at {}: {}", custom_path, err))?;
34            Ok(load_config_from_str(&data))
35        }
36        None => Ok(load_config()),
37    }
38}
39
40/// Returns the built-in default configuration.
41pub fn default_config() -> Config {
42    load_config_from_str(DEFAULT_CONFIG)
43}
44
45/// Returns the built-in default layout section.
46pub fn default_layout() -> Vec<LayoutItem> {
47    default_config().layout
48}
49
50/// Loads the modules section from `config.jsonc`.
51///
52/// # Returns
53///
54/// A `Vec<LayoutItem>` containing the loaded module configuration.
55pub fn load_print_layout() -> Vec<LayoutItem> {
56    let layout = load_config().layout;
57    if layout.is_empty() {
58        default_layout()
59    } else {
60        layout
61    }
62}
63
64/// Loads the configuration flags from `config.jsonc`.
65///
66/// # Returns
67///
68/// A `Flags` struct containing the loaded configuration.
69pub fn load_flags() -> Flags {
70    load_config().flags
71}
72
73/// Generates the default unified configuration file.
74///
75/// Writes `config.jsonc` with the default contents. Returns a map with the filename
76/// and whether the operation succeeded, matching the previous multi-file API.
77pub fn generate_config_files() -> HashMap<String, bool> {
78    let mut results = HashMap::new();
79
80    let result = save_to_config_file("config.jsonc", DEFAULT_CONFIG).is_ok();
81    results.insert("config.jsonc".to_string(), result);
82
83    results
84}
85
86/// Saves the provided content to a configuration file with the specified file name.
87///
88/// The function ensures that the directory for the file exists, creating it if necessary.
89/// It then writes the content to the file, overwriting any existing content.
90///
91/// # Arguments
92///
93/// * `file_name` - A string slice that holds the name of the file to be created or overwritten.
94/// * `content` - A string slice containing the content to write to the file.
95///
96/// # Returns
97///
98/// A `Result` which is:
99///
100/// * `Ok(())` if the operation is successful.
101/// * `Err` if an error occurs during directory creation or file writing.
102fn save_to_config_file(file_name: &str, content: &str) -> std::io::Result<()> {
103    let path = config_file(file_name);
104
105    if let Some(parent) = path.parent() {
106        fs::create_dir_all(parent)?;
107    }
108
109    let mut file = File::create(&path)?;
110    file.write_all(content.as_bytes())?;
111
112    Ok(())
113}
114
115/// Deletes the generated configuration file.
116///
117/// This function is used in the `--clean-config` flag, and it removes the default config file.
118/// It returns a HashMap where the key is the config file name and the value indicates
119/// whether the file was deleted.
120pub fn delete_config_files() -> HashMap<String, bool> {
121    let mut results = HashMap::new();
122
123    let file = "config.jsonc";
124    let result = delete_config_file(file).is_ok();
125    results.insert(file.to_string(), result);
126
127    results
128}
129
130/// Deletes the given configuration file, returning an error if the operation fails.
131///
132/// This function is used by `delete_config_files` to remove the generated configuration.
133/// It does not report an error if the file does not exist.
134fn delete_config_file(file_name: &str) -> std::io::Result<()> {
135    let path = config_file(file_name);
136
137    if path.exists() {
138        std::fs::remove_file(path)?;
139    }
140
141    Ok(())
142}
143
144/// Returns a `PathBuf` for the configuration file with the given `name`.
145///
146/// If `XDG_CONFIG_HOME` is set, the function will return a path in that directory.
147/// If `XDG_CONFIG_HOME` is not set, the function will return a path in the current directory.
148///
149/// The returned path will have the "leenfetch" directory as its parent, and the given `name` as its file name.
150fn config_file(name: &str) -> PathBuf {
151    config_dir()
152        .unwrap_or_else(|| PathBuf::from("."))
153        .join("leenfetch")
154        .join(name)
155}
156
157/// Ensures that the configuration file exists.
158///
159/// This function creates the `leenfetch` directory and the config file if they do not exist.
160/// It will not overwrite an existing config file.
161///
162/// Returns a `HashMap` where the key is the config file name and the value indicates
163/// whether the file was created.
164pub fn ensure_config_files_exist() -> HashMap<String, bool> {
165    let mut results = HashMap::new();
166
167    let filename = "config.jsonc";
168    let created = ensure_config_file_exists(filename, DEFAULT_CONFIG).unwrap_or(false);
169    results.insert(filename.to_string(), created);
170
171    results
172}
173
174/// Ensures that the configuration file with the given `file_name` exists.
175///
176/// If the file does not exist, the function will create it with the given `default_content`.
177/// If the file already exists, the function will return `false` without modifying it.
178///
179/// # Returns
180///
181/// A `Result` which is `Ok(true)` if the file was created, or `Ok(false)` if the file already existed.
182/// If an error occurs while attempting to create the file, the function will return `Err`.
183fn ensure_config_file_exists(file_name: &str, default_content: &str) -> std::io::Result<bool> {
184    let path = config_file(file_name);
185
186    if path.exists() {
187        return Ok(false); // Already exists
188    }
189
190    save_to_config_file(file_name, default_content)?;
191    Ok(true) // Created
192}