summaryrefslogtreecommitdiffstats
path: root/src/config.rs
blob: 0154cda45cc28dc56439ad34d75680447c2b1487 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
use directories::ProjectDirs;
use serde::{Deserialize, Serialize};
use std::fmt;
use std::fs;
use std::io::Write;
use std::path::PathBuf;

use crate::error::{Error, Result};
use crate::utils;

#[derive(Deserialize, Serialize, Debug, Clone)]
#[serde(rename_all = "lowercase")] // TODO test this
pub enum SearchEngine {
    DuckDuckGo,
    Google,
    StackExchange,
}

#[derive(Deserialize, Serialize, Debug, Clone)]
#[serde(default)]
pub struct Config {
    pub api_key: Option<String>,
    pub limit: u16,
    pub lucky: bool,
    pub sites: Vec<String>,
    pub search_engine: SearchEngine,
}

impl fmt::Display for SearchEngine {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let s = match &self {
            SearchEngine::DuckDuckGo => "duckduckgo",
            SearchEngine::Google => "google",
            SearchEngine::StackExchange => "stackexchange",
        };
        write!(f, "{}", s)
    }
}

impl Default for SearchEngine {
    fn default() -> Self {
        SearchEngine::DuckDuckGo
    }
}

// TODO make a friender config file, like the colors.toml below
impl Default for Config {
    fn default() -> Self {
        Config {
            api_key: None,
            limit: 20,
            lucky: true,
            sites: vec![String::from("stackoverflow")],
            search_engine: SearchEngine::default(),
        }
    }
}

/// Get user config (writes default if none found)
pub fn user_config() -> Result<Config> {
    let project = project_dir()?;
    let dir = project.config_dir();
    fs::create_dir_all(&dir)?;
    let filename = config_file_name()?;

    match utils::open_file(&filename)? {
        None => {
            let def = Config::default();
            write_config(&def)?;
            Ok(def)
        }
        Some(file) => serde_yaml::from_reader(file)
            .map_err(|_| Error::MalformedFile(filename.clone()))
            .and_then(|cfg: Config| {
                if cfg.sites.is_empty() {
                    Err(Error::MalformedFile(filename))
                } else {
                    Ok(cfg)
                }
            }),
    }
}

pub fn set_api_key(key: String) -> Result<()> {
    let mut cfg = user_config()?;
    cfg.api_key = Some(key);
    write_config(&cfg)
}

/// Get project directory
pub fn project_dir() -> Result<ProjectDirs> {
    ProjectDirs::from("io", "Sam Tay", "so").ok_or_else(|| Error::ProjectDir)
}

pub fn theme_file_name() -> Result<PathBuf> {
    let name = project_dir()?.config_dir().join("colors.toml");
    if !name.as_path().exists() {
        let mut file = utils::create_file(&name)?;
        file.write_all(DEFAULT_COLORS_TOML.as_bytes())?;
    }
    Ok(name)
}

fn write_config(config: &Config) -> Result<()> {
    let filename = config_file_name()?;
    let file = utils::create_file(&filename)?;
    Ok(serde_yaml::to_writer(file, config)?)
}

// TODO consider switching to .toml to be consistent with colors.toml
fn config_file_name() -> Result<PathBuf> {
    Ok(project_dir()?.config_dir().join("config.yml"))
}

static DEFAULT_COLORS_TOML: &str = r##"
# Every field in a theme file is optional.

shadow = false
borders = "outset" # Alternatives are "none" and "simple"

# Base colors are
# red, green, blue, cyan, magenta, yellow, white and black.
#
# There are 3 ways to select a color:
# - The 16 base colors are selected by name:
#       "blue", "light red", "magenta", ...
# - Low-resolution colors use 3 characters, each <= 5:
#       "541", "003", ...
# - Full-resolution colors start with '#' and can be 3 or 6 hex digits:
#       "#1A6", "#123456", ...
[colors]
background = "default"

# If the terminal doesn't support custom color (like the linux TTY),
# non-base colors will be skipped.
shadow     = []
view       = "default"

# An array with a single value has the same effect as a simple value.
primary   = ["default"]
secondary = "cyan" # secondary style is used for code hightlighting
tertiary  = "green"

# Hex values can use lower or uppercase.
# (base color MUST be lowercase)
# If the value is an array, the first valid
# and supported color will be used.
title_primary   = ["BLUE", "red"] # `BLUE` will be skipped.
title_secondary = "yellow"

# Lower precision values can use only 3 digits.
highlight          = "yellow"
highlight_inactive = "light yellow"
"##;