termusiclib/config/v2/server/
config_extra.rs1use std::{borrow::Cow, fmt::Write as _, path::Path};
2
3use anyhow::{Context, Result};
4use figment::{
5 Figment,
6 providers::{Format, Toml},
7};
8use serde::{Deserialize, Serialize};
9
10use crate::utils::get_app_config_path;
11
12use super::ServerSettings;
13
14pub const FILE_NAME: &str = "server.toml";
16
17type ApplicationType = ServerSettings;
21
22#[derive(Debug, Clone, Serialize)]
26#[serde(untagged)]
27pub enum ServerConfigVersionedDefaulted<'a> {
28 Versioned(ServerConfigVersioned<'a>),
30 Unversioned(ServerSettings),
32}
33
34impl<'a, 'de> Deserialize<'de> for ServerConfigVersionedDefaulted<'a> {
36 fn deserialize<D>(deserializer: D) -> std::prelude::v1::Result<Self, D::Error>
37 where
38 D: serde::Deserializer<'de>,
39 {
40 let content =
42 <serde::__private::de::Content<'_> as serde::Deserialize>::deserialize(deserializer)?;
43 let deserializer = serde::__private::de::ContentRefDeserializer::<D::Error>::new(&content);
44
45 let mut err_res = String::new();
46
47 match <ServerConfigVersioned<'a>>::deserialize(deserializer)
48 .map(ServerConfigVersionedDefaulted::Versioned)
49 {
50 Ok(val) => return Ok(val),
51 Err(err) => {
52 let _ = write!(err_res, "{err:#}");
53 }
54 }
55 match ServerSettings::deserialize(deserializer)
56 .map(ServerConfigVersionedDefaulted::Unversioned)
57 {
58 Ok(val) => return Ok(val),
59 Err(err) => {
61 let err_str = err.to_string();
62 if err_str != err_res {
64 let _ = write!(err_res, "\n{err_str:#}");
65 }
66 }
67 }
68
69 Err(<D::Error as serde::de::Error>::custom(err_res))
70 }
71}
72
73impl ServerConfigVersionedDefaulted<'_> {
75 pub fn from_file<P: AsRef<Path>>(path: P) -> Result<Self> {
77 let path = path.as_ref();
78 {
79 let v1_config_path = path
80 .parent()
81 .context("expected server config path to have a parent")?
82 .join(super::super::super::v1::FILE_NAME);
83 if !path.exists() && v1_config_path.exists() {
84 info!("New config file does not exist, but old one does exist.");
85 return Self::migrate_from_v1(&v1_config_path, path);
86 }
87 }
88
89 if !path.exists() {
91 let config = ServerSettings::default();
92 Self::save_file(path, &config)?;
93 return Ok(Self::Unversioned(config));
94 }
95
96 let data: Self = Figment::new().merge(Toml::file(path)).extract()?;
97
98 Ok(data)
99 }
100
101 pub fn from_config_path() -> Result<Self> {
103 let server_config_path = get_app_config_path()?.join(FILE_NAME);
104
105 Self::from_file(server_config_path)
106 }
107
108 pub(in super::super) fn migrate_from_v1(_v1_path: &Path, v2_path: &Path) -> Result<Self> {
111 use super::super::super::v1::Settings;
112
113 info!("Migrating server config from v1 format to v2");
114
115 let old_settings = {
116 let mut settings = Settings::default();
117 settings.load()?;
118
119 settings
120 };
121
122 let new_settings = ServerSettings::try_from(old_settings)
123 .context("Failed to convert server config from v1 to v2 config")?;
124
125 Self::save_file(v2_path, &new_settings)?;
127
128 Ok(Self::Unversioned(new_settings))
129 }
130
131 pub fn save_file<'b, P: AsRef<Path>>(path: P, config: &'b ApplicationType) -> Result<()> {
135 let data = ServerConfigVersionedDefaulted::<'b>::Versioned(ServerConfigVersioned::V2(
137 Cow::Borrowed(config),
138 ));
139 std::fs::write(path, toml::to_string(&data)?)?;
140
141 Ok(())
142 }
143
144 pub fn save_config_path(config: &ApplicationType) -> Result<()> {
146 let server_config_path = get_app_config_path()?.join(FILE_NAME);
147
148 Self::save_file(server_config_path, config)
149 }
150
151 #[must_use]
155 pub fn into_settings(self) -> ApplicationType {
156 let versioned = match self {
157 ServerConfigVersionedDefaulted::Versioned(versioned) => versioned,
158 ServerConfigVersionedDefaulted::Unversioned(v) => return v,
159 };
160
161 versioned.into_settings()
162 }
163}
164
165#[derive(Debug, Clone, Deserialize, Serialize)]
167#[serde(tag = "version")]
168pub enum ServerConfigVersioned<'a> {
169 #[serde(rename = "2")]
173 V2(Cow<'a, ServerSettings>),
174}
175
176impl ServerConfigVersioned<'_> {
177 #[must_use]
181 pub fn into_settings(self) -> ApplicationType {
182 match self {
183 ServerConfigVersioned::V2(v) => v.into_owned(),
184 }
185 }
186}