Skip to main content

proteus_lib/container/play_settings/
mod.rs

1//! Serde models for `play_settings.json` with versioned decoding.
2
3use log::{info, warn};
4use serde::{Deserialize, Deserializer, Serialize, Serializer};
5
6use crate::dsp::effects::AudioEffect;
7#[allow(deprecated)]
8#[deprecated(note = "Use DelayReverbSettings instead.")]
9pub use crate::dsp::effects::BasicReverbSettings;
10pub use crate::dsp::effects::{
11    CompressorSettings, ConvolutionReverbSettings, DelayReverbSettings, DistortionSettings,
12    HighPassFilterSettings, LimiterSettings, LowPassFilterSettings,
13};
14
15pub mod legacy;
16pub mod v1;
17pub mod v2;
18
19pub use legacy::{PlaySettingsLegacy, PlaySettingsLegacyFile, PlaySettingsTrackLegacy};
20pub use v1::{PlaySettingsV1, PlaySettingsV1File};
21pub use v2::{PlaySettingsV2, PlaySettingsV2File};
22
23/// Effect settings variants that can appear in the settings file.
24pub type EffectSettings = AudioEffect;
25
26/// Track-level configuration shared by newer settings versions.
27#[derive(Debug, Clone, Serialize, Deserialize)]
28pub struct SettingsTrack {
29    pub level: f32,
30    pub pan: f32,
31    pub ids: Vec<u32>,
32    pub name: String,
33    pub safe_name: String,
34}
35
36/// Wrapper allowing `play_settings` to be nested or flat.
37#[derive(Debug, Clone, Serialize, Deserialize)]
38#[serde(untagged)]
39pub enum PlaySettingsContainer<T> {
40    Nested { play_settings: T },
41    Flat(T),
42}
43
44impl<T> PlaySettingsContainer<T> {
45    /// Return the inner settings payload, regardless of nesting.
46    pub fn inner(&self) -> &T {
47        match self {
48            PlaySettingsContainer::Nested { play_settings } => play_settings,
49            PlaySettingsContainer::Flat(inner) => inner,
50        }
51    }
52}
53
54/// Versioned settings file representation.
55#[derive(Debug, Clone)]
56pub enum PlaySettingsFile {
57    Legacy(PlaySettingsLegacyFile),
58    V1(PlaySettingsV1File),
59    V2(PlaySettingsV2File),
60    Unknown {
61        encoder_version: Option<String>,
62        raw: serde_json::Value,
63    },
64}
65
66impl PlaySettingsFile {
67    /// Get the encoder version string, if known.
68    pub fn encoder_version(&self) -> Option<&str> {
69        match self {
70            PlaySettingsFile::Legacy(_) => None,
71            PlaySettingsFile::V1(_) => Some("1"),
72            PlaySettingsFile::V2(_) => Some("2"),
73            PlaySettingsFile::Unknown {
74                encoder_version, ..
75            } => encoder_version.as_deref(),
76        }
77    }
78}
79
80impl<'de> Deserialize<'de> for PlaySettingsFile {
81    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
82    where
83        D: Deserializer<'de>,
84    {
85        let value = serde_json::Value::deserialize(deserializer)?;
86        let encoder_version = value.get("encoder_version").and_then(|raw| match raw {
87            serde_json::Value::String(version) => Some(version.clone()),
88            serde_json::Value::Number(number) => number
89                .as_f64()
90                .map(|val| {
91                    if (val - 1.0).abs() < f64::EPSILON {
92                        "1".to_string()
93                    } else if (val - 2.0).abs() < f64::EPSILON {
94                        "2".to_string()
95                    } else {
96                        number.to_string()
97                    }
98                })
99                .or_else(|| Some(number.to_string())),
100            _ => None,
101        });
102
103        info!("Encoder version: {:?}", encoder_version);
104
105        let parsed = match encoder_version.as_deref() {
106            None => serde_json::from_value::<PlaySettingsLegacyFile>(value.clone())
107                .map(PlaySettingsFile::Legacy),
108            Some("1") => serde_json::from_value::<PlaySettingsV1File>(value.clone())
109                .map(PlaySettingsFile::V1),
110            Some("2") => serde_json::from_value::<PlaySettingsV2File>(value.clone())
111                .map(PlaySettingsFile::V2),
112            Some(version) => {
113                warn!("Unknown encoder version: {:?}", version);
114                return Ok(PlaySettingsFile::Unknown {
115                    encoder_version,
116                    raw: value,
117                });
118            }
119        };
120
121        parsed.or_else(|_| {
122            Ok(PlaySettingsFile::Unknown {
123                encoder_version,
124                raw: value,
125            })
126        })
127    }
128}
129
130impl Serialize for PlaySettingsFile {
131    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
132    where
133        S: Serializer,
134    {
135        fn with_version<T, S>(payload: &T, version: &str, serializer: S) -> Result<S::Ok, S::Error>
136        where
137            T: Serialize,
138            S: Serializer,
139        {
140            let mut value = serde_json::to_value(payload).map_err(serde::ser::Error::custom)?;
141            match value {
142                serde_json::Value::Object(ref mut map) => {
143                    map.insert(
144                        "encoder_version".to_string(),
145                        serde_json::Value::String(version.to_string()),
146                    );
147                }
148                other => {
149                    let mut map = serde_json::Map::new();
150                    map.insert(
151                        "encoder_version".to_string(),
152                        serde_json::Value::String(version.to_string()),
153                    );
154                    map.insert("play_settings".to_string(), other);
155                    value = serde_json::Value::Object(map);
156                }
157            }
158            value.serialize(serializer)
159        }
160
161        match self {
162            PlaySettingsFile::Legacy(file) => file.serialize(serializer),
163            PlaySettingsFile::V1(file) => with_version(file, "1", serializer),
164            PlaySettingsFile::V2(file) => with_version(file, "2", serializer),
165            PlaySettingsFile::Unknown { raw, .. } => raw.serialize(serializer),
166        }
167    }
168}