1use std::error::Error;
6use std::path::PathBuf;
7use std::str::FromStr;
8
9use aimcal_core::{APP_NAME, Config as CoreConfig};
10use colored::Colorize;
11
12pub async fn parse_config(path: Option<PathBuf>) -> Result<(CoreConfig, Config), Box<dyn Error>> {
13 let path = match path {
14 Some(path) => path,
15 None => {
16 let config = get_config_dir()?.join(format!("{APP_NAME}/config.toml"));
19 if !config.exists() {
20 return Err(format!("No config found at: {}", config.display()).into());
21 }
22 config
23 }
24 };
25
26 let content = std::fs::read_to_string(&path)
27 .map_err(|e| format!("Failed to read config file at {}: {}", path.display(), e))?;
28
29 match content.parse::<ConfigRaw>() {
30 Ok(raw) => Ok((raw.core, Config {})),
31 Err(err) => {
32 match content.parse::<ConfigLegacy>() {
34 Ok(legacy) => {
35 println!(
36 "{} Please update your config file by moving all entry to `[core]` sub-table",
37 "Warning:".yellow(),
38 );
39 Ok((legacy.0, Config {}))
40 }
41 Err(_) => Err(err),
42 }
43 }
44 }
45}
46
47#[derive(Debug, Clone, Copy, serde::Deserialize)]
49pub struct Config;
50
51#[derive(Debug, serde::Deserialize)]
52struct ConfigLegacy(CoreConfig);
53
54impl FromStr for ConfigLegacy {
55 type Err = Box<dyn Error>;
56
57 fn from_str(s: &str) -> Result<Self, Self::Err> {
58 Ok(toml::from_str(s)?)
59 }
60}
61
62#[derive(Debug, serde::Deserialize)]
63struct ConfigRaw {
64 core: CoreConfig,
65}
66
67impl FromStr for ConfigRaw {
68 type Err = Box<dyn Error>;
69
70 fn from_str(s: &str) -> Result<Self, Self::Err> {
71 Ok(toml::from_str(s)?)
72 }
73}
74
75fn get_config_dir() -> Result<PathBuf, Box<dyn Error>> {
76 #[cfg(unix)]
77 let config_dir = xdg::BaseDirectories::new().get_config_home();
78 #[cfg(windows)]
79 let config_dir = dirs::config_dir();
80 config_dir.ok_or("User-specific home directory not found".into())
81}