summaryrefslogtreecommitdiff
path: root/src/config.rs
blob: 975437f542f032f43214a22eaf9d726294a4ac6a (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
use std::path::PathBuf;

use serde::Deserialize;
use tokio_stream::StreamExt;
use tokio_stream::wrappers::ReadDirStream;

use crate::theme::{Appearance, Theme, theme_from_str, theme_index};
use crate::{Error, environment};

#[derive(Debug, Clone, Default)]
pub struct Config {
    pub theme: Appearance,
    pub last_project: Option<PathBuf>,
}

impl Config {
    pub fn selected_theme(&self) -> iced::Theme {
        self.theme.selected.clone()
    }

    pub fn config_dir() -> PathBuf {
        let dir = environment::config_dir();

        if !dir.exists() {
            std::fs::create_dir_all(dir.as_path())
                .expect("expected permissions to create config folder");
        }
        dir
    }

    pub fn themes_dir() -> PathBuf {
        let dir = Self::config_dir().join("themes");

        if !dir.exists() {
            std::fs::create_dir_all(dir.as_path())
                .expect("expected permissions to create themes folder");
        }
        dir
    }

    pub fn config_file_path() -> PathBuf {
        Self::config_dir().join(environment::CONFIG_FILE_NAME)
    }

    pub async fn load() -> Result<Self, Error> {
        use tokio::fs;

        #[derive(Deserialize)]
        pub struct Configuration {
            #[serde(default)]
            pub theme: String,
            pub last_project: Option<PathBuf>,
        }

        let path = Self::config_file_path();
        if !path.try_exists()? {
            return Err(Error::ConfigMissing);
        }

        let content = fs::read_to_string(path).await?;

        let Configuration {
            theme,
            last_project,
        } = toml::from_str(content.as_ref())?;

        let theme = Self::load_theme(theme).await.unwrap_or_default();

        Ok(Self {
            theme,
            last_project,
        })
    }

    pub async fn load_theme(theme_name: String) -> Result<Appearance, Error> {
        use tokio::fs;

        let read_entry = async move |entry: fs::DirEntry| {
            let content = fs::read_to_string(entry.path()).await.ok()?;

            let theme: Theme = toml::from_str(content.as_ref()).ok()?;

            Some(iced::Theme::from(theme))
        };

        let mut selected = Theme::default().into();
        let mut all = iced::Theme::ALL.to_owned();
        all.push(Theme::default().into());

        if theme_index(&theme_name, iced::Theme::ALL).is_some() {
            selected = theme_from_str(None, &theme_name);
        }

        let mut stream =
            ReadDirStream::new(fs::read_dir(Self::themes_dir()).await?);
        while let Some(entry) = stream.next().await {
            let Ok(entry) = entry else {
                continue;
            };

            if let Some(theme) = read_entry(entry).await {
                if theme.to_string() == theme_name {
                    selected = theme.clone();
                }
                all.push(theme);
            }
        }

        Ok(Appearance {
            selected,
            all: all.into(),
        })
    }
}