1use std::{fs, path::PathBuf};
2
3use anyhow::{Context, Result};
4use serde::Deserialize;
5
6use crate::{cli::Cli, options::IconMode};
7
8#[derive(Debug, Clone, Deserialize)]
9#[serde(default)]
10pub struct Config {
11 pub theme: String,
12 pub wrap: bool,
13 pub tab_width: usize,
14 pub icons: IconMode,
15}
16
17impl Default for Config {
18 fn default() -> Self {
19 Self {
20 theme: "ember".to_string(),
21 wrap: true,
22 tab_width: 4,
23 icons: IconMode::NerdFont,
24 }
25 }
26}
27
28impl Config {
29 pub fn load() -> Result<Self> {
30 let Some(path) = config_path() else {
31 return Ok(Self::default());
32 };
33
34 if !path.exists() {
35 return Ok(Self::default());
36 }
37
38 let source = fs::read_to_string(&path)
39 .with_context(|| format!("failed to read config '{}'", path.display()))?;
40 let mut config: Self = toml::from_str(&source)
41 .with_context(|| format!("invalid config '{}'", path.display()))?;
42 config.tab_width = config.tab_width.clamp(1, 16);
43 Ok(config)
44 }
45
46 pub fn apply_cli(mut self, cli: &Cli) -> Self {
47 if let Some(theme) = &cli.theme {
48 self.theme = theme.clone();
49 }
50 if cli.wrap {
51 self.wrap = true;
52 }
53 if cli.no_wrap {
54 self.wrap = false;
55 }
56 if let Some(icons) = cli.icons {
57 self.icons = icons;
58 }
59 self
60 }
61}
62
63pub fn config_path() -> Option<PathBuf> {
64 dirs::config_dir().map(|dir| dir.join("iris").join("config.toml"))
65}