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

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

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

#[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 = |entry: fs::DirEntry| async move {
            let content = fs::read_to_string(entry.path()).await.ok()?;

            let theme: Theme = toml::from_str(content.as_ref()).ok()?;
            let name = entry.path().file_stem()?.to_string_lossy().to_string();

            Some(theme.into_iced_theme(name))
        };

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

        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;
            };

            let Some(file_name) = entry.file_name().to_str().map(String::from)
            else {
                continue;
            };

            if let Some(file_name) = file_name.strip_suffix(".toml") {
                if let Some(theme) = read_entry(entry).await {
                    if file_name == theme_name {
                        selected = theme.clone();
                    }
                    all.push(theme);
                }
            }
        }

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