1use 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}