1use std::{collections::BTreeMap, convert::TryFrom};
2
3use async_trait::async_trait;
4
5use super::ConfigBackend;
6use crate::{
7 db::{models as db_models, Db},
8 models::*,
9};
10
11pub struct DbBackend {
12 db: Db,
13}
14
15impl DbBackend {
16 pub fn new(db: Db) -> Self {
17 Self::from(db)
18 }
19}
20
21impl From<Db> for DbBackend {
22 fn from(db: Db) -> Self {
23 Self { db }
24 }
25}
26
27#[async_trait]
28impl ConfigBackend for DbBackend {
29 async fn load(&mut self) -> Result<Config, ConfigError> {
30 let mut instances = BTreeMap::new();
31 let mut global = GlobalConfigCreator::default();
32
33 for instance in sqlx::query_as::<_, db_models::DbInstance>("SELECT * FROM instances")
34 .fetch_all(&mut *self.db)
35 .await?
36 .into_iter()
37 .map(Instance::try_from)
38 {
39 let instance = instance?;
40 instances.insert(instance.id, InstanceConfigCreator::new(instance));
41 }
42
43 for setting in sqlx::query_as::<_, db_models::DbSetting>("SELECT * FROM settings")
44 .fetch_all(&mut *self.db)
45 .await?
46 .into_iter()
47 .map(Setting::try_from)
48 {
49 let setting = setting?;
50 match setting.config {
51 SettingData::BackgroundEffect(config) => {
52 match instances.get_mut(
53 &setting
54 .hyperion_inst
55 .ok_or(ConfigError::MissingHyperionInst("backgroundEffect"))?,
56 ) {
57 Some(instance) => instance.background_effect = Some(config),
58 None => continue,
59 }
60 }
61 SettingData::BlackBorderDetector(config) => {
62 match instances.get_mut(
63 &setting
64 .hyperion_inst
65 .ok_or(ConfigError::MissingHyperionInst("blackborderdetector"))?,
66 ) {
67 Some(instance) => instance.black_border_detector = Some(config),
68 None => continue,
69 }
70 }
71 SettingData::BoblightServer(config) => {
72 match instances.get_mut(
73 &setting
74 .hyperion_inst
75 .ok_or(ConfigError::MissingHyperionInst("boblightServer"))?,
76 ) {
77 Some(instance) => instance.boblight_server = Some(config),
78 None => continue,
79 }
80 }
81 SettingData::ColorAdjustment(config) => {
82 match instances.get_mut(
83 &setting
84 .hyperion_inst
85 .ok_or(ConfigError::MissingHyperionInst("color"))?,
86 ) {
87 Some(instance) => instance.color = Some(config),
88 None => continue,
89 }
90 }
91 SettingData::Device(config) => {
92 match instances.get_mut(
93 &setting
94 .hyperion_inst
95 .ok_or(ConfigError::MissingHyperionInst("device"))?,
96 ) {
97 Some(instance) => instance.device = Some(config),
98 None => continue,
99 }
100 }
101 SettingData::Effects(config) => {
102 match instances.get_mut(
103 &setting
104 .hyperion_inst
105 .ok_or(ConfigError::MissingHyperionInst("effects"))?,
106 ) {
107 Some(instance) => instance.effects = Some(config),
108 None => continue,
109 }
110 }
111 SettingData::ForegroundEffect(config) => {
112 match instances.get_mut(
113 &setting
114 .hyperion_inst
115 .ok_or(ConfigError::MissingHyperionInst("foregroundEffect"))?,
116 ) {
117 Some(instance) => instance.foreground_effect = Some(config),
118 None => continue,
119 }
120 }
121 SettingData::InstanceCapture(config) => {
122 match instances.get_mut(
123 &setting
124 .hyperion_inst
125 .ok_or(ConfigError::MissingHyperionInst("instCapture"))?,
126 ) {
127 Some(instance) => instance.instance_capture = Some(config),
128 None => continue,
129 }
130 }
131 SettingData::LedConfig(config) => {
132 match instances.get_mut(
133 &setting
134 .hyperion_inst
135 .ok_or(ConfigError::MissingHyperionInst("ledConfig"))?,
136 ) {
137 Some(instance) => instance.led_config = Some(config),
138 None => continue,
139 }
140 }
141 SettingData::Leds(config) => {
142 match instances.get_mut(
143 &setting
144 .hyperion_inst
145 .ok_or(ConfigError::MissingHyperionInst("leds"))?,
146 ) {
147 Some(instance) => instance.leds = Some(config),
148 None => continue,
149 }
150 }
151 SettingData::Smoothing(config) => {
152 match instances.get_mut(
153 &setting
154 .hyperion_inst
155 .ok_or(ConfigError::MissingHyperionInst("smoothing"))?,
156 ) {
157 Some(instance) => instance.smoothing = Some(config),
158 None => continue,
159 }
160 }
161
162 SettingData::FlatbuffersServer(config) => {
163 global.flatbuffers_server = Some(config);
164 }
165 SettingData::Forwarder(config) => {
166 global.forwarder = Some(config);
167 }
168 SettingData::Framegrabber(config) => {
169 global.framegrabber = Some(config);
170 }
171 SettingData::General(config) => {
172 global.general = Some(config);
173 }
174 SettingData::GrabberV4L2(config) => {
175 global.grabber_v4l2 = Some(config);
176 }
177 SettingData::JsonServer(config) => {
178 global.json_server = Some(config);
179 }
180 SettingData::Logger(config) => {
181 global.logger = Some(config);
182 }
183 SettingData::Network(config) => {
184 global.network = Some(config);
185 }
186 SettingData::ProtoServer(config) => {
187 global.proto_server = Some(config);
188 }
189 SettingData::WebConfig(config) => {
190 global.web_config = Some(config);
191 }
192 SettingData::Hooks(config) => {
193 global.hooks = Some(config);
194 }
195 }
196 }
197
198 let meta: Result<Vec<_>, _> = sqlx::query_as::<_, db_models::DbMeta>("SELECT * FROM meta")
199 .fetch_all(&mut *self.db)
200 .await?
201 .into_iter()
202 .map(Meta::try_from)
203 .collect();
204 let meta = meta?;
205
206 let users: Result<Vec<_>, _> = sqlx::query_as::<_, db_models::DbUser>("SELECT * FROM auth")
207 .fetch_all(&mut *self.db)
208 .await?
209 .into_iter()
210 .map(User::try_from)
211 .collect();
212 let users = users?;
213
214 let instances: BTreeMap<i32, InstanceConfig> =
215 instances.into_iter().map(|(k, v)| (k, v.into())).collect();
216
217 let global: GlobalConfig = global.into();
218
219 debug!(
220 name = %global.general.name,
221 instances = %instances.len(),
222 meta = %meta.len(),
223 users = %users.len(),
224 "loaded",
225 );
226
227 Ok(Config {
228 instances,
229 global,
230 meta,
231 users,
232 })
233 }
234}
235
236struct InstanceConfigCreator {
237 instance: Instance,
238 background_effect: Option<BackgroundEffect>,
239 black_border_detector: Option<BlackBorderDetector>,
240 boblight_server: Option<BoblightServer>,
241 color: Option<ColorAdjustment>,
242 device: Option<Device>,
243 effects: Option<Effects>,
244 foreground_effect: Option<ForegroundEffect>,
245 instance_capture: Option<InstanceCapture>,
246 led_config: Option<LedConfig>,
247 leds: Option<Leds>,
248 smoothing: Option<Smoothing>,
249}
250
251impl From<InstanceConfigCreator> for InstanceConfig {
252 fn from(creator: InstanceConfigCreator) -> Self {
253 Self {
254 instance: creator.instance,
255 background_effect: creator.background_effect.unwrap_or_default(),
256 black_border_detector: creator.black_border_detector.unwrap_or_default(),
257 boblight_server: creator.boblight_server.unwrap_or_default(),
258 color: creator.color.unwrap_or_default(),
259 device: creator.device.unwrap_or_default(),
260 effects: creator.effects.unwrap_or_default(),
261 foreground_effect: creator.foreground_effect.unwrap_or_default(),
262 instance_capture: creator.instance_capture.unwrap_or_default(),
263 led_config: creator.led_config.unwrap_or_default(),
264 leds: creator.leds.unwrap_or_default(),
265 smoothing: creator.smoothing.unwrap_or_default(),
266 }
267 }
268}
269
270impl InstanceConfigCreator {
271 fn new(instance: Instance) -> Self {
272 Self {
273 instance,
274 background_effect: None,
275 black_border_detector: None,
276 boblight_server: None,
277 color: None,
278 device: None,
279 effects: None,
280 foreground_effect: None,
281 instance_capture: None,
282 led_config: None,
283 leds: None,
284 smoothing: None,
285 }
286 }
287}
288
289impl From<GlobalConfigCreator> for GlobalConfig {
290 fn from(creator: GlobalConfigCreator) -> Self {
291 Self {
292 flatbuffers_server: creator.flatbuffers_server.unwrap_or_default(),
293 forwarder: creator.forwarder.unwrap_or_default(),
294 framegrabber: creator.framegrabber.unwrap_or_default(),
295 general: creator.general.unwrap_or_default(),
296 grabber_v4l2: creator.grabber_v4l2.unwrap_or_default(),
297 json_server: creator.json_server.unwrap_or_default(),
298 logger: creator.logger.unwrap_or_default(),
299 network: creator.network.unwrap_or_default(),
300 proto_server: creator.proto_server.unwrap_or_default(),
301 web_config: creator.web_config.unwrap_or_default(),
302 hooks: creator.hooks.unwrap_or_default(),
303 }
304 }
305}
306
307#[derive(Default)]
308struct GlobalConfigCreator {
309 flatbuffers_server: Option<FlatbuffersServer>,
310 forwarder: Option<Forwarder>,
311 framegrabber: Option<Framegrabber>,
312 general: Option<General>,
313 grabber_v4l2: Option<GrabberV4L2>,
314 json_server: Option<JsonServer>,
315 logger: Option<Logger>,
316 network: Option<Network>,
317 proto_server: Option<ProtoServer>,
318 web_config: Option<WebConfig>,
319 hooks: Option<Hooks>,
320}