sericom_core/configs/
mod.rs1mod appearance;
6mod defaults;
7pub mod errors;
8pub use appearance::*;
9pub use defaults::*;
10
11use crate::{
12 configs::errors::{ConfigError, TomlError},
13 create_recursive,
14};
15use serde::Deserialize;
16use std::{io::Read, ops::Range, path::PathBuf, sync::OnceLock};
17
18pub static CONFIG: OnceLock<Config> = OnceLock::new();
25
26#[derive(Default, Debug, Deserialize, PartialEq)]
30pub struct Config {
31 #[serde(default)]
32 pub appearance: Appearance,
33 #[serde(default)]
34 pub defaults: Defaults,
35}
36
37impl Config {
38 fn apply_overrides(&mut self, overrides: ConfigOverride) {
39 if let Some(color) = overrides.color {
40 self.appearance.fg = color;
41 }
42 if let Some(dir) = overrides.out_dir {
43 self.defaults.out_dir = dir;
44 }
45 if let Some(script) = overrides.exit_script {
46 self.defaults.exit_script = Some(script);
47 }
48 }
49}
50
51pub fn initialize_config(overrides: ConfigOverride) -> miette::Result<(), ConfigError> {
64 let mut config: Config = if let Ok(config_file) = get_config_file() {
65 let mut file = std::fs::File::open(config_file).expect("File should exist");
66 let mut contents = String::new();
67 file.read_to_string(&mut contents)?;
68 toml::from_str(&contents).map_err(|e| {
69 TomlError::new(
70 e.span().unwrap_or(Range { start: 0, end: 0 }),
71 contents,
72 e.message().to_string(),
73 )
74 })?
75 } else {
76 Config::default()
77 };
78
79 config.apply_overrides(overrides);
80
81 CONFIG
82 .set(config)
83 .map_err(|_| ConfigError::AlreadyInitialized)?;
84 Ok(())
85}
86
87pub fn get_config() -> &'static Config {
95 CONFIG.get().expect("Config not initialized")
96}
97
98#[derive(Debug)]
99pub struct ConfigOverride {
101 pub color: Option<SeriColor>,
103 pub out_dir: Option<PathBuf>,
105 pub exit_script: Option<PathBuf>,
107}
108
109fn get_conf_dir() -> std::path::PathBuf {
110 let mut user_home_dir = std::env::home_dir().expect("Failed to get home directory");
111
112 if cfg!(windows) {
113 user_home_dir.push(".config\\sericom");
114 } else {
115 user_home_dir.push(".config/sericom");
116 }
117
118 let user_conf_dir = user_home_dir;
119 create_recursive!(user_conf_dir.as_path());
120
121 user_conf_dir
122}
123
124fn get_config_file() -> miette::Result<std::path::PathBuf, ConfigError> {
125 let mut conf_dir = get_conf_dir();
126 conf_dir.push("config.toml");
127 let conf_file = conf_dir;
128
129 if conf_file.exists() && conf_file.is_file() {
130 Ok(conf_file)
131 } else {
132 Err(std::io::Error::new(std::io::ErrorKind::NotFound, "Could not find config file.").into())
133 }
134}
135
136#[test]
137fn parse_test_config() -> miette::Result<()> {
138 use miette::IntoDiagnostic;
139 let file: Config = toml::from_str(
140 r#"
141 [appearance]
142 fg = "dark-grey"
143 bg = "red"
144
145 [defaults]
146 out-dir = "$HOME/.config"
147 exit-script = "~/.local/bin/format-cisco"
148 "#,
149 )
150 .into_diagnostic()?;
151
152 let parsed_conf = Config {
153 appearance: Appearance {
154 fg: SeriColor::DarkGrey,
155 bg: SeriColor::Red,
156 },
157 defaults: Defaults {
158 out_dir: PathBuf::from("/home/thomas/.config"),
159 exit_script: Some(PathBuf::from("/home/thomas/.local/bin/format-cisco")),
160 debug_dir: PathBuf::from("/home/thomas/Code/Work/sericom/sericom-core"),
161 },
163 };
164
165 assert_eq!(file, parsed_conf);
166 Ok(())
167}
168
169#[test]
170fn check_conf_dir_is_dir() {
171 let dir = get_conf_dir();
172 assert!(std::fs::metadata(dir).unwrap().is_dir())
173}
174
175#[test]
176fn valid_conf_dir() {
177 let dir = get_conf_dir();
178 if cfg!(target_family = "windows") {
179 assert_eq!(dir.to_str().unwrap(), "C:\\Users\\Thomas\\.config\\sericom")
180 } else {
181 assert_eq!(dir.to_str().unwrap(), "/home/thomas/.config/sericom")
182 }
183}
184
185#[test]
186fn get_expanded_path() {
187 use crate::path_utils::ExpandPaths;
188
189 let p = PathBuf::from("$HOME/.config/sericom/config.toml")
190 .get_expanded_path()
191 .unwrap();
192
193 let p2 = PathBuf::from("~/.config/sericom/config.toml")
194 .get_expanded_path()
195 .unwrap();
196
197 let p3 = PathBuf::from("$XDG_CONFIG_HOME/some/path")
198 .get_expanded_path()
199 .unwrap();
200
201 assert_eq!(p, PathBuf::from("/home/thomas/.config/sericom/config.toml"));
202 assert_eq!(
203 p2,
204 PathBuf::from("/home/thomas/.config/sericom/config.toml")
205 );
206 assert_eq!(p3, PathBuf::from("/home/thomas/.config/some/path"))
207}
208
209