plex_api/media_container/preferences/
deserializer.rs

1//! We have a custom deserializer for preferences to avoid nasty untagged
2//! enums.
3//!
4//! Each setting represented in the server's response has a `type` field,
5//! which determines what data is stored inside the `value` and `default`
6//! fields. I know two ways how we parse it using only derives:
7//!
8//! 1. Put both fields inside a tagged enum. This leads to complications
9//!    when we expose the API for changing the value.
10//! 2. Put each field inside an untagged enum and just ignore the `type`.
11//!    This could lead to unexpected errors for the API users.
12//!
13//! If you know how to simplify all this — I'd be happy to hear from you :)
14
15use super::Setting;
16use serde::{
17    de::{self, MapAccess, Visitor},
18    Deserialize, Deserializer,
19};
20use std::str::FromStr;
21use thiserror::Error;
22
23impl<'de> Deserialize<'de> for Setting {
24    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
25    where
26        D: Deserializer<'de>,
27    {
28        #[derive(Debug, Deserialize, Hash, PartialEq, Eq)]
29        #[serde(field_identifier, rename_all = "camelCase")]
30        enum Field {
31            Id,
32            Label,
33            Summary,
34            Hidden,
35            Advanced,
36            Group,
37            Value,
38            Default,
39            r#Type,
40            EnumValues,
41        }
42
43        struct SettingVisitor;
44        impl<'de> Visitor<'de> for SettingVisitor {
45            type Value = Setting;
46
47            fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
48                formatter.write_str("struct Setting")
49            }
50
51            fn visit_map<V>(self, mut map: V) -> Result<Self::Value, V::Error>
52            where
53                V: MapAccess<'de>,
54            {
55                let mut id = None;
56                let mut label = None;
57                let mut summary = None;
58                let mut hidden = None;
59                let mut advanced = None;
60                let mut group = None;
61                let mut value = None;
62                let mut default = None;
63                let mut r#type = None;
64                let mut enum_values: Option<serde_json::Value> = None;
65
66                while let Some(key) = map.next_key()? {
67                    match key {
68                        Field::Id => {
69                            if id.is_some() {
70                                return Err(de::Error::duplicate_field("id"));
71                            }
72                            id = Some(map.next_value()?);
73                        }
74                        Field::Label => {
75                            if label.is_some() {
76                                return Err(de::Error::duplicate_field("label"));
77                            }
78                            label = Some(map.next_value()?);
79                        }
80                        Field::Summary => {
81                            if summary.is_some() {
82                                return Err(de::Error::duplicate_field("summary"));
83                            }
84                            summary = Some(map.next_value()?);
85                        }
86                        Field::Hidden => {
87                            if hidden.is_some() {
88                                return Err(de::Error::duplicate_field("hidden"));
89                            }
90                            hidden = Some(map.next_value()?);
91                        }
92                        Field::Advanced => {
93                            if advanced.is_some() {
94                                return Err(de::Error::duplicate_field("advanced"));
95                            }
96                            advanced = Some(map.next_value()?);
97                        }
98                        Field::Group => {
99                            if group.is_some() {
100                                return Err(de::Error::duplicate_field("group"));
101                            }
102                            group = Some(map.next_value()?);
103                        }
104                        Field::Value => {
105                            if value.is_some() {
106                                return Err(de::Error::duplicate_field("value"));
107                            }
108                            value = Some(map.next_value()?);
109                        }
110                        Field::Default => {
111                            if default.is_some() {
112                                return Err(de::Error::duplicate_field("default"));
113                            }
114                            default = Some(map.next_value()?);
115                        }
116                        Field::Type => {
117                            if r#type.is_some() {
118                                return Err(de::Error::duplicate_field("type"));
119                            }
120                            r#type = Some(map.next_value()?);
121                        }
122                        Field::EnumValues => {
123                            if enum_values.is_some() {
124                                return Err(de::Error::duplicate_field("enumValues"));
125                            }
126                            enum_values = Some(map.next_value()?);
127                        }
128                    }
129                }
130
131                let id: String = id.ok_or_else(|| de::Error::missing_field("id"))?;
132                let label: String = label.ok_or_else(|| de::Error::missing_field("label"))?;
133                let summary: String = summary.ok_or_else(|| de::Error::missing_field("summary"))?;
134                let hidden: bool = hidden.ok_or_else(|| de::Error::missing_field("hidden"))?;
135                let advanced: bool =
136                    advanced.ok_or_else(|| de::Error::missing_field("advanced"))?;
137                let group: String = group.ok_or_else(|| de::Error::missing_field("group"))?;
138                let r#type: String = r#type.ok_or_else(|| de::Error::missing_field("type"))?;
139                let value: serde_json::Value =
140                    value.ok_or_else(|| de::Error::missing_field("value"))?;
141                let default: serde_json::Value =
142                    default.ok_or_else(|| de::Error::missing_field("default"))?;
143
144                let mut suggested_values: Option<Vec<super::SettingEnumValue>> = None;
145
146                if let Some(serde_json::Value::String(enum_values)) = enum_values {
147                    let suggested_values_tmp: Result<Vec<_>, _> = enum_values
148                        .split('|')
149                        .map(super::SettingEnumValue::from_str)
150                        .collect();
151
152                    if let Ok(ok) = suggested_values_tmp {
153                        suggested_values = Some(ok);
154                    } else {
155                        return Err(de::Error::custom("invalid value inside enumValues"));
156                    }
157                }
158
159                match r#type.as_str() {
160                    "bool" => Ok(Setting {
161                        id,
162                        label,
163                        summary,
164                        hidden,
165                        advanced,
166                        group,
167                        value: super::Value::Bool(
168                            value
169                                .as_bool()
170                                .ok_or_else(|| de::Error::custom("bool expected"))?,
171                        ),
172                        default: super::Value::Bool(
173                            default
174                                .as_bool()
175                                .ok_or_else(|| de::Error::custom("bool expected"))?,
176                        ),
177                        suggested_values,
178                    }),
179                    "int" => {
180                        fn int_or_err<E>(v: serde_json::Value, id: &str) -> Result<i64, E>
181                        where
182                            E: de::Error,
183                        {
184                            match v {
185                                serde_json::Value::Number(n) => n
186                                    .as_i64()
187                                    .ok_or_else(|| de::Error::custom(format_args!("invalid value for {id}: {n}, expected numeric or numeric string"))),
188
189                                serde_json::Value::String(s) => s
190                                    .parse::<i64>()
191                                    .map_err(|_| de::Error::custom(format_args!("invalid value for {id}: \"{s}\", expected numeric or numeric string"))),
192
193                                _ => Err(de::Error::custom(format_args!("invalid value for {id}: {v:?}, expected numeric or numeric string"))),
194                            }
195                        }
196
197                        let value: i64 = int_or_err(value, &id)?;
198                        let default: i64 = int_or_err(default, &id)?;
199
200                        Ok(Setting {
201                            id,
202                            label,
203                            summary,
204                            hidden,
205                            advanced,
206                            group,
207                            value: super::Value::Int(value),
208                            default: super::Value::Int(default),
209                            suggested_values,
210                        })
211                    }
212                    "text" => Ok(Setting {
213                        id,
214                        label,
215                        summary,
216                        hidden,
217                        advanced,
218                        group,
219                        value: super::Value::Text(
220                            value
221                                .as_str()
222                                .ok_or_else(|| de::Error::custom("string expected"))?
223                                .to_owned(),
224                        ),
225                        default: super::Value::Text(
226                            default
227                                .as_str()
228                                .ok_or_else(|| de::Error::custom("string expected"))?
229                                .to_owned(),
230                        ),
231                        suggested_values,
232                    }),
233                    "double" => {
234                        fn double_or_err<E>(v: serde_json::Value, id: &str) -> Result<f64, E>
235                        where
236                            E: de::Error,
237                        {
238                            match v {
239                                serde_json::Value::Number(n) => n
240                                    .as_f64()
241                                    .ok_or_else(|| de::Error::custom(format_args!("invalid value for {id}: {n}, expected numeric or numeric string"))),
242
243                                serde_json::Value::String(s) => s
244                                    .parse::<f64>()
245                                    .map_err(|_| de::Error::custom(format_args!("invalid value for {id}: \"{s}\", expected numeric or numeric string"))),
246
247                                _ => Err(de::Error::custom(format_args!("invalid value for {id}: {v:?}, expected numeric or numeric string"))),
248                            }
249                        }
250
251                        let value: f64 = double_or_err(value, &id)?;
252                        let default: f64 = double_or_err(default, &id)?;
253
254                        Ok(Setting {
255                            id,
256                            label,
257                            summary,
258                            hidden,
259                            advanced,
260                            group,
261                            value: super::Value::Double(value),
262                            default: super::Value::Double(default),
263                            suggested_values,
264                        })
265                    }
266                    _ => Err(de::Error::unknown_variant(
267                        &r#type,
268                        &["bool", "int", "text", "double"],
269                    )),
270                }
271            }
272        }
273
274        const FIELDS: &[&str] = &[
275            "id",
276            "label",
277            "summary",
278            "hidden",
279            "advanced",
280            "group",
281            "value",
282            "default",
283            "type",
284            "enumValues",
285        ];
286        deserializer.deserialize_struct("Setting", FIELDS, SettingVisitor)
287    }
288}
289
290#[derive(Error, Debug)]
291pub enum SettingError {
292    #[error("Provided `enumValues` string incorrectly formatted")]
293    IncorrectEnumValuesFormat,
294}
295
296impl std::str::FromStr for super::SettingEnumValue {
297    type Err = SettingError;
298
299    fn from_str(s: &str) -> Result<Self, Self::Err> {
300        let value: Vec<_> = s.split(':').collect();
301
302        match value.len() {
303            1 => Ok(super::SettingEnumValue {
304                value: String::from(s),
305                hint: String::from(s),
306            }),
307            2 => Ok(super::SettingEnumValue {
308                value: String::from(value[0]),
309                hint: String::from(value[1]),
310            }),
311            _ => Err(SettingError::IncorrectEnumValuesFormat),
312        }
313    }
314}