Skip to main content

modde_games/optiscaler/
spec.rs

1//! Serde spec types for the `OptiScaler` profile TOML format, covering both the
2//! owned deserialization shapes used when reading profiles and the borrowed
3//! serialization shapes used by [`serialize`] when exporting them.
4
5use serde::{Deserialize, Serialize};
6
7#[derive(Debug, Clone, Deserialize)]
8#[allow(clippy::struct_excessive_bools)]
9pub struct OptiScalerProfileSpec {
10    pub id: String,
11    pub name: String,
12    pub source_url: String,
13    pub tested_optiscaler_version: String,
14    pub source_mode: Option<String>,
15    pub goverlay_channel: Option<String>,
16    pub proxy_dll: String,
17    pub release_tag: Option<String>,
18    pub release_asset: Option<String>,
19    #[serde(default)]
20    pub wine_dll_overrides: Vec<String>,
21    #[serde(default)]
22    pub copy_companion_files: bool,
23    #[serde(default)]
24    pub enable_optipatcher: bool,
25    pub fsr4_variant: Option<String>,
26    #[serde(default)]
27    pub emulate_fp8: bool,
28    #[serde(default)]
29    pub spoof_dlss: bool,
30    #[serde(default)]
31    pub ini_overrides: Vec<IniOverrideSpec>,
32    #[serde(default)]
33    pub notes: String,
34}
35
36#[derive(Debug, Clone, Deserialize)]
37pub struct IniOverrideSpec {
38    pub key: String,
39    pub value: String,
40}
41
42#[derive(Debug, Clone, Deserialize, Default)]
43pub struct OptiScalerProfilesSpec {
44    #[serde(default)]
45    pub profile: Vec<OptiScalerProfileSpec>,
46}
47
48#[derive(Debug, Clone, Deserialize)]
49pub struct OptiScalerImportToml {
50    pub optiscaler: OptiScalerProfilesSpec,
51}
52
53#[derive(Debug, Clone, Serialize)]
54pub(crate) struct IniOverrideToml<'a> {
55    pub key: &'a str,
56    pub value: &'a str,
57}
58
59#[derive(Debug, Clone, Serialize)]
60#[allow(clippy::struct_excessive_bools)]
61pub(crate) struct OptiScalerProfileToml<'a> {
62    pub id: &'a str,
63    pub name: &'a str,
64    pub source_url: &'a str,
65    pub tested_optiscaler_version: &'a str,
66    #[serde(skip_serializing_if = "Option::is_none")]
67    pub source_mode: Option<&'a str>,
68    #[serde(skip_serializing_if = "Option::is_none")]
69    pub goverlay_channel: Option<&'a str>,
70    pub proxy_dll: &'a str,
71    #[serde(skip_serializing_if = "Option::is_none")]
72    pub release_tag: Option<&'a str>,
73    #[serde(skip_serializing_if = "Option::is_none")]
74    pub release_asset: Option<&'a str>,
75    #[serde(default, skip_serializing_if = "Vec::is_empty")]
76    pub wine_dll_overrides: Vec<&'a str>,
77    #[serde(default, skip_serializing_if = "std::ops::Not::not")]
78    pub copy_companion_files: bool,
79    #[serde(default, skip_serializing_if = "std::ops::Not::not")]
80    pub enable_optipatcher: bool,
81    #[serde(skip_serializing_if = "Option::is_none")]
82    pub fsr4_variant: Option<&'a str>,
83    #[serde(default, skip_serializing_if = "std::ops::Not::not")]
84    pub emulate_fp8: bool,
85    #[serde(default, skip_serializing_if = "std::ops::Not::not")]
86    pub spoof_dlss: bool,
87    #[serde(default, skip_serializing_if = "Vec::is_empty")]
88    pub ini_overrides: Vec<IniOverrideToml<'a>>,
89    #[serde(default, skip_serializing_if = "str::is_empty")]
90    pub notes: &'a str,
91}
92
93#[derive(Debug, Clone, Serialize)]
94pub(crate) struct OptiScalerProfilesToml<'a> {
95    #[serde(default, skip_serializing_if = "Vec::is_empty")]
96    pub profile: Vec<OptiScalerProfileToml<'a>>,
97}
98
99#[derive(Debug, Clone, Serialize)]
100pub(crate) struct OptiScalerExportToml<'a> {
101    pub optiscaler: OptiScalerProfilesToml<'a>,
102}
103
104pub fn serialize(profiles: &[crate::optiscaler::OptiScalerProfile]) -> anyhow::Result<String> {
105    let toml = OptiScalerExportToml {
106        optiscaler: OptiScalerProfilesToml {
107            profile: profiles
108                .iter()
109                .map(|profile| OptiScalerProfileToml {
110                    id: profile.id,
111                    name: profile.name,
112                    source_url: profile.source_url,
113                    tested_optiscaler_version: profile.tested_optiscaler_version,
114                    source_mode: profile.source_mode,
115                    goverlay_channel: profile.goverlay_channel,
116                    proxy_dll: profile.proxy_dll,
117                    release_tag: profile.release_tag,
118                    release_asset: profile.release_asset,
119                    wine_dll_overrides: profile.wine_dll_overrides.to_vec(),
120                    copy_companion_files: profile.copy_companion_files,
121                    enable_optipatcher: profile.enable_optipatcher,
122                    fsr4_variant: profile.fsr4_variant,
123                    emulate_fp8: profile.emulate_fp8,
124                    spoof_dlss: profile.spoof_dlss,
125                    ini_overrides: profile
126                        .ini_overrides
127                        .iter()
128                        .map(|override_| IniOverrideToml {
129                            key: override_.key,
130                            value: override_.value,
131                        })
132                        .collect(),
133                    notes: profile.notes,
134                })
135                .collect(),
136        },
137    };
138    let body = toml::to_string_pretty(&toml)?;
139    Ok(format!("[optiscaler]\n{body}"))
140}