1use crate::config::{ClientConfig, MaskedString, Platform};
2use anyhow::{bail, Context, Result};
3use std::str::FromStr;
4
5macro_rules! config_options {
6 ($(
7 $variant:ident => $key:literal,
8 get: $getter:expr,
9 set: $setter:expr
10 ),* $(,)?) => {
11 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
12 pub enum ConfigOption {
13 $($variant,)*
14 }
15
16 impl ConfigOption {
17 pub fn all() -> &'static [Self] {
18 &[$(Self::$variant,)*]
19 }
20
21 pub fn as_str(&self) -> &'static str {
22 match self {
23 $(Self::$variant => $key,)*
24 }
25 }
26
27
28 pub fn get_value(&self, config: &ClientConfig) -> Result<String> {
29 match self {
30 $(Self::$variant => $getter(config),)*
31 }
32 }
33
34 pub fn set_value(&self, config: &mut ClientConfig, value: &str) -> Result<()> {
35 match self {
36 $(Self::$variant => $setter(config, value)?,)*
37 }
38 Ok(())
39 }
40 }
41
42 impl FromStr for ConfigOption {
43 type Err = anyhow::Error;
44
45 fn from_str(s: &str) -> Result<Self, Self::Err> {
46 match s {
47 $($key => Ok(Self::$variant),)*
48 _ => bail!("Unknown config option: {}", s),
49 }
50 }
51 }
52 };
53}
54
55config_options! {
56 Server => "server",
57 get: |config: &ClientConfig| Ok(config.server.to_string()),
58 set: |config: &mut ClientConfig, value: &str| -> Result<(), anyhow::Error> {
59 config.server = value.parse().context("Invalid server URL")?;
60 Ok(())
61 },
62
63 Token => "token",
64 get: |config: &ClientConfig| Ok(config.token.as_ref().map_or("".to_string(), |t| t.to_string())),
65 set: |config: &mut ClientConfig, value: &str| -> Result<(), anyhow::Error> {
66 config.token = Some(MaskedString::from(value));
67 Ok(())
68 },
69
70 Hwid => "hwid",
71 get: |config: &ClientConfig| Ok(config.get_hwid()),
72 set: |config: &mut ClientConfig, value: &str| -> Result<(), anyhow::Error> {
73 config.hwid = Some(value.to_string());
74 Ok(())
75 },
76
77 HeartbeatTimeout => "heartbeat_timeout",
78 get: |config: &ClientConfig| Ok(config.heartbeat_timeout.to_string()),
79 set: |config: &mut ClientConfig, value: &str| -> Result<(), anyhow::Error> {
80 config.heartbeat_timeout = value.parse().context("Invalid heartbeat_timeout")?;
81 Ok(())
82 },
83
84 OneCHome => "1c_home",
85 get: |config: &ClientConfig| Ok(config.one_c_home.clone().unwrap_or_default()),
86 set: |config: &mut ClientConfig, value: &str| -> Result<(), anyhow::Error> {
87 config.one_c_home = if value.is_empty() { None } else { Some(value.to_string()) };
88 Ok(())
89 },
90
91 OneCPlatform => "1c_platform",
92 get: |config: &ClientConfig| Ok(config.one_c_platform.as_ref().map(|p| p.to_string()).unwrap_or_default()),
93 set: |config: &mut ClientConfig, value: &str| -> Result<(), anyhow::Error> {
94 config.one_c_platform = if value.is_empty() {
95 None
96 } else {
97 Some(value.parse::<Platform>().context("Invalid platform")?)
98 };
99 Ok(())
100 },
101
102 OneCPublishDir => "1c_publish_dir",
103 get: |config: &ClientConfig| Ok(config.one_c_publish_dir.clone().unwrap_or_default()),
104 set: |config: &mut ClientConfig, value: &str| -> Result<(), anyhow::Error> {
105 config.one_c_publish_dir = if value.is_empty() { None } else { Some(value.to_string()) };
106 Ok(())
107 },
108
109 MinecraftServer => "minecraft_server",
110 get: |config: &ClientConfig| Ok(config.minecraft_server.clone().unwrap_or_default()),
111 set: |config: &mut ClientConfig, value: &str| -> Result<(), anyhow::Error> {
112 config.minecraft_server = if value.is_empty() { None } else { Some(value.to_string()) };
113 Ok(())
114 },
115
116 MinecraftJavaOpts => "minecraft_java_opts",
117 get: |config: &ClientConfig| Ok(config.minecraft_java_opts.clone().unwrap_or_default()),
118 set: |config: &mut ClientConfig, value: &str| -> Result<(), anyhow::Error> {
119 config.minecraft_java_opts = if value.is_empty() { None } else { Some(value.to_string()) };
120 Ok(())
121 },
122
123 MinimizeToTrayOnClose => "minimize_to_tray_on_close",
124 get: |config: &ClientConfig| Ok(config.minimize_to_tray_on_close.map_or("false".to_string(), |v| v.to_string())),
125 set: |config: &mut ClientConfig, value: &str| -> Result<(), anyhow::Error> {
126 config.minimize_to_tray_on_close = Some(value.parse().context("Invalid boolean value")?);
127 Ok(())
128 },
129
130 MinimizeToTrayOnStart => "minimize_to_tray_on_start",
131 get: |config: &ClientConfig| Ok(config.minimize_to_tray_on_start.map_or("false".to_string(), |v| v.to_string())),
132 set: |config: &mut ClientConfig, value: &str| -> Result<(), anyhow::Error> {
133 config.minimize_to_tray_on_start = Some(value.parse().context("Invalid boolean value")?);
134 Ok(())
135 },
136
137 UnsafeTls => "unsafe_tls",
138 get: |config: &ClientConfig| Ok(config.transport.tls.as_ref().map_or("".to_string(), |tls| {
139 tls.danger_ignore_certificate_verification.map_or("".to_string(), |v| v.to_string())
140 })),
141 set: |config: &mut ClientConfig, value: &str| -> Result<(), anyhow::Error> {
142 let allow = value.parse().context("Invalid boolean value")?;
143 if config.transport.tls.is_none() {
144 config.transport.tls = Some(Default::default());
145 }
146 if let Some(tls) = config.transport.tls.as_mut() {
147 tls.danger_ignore_certificate_verification = Some(allow);
148 }
149 Ok(())
150 },
151}
152
153impl ClientConfig {
154 pub fn get_option(&self, option: ConfigOption) -> Result<String> {
155 option.get_value(self)
156 }
157
158 pub fn set_option(&mut self, option: ConfigOption, value: &str) -> Result<()> {
159 option.set_value(self, value)?;
160 self.save()
161 }
162
163 pub fn get_all_config_options(&self) -> Vec<(String, String)> {
164 ConfigOption::all()
165 .iter()
166 .filter_map(|&option| {
167 option
168 .get_value(self)
169 .ok()
170 .map(|value| (option.as_str().to_string(), value))
171 })
172 .collect()
173 }
174}