hyperion/models/backend/
file.rs1use 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}