hyperion/models/backend/
file.rs

1use std::{
2    collections::BTreeMap,
3    convert::TryFrom,
4    path::{Path, PathBuf},
5};
6
7use async_trait::async_trait;
8
9use super::ConfigBackend;
10use crate::models::*;
11
12pub trait ConfigExt {
13    fn to_string(&self) -> Result<String, toml::ser::Error>;
14}
15
16impl ConfigExt for Config {
17    fn to_string(&self) -> Result<String, toml::ser::Error> {
18        toml::to_string_pretty(&SerializableConfig::from(self))
19    }
20}
21
22pub struct FileBackend {
23    path: PathBuf,
24}
25
26impl FileBackend {
27    pub fn new(path: &Path) -> Self {
28        Self {
29            path: path.to_owned(),
30        }
31    }
32}
33
34#[async_trait]
35impl ConfigBackend for FileBackend {
36    async fn load(&mut self) -> Result<Config, ConfigError> {
37        use tokio::io::AsyncReadExt;
38
39        let mut file = tokio::fs::File::open(&self.path).await?;
40        let mut full = String::new();
41        file.read_to_string(&mut full).await?;
42
43        let config: DeserializableConfig = toml::from_str(&full)?;
44        Ok(config.try_into()?)
45    }
46}
47
48#[derive(Serialize)]
49struct SerializableConfig<'c> {
50    instances: BTreeMap<String, &'c InstanceConfig>,
51    #[serde(flatten)]
52    global: &'c GlobalConfig,
53    meta: &'c Vec<Meta>,
54    users: &'c Vec<User>,
55}
56
57impl<'c> From<&'c Config> for SerializableConfig<'c> {
58    fn from(config: &'c Config) -> Self {
59        Self {
60            instances: config
61                .instances
62                .iter()
63                .map(|(k, v)| (k.to_string(), v))
64                .collect(),
65            global: &config.global,
66            meta: &config.meta,
67            users: &config.users,
68        }
69    }
70}
71
72fn default_meta() -> Vec<Meta> {
73    vec![Meta::new()]
74}
75
76fn default_users() -> Vec<User> {
77    vec![User::hyperion()]
78}
79
80#[derive(Deserialize)]
81struct DeserializableConfig {
82    instances: BTreeMap<String, InstanceConfig>,
83    #[serde(default, flatten)]
84    global: GlobalConfig,
85    #[serde(default = "default_meta")]
86    meta: Vec<Meta>,
87    #[serde(default = "default_users")]
88    users: Vec<User>,
89}
90
91impl TryFrom<DeserializableConfig> for Config {
92    type Error = ConfigError;
93
94    fn try_from(value: DeserializableConfig) -> Result<Self, Self::Error> {
95        Ok(Self {
96            instances: value
97                .instances
98                .into_iter()
99                .map(|(k, v)| {
100                    k.parse()
101                        .map_err(|_| ConfigError::InvalidId(k.clone()))
102                        .map(|k| (k, v))
103                })
104                .collect::<Result<_, _>>()?,
105            global: value.global,
106            meta: value.meta,
107            users: value.users,
108        })
109    }
110}