bbc_news_cli/
config.rs

1use anyhow::{Context, Result};
2use serde::{Deserialize, Serialize};
3use std::path::PathBuf;
4use crate::theme::ThemeName;
5
6#[derive(Debug, Deserialize, Serialize)]
7pub struct Config {
8    #[serde(default)]
9    pub keybindings: KeyBindings,
10    #[serde(default)]
11    pub theme: ThemeName,
12}
13
14#[derive(Debug, Deserialize, Serialize)]
15pub struct KeyBindings {
16    #[serde(default = "default_quit")]
17    pub quit: char,
18    #[serde(default = "default_open")]
19    pub open: char,
20    #[serde(default = "default_open_new_tab")]
21    pub open_new_tab: char,
22    #[serde(default = "default_refresh")]
23    pub refresh: char,
24    #[serde(default = "default_latest")]
25    pub latest: char,
26    #[serde(default = "default_scroll_up")]
27    pub scroll_up: char,
28    #[serde(default = "default_scroll_down")]
29    pub scroll_down: char,
30    #[serde(default = "default_scroll_bottom")]
31    pub scroll_bottom: char,
32}
33
34fn default_quit() -> char { 'q' }
35fn default_open() -> char { 'o' }
36fn default_open_new_tab() -> char { 'O' }
37fn default_refresh() -> char { 'r' }
38fn default_latest() -> char { 'l' }
39fn default_scroll_up() -> char { 'k' }
40fn default_scroll_down() -> char { 'j' }
41fn default_scroll_bottom() -> char { 'G' }
42
43impl Default for KeyBindings {
44    fn default() -> Self {
45        Self {
46            quit: default_quit(),
47            open: default_open(),
48            open_new_tab: default_open_new_tab(),
49            refresh: default_refresh(),
50            latest: default_latest(),
51            scroll_up: default_scroll_up(),
52            scroll_down: default_scroll_down(),
53            scroll_bottom: default_scroll_bottom(),
54        }
55    }
56}
57
58impl Default for Config {
59    fn default() -> Self {
60        Self {
61            keybindings: KeyBindings::default(),
62            theme: ThemeName::default(),
63        }
64    }
65}
66
67pub fn load_config() -> Result<Config> {
68    let config_path = get_config_path()?;
69
70    if !config_path.exists() {
71        return Ok(Config::default());
72    }
73
74    let content = std::fs::read_to_string(&config_path)
75        .context("Failed to read config file")?;
76
77    let config: Config = toml::from_str(&content)
78        .context("Failed to parse config file")?;
79
80    Ok(config)
81}
82
83fn get_config_path() -> Result<PathBuf> {
84    // Try ~/.bbcli first
85    if let Some(home) = dirs::home_dir() {
86        let bbcli_path = home.join(".bbcli");
87        if bbcli_path.exists() {
88            return Ok(bbcli_path);
89        }
90    }
91
92    // Try ~/.config/bbcli
93    if let Some(config_dir) = dirs::config_dir() {
94        let bbcli_config = config_dir.join("bbcli");
95        return Ok(bbcli_config);
96    }
97
98    // Fallback to home directory
99    dirs::home_dir()
100        .map(|h| h.join(".bbcli"))
101        .context("Could not determine config path")
102}