1use crate::{
2 media_container::{
3 preferences::{Preferences as MediaContainerPreferences, Setting, Value},
4 MediaContainerWrapper,
5 },
6 url::SERVER_PREFS,
7 HttpClient, Result,
8};
9use std::mem::discriminant;
10
11#[derive(Debug, Clone)]
12pub struct Preferences<'a> {
13 client: HttpClient,
14 settings: Vec<Setting>,
15 changed: Vec<&'a str>,
16}
17
18impl<'a> Preferences<'a> {
19 #[tracing::instrument(level = "debug", skip(client))]
20 pub async fn new<C: Into<HttpClient>>(client: C) -> Result<Preferences<'a>> {
21 let client = client.into();
22 let mc: MediaContainerWrapper<MediaContainerPreferences> =
23 client.get(SERVER_PREFS).json().await?;
24
25 Ok(Preferences {
26 client,
27 settings: mc.media_container.settings,
28 changed: vec![],
29 })
30 }
31
32 pub fn get(&self, key: &str) -> Option<&Setting> {
33 self.settings.iter().find(|s| s.id == key)
34 }
35
36 pub fn all(&self) -> &[Setting] {
37 &self.settings
38 }
39
40 pub fn set(&mut self, key: &'a str, value: Value) -> Result<&mut Self> {
41 if let Some(setting) = self.settings.iter_mut().find(|s| s.id == key) {
42 if discriminant(&setting.value) == discriminant(&value) {
43 setting.value = value;
44 self.changed.push(key);
45 Ok(self)
46 } else {
47 Err(crate::Error::IncompatibleSettingValues)
48 }
49 } else {
50 Err(crate::Error::RequestedSettingNotFound(key.to_string()))
51 }
52 }
53
54 #[tracing::instrument(level = "debug")]
55 pub async fn commit(self) -> Result<Preferences<'a>> {
56 if self.changed.is_empty() {
57 return Ok(self);
58 }
59
60 let mut params = vec![];
61 for s in self
62 .settings
63 .iter()
64 .filter(|s| self.changed.contains(&s.id.as_str()))
65 {
66 params.push((s.id.as_str(), s.value.to_string()));
67 }
68
69 let uri = format!("{}?{}", SERVER_PREFS, serde_urlencoded::to_string(params)?);
70
71 self.client.put(uri).consume().await?;
72
73 Ok(Preferences {
74 client: self.client,
75 settings: self.settings,
76 changed: vec![],
77 })
78 }
79}