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}