1use std::{cmp::Ordering, str::FromStr};
2
3use crate::{utils::file_get_async, Settings};
4
5use super::{Proxy, ProxyType, RegexMatchConfig, RegexMatchConfigs};
6
7pub struct ExtraSettings {
9 pub enable_rule_generator: bool,
11 pub overwrite_original_rules: bool,
13 pub rename_array: RegexMatchConfigs,
15 pub emoji_array: RegexMatchConfigs,
17 pub add_emoji: bool,
19 pub remove_emoji: bool,
21 pub append_proxy_type: bool,
23 pub nodelist: bool,
25 pub sort_flag: bool,
27 pub filter_deprecated: bool,
29 pub clash_new_field_name: bool,
31 pub clash_script: bool,
33 pub surge_ssr_path: String,
35 pub managed_config_prefix: String,
37 pub quanx_dev_id: String,
39 pub udp: Option<bool>,
41 pub tfo: Option<bool>,
43 pub skip_cert_verify: Option<bool>,
45 pub tls13: Option<bool>,
47 pub clash_classical_ruleset: bool,
49 pub sort_script: String,
51 pub clash_proxies_style: String,
53 pub clash_proxy_groups_style: String,
55 pub authorized: bool,
57 #[cfg(feature = "js-runtime")]
59 pub js_context: Option<rquickjs::Context>,
60 #[cfg(feature = "js-runtime")]
62 pub js_runtime: Option<rquickjs::Runtime>,
63}
64
65impl std::fmt::Debug for ExtraSettings {
66 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
67 f.debug_struct("ExtraSettings")
68 .field("enable_rule_generator", &self.enable_rule_generator)
69 .field("overwrite_original_rules", &self.overwrite_original_rules)
70 .field("rename_array", &self.rename_array)
71 .field("emoji_array", &self.emoji_array)
72 .field("add_emoji", &self.add_emoji)
73 .field("remove_emoji", &self.remove_emoji)
74 .field("append_proxy_type", &self.append_proxy_type)
75 .field("nodelist", &self.nodelist)
76 .field("sort_flag", &self.sort_flag)
77 .field("filter_deprecated", &self.filter_deprecated)
78 .field("clash_new_field_name", &self.clash_new_field_name)
79 .field("clash_script", &self.clash_script)
80 .field("surge_ssr_path", &self.surge_ssr_path)
81 .field("managed_config_prefix", &self.managed_config_prefix)
82 .field("quanx_dev_id", &self.quanx_dev_id)
83 .field("udp", &self.udp)
84 .field("tfo", &self.tfo)
85 .field("skip_cert_verify", &self.skip_cert_verify)
86 .field("tls13", &self.tls13)
87 .field("clash_classical_ruleset", &self.clash_classical_ruleset)
88 .field("sort_script", &self.sort_script)
89 .field("clash_proxies_style", &self.clash_proxies_style)
90 .field("clash_proxy_groups_style", &self.clash_proxy_groups_style)
91 .field("authorized", &self.authorized)
92 .finish()
93 }
94}
95
96impl Default for ExtraSettings {
97 fn default() -> Self {
98 let global = Settings::current();
99
100 ExtraSettings {
101 enable_rule_generator: global.enable_rule_gen,
102 overwrite_original_rules: global.overwrite_original_rules,
103 rename_array: Vec::new(),
104 emoji_array: Vec::new(),
105 add_emoji: false,
106 remove_emoji: false,
107 append_proxy_type: false,
108 nodelist: false,
109 sort_flag: false,
110 filter_deprecated: false,
111 clash_new_field_name: true,
112 clash_script: false,
113 surge_ssr_path: global.surge_ssr_path.clone(),
114 managed_config_prefix: String::new(),
115 quanx_dev_id: String::new(),
116 udp: None,
117 tfo: None,
118 skip_cert_verify: None,
119 tls13: None,
120 clash_classical_ruleset: false,
121 sort_script: String::new(),
122 clash_proxies_style: if global.clash_proxies_style.is_empty() {
123 "flow".to_string()
124 } else {
125 global.clash_proxies_style.clone()
126 },
127 clash_proxy_groups_style: if global.clash_proxy_groups_style.is_empty() {
128 "flow".to_string()
129 } else {
130 global.clash_proxy_groups_style.clone()
131 },
132 authorized: false,
133 #[cfg(feature = "js-runtime")]
134 js_context: None,
135 #[cfg(feature = "js-runtime")]
136 js_runtime: None,
137 }
138 }
139}
140
141#[cfg(feature = "js-runtime")]
142impl ExtraSettings {
143 pub fn init_js_context(&mut self) {
144 if self.js_runtime.is_none() {
145 self.js_runtime = Some(rquickjs::Runtime::new().unwrap());
146 self.js_context =
147 Some(rquickjs::Context::base(&self.js_runtime.as_ref().unwrap()).unwrap());
148 }
149 }
150
151 pub fn eval_filter_function(
152 &mut self,
153 nodes: &mut Vec<Proxy>,
154 source_str: &str,
155 ) -> Result<(), Box<dyn std::error::Error>> {
156 self.init_js_context();
157 if let Some(context) = &mut self.js_context {
158 let mut error_thrown = None;
159 context.with(|ctx| {
160 match ctx.eval::<(), &str>(source_str) {
161 Ok(_) => (),
162 Err(e) => {
163 match e {
164 rquickjs::Error::Exception => {
165 log::error!(
166 "JavaScript eval throw exception: {}",
167 ctx.catch()
168 .try_into_string()
169 .unwrap()
170 .to_string()
171 .unwrap_or_default()
172 );
173 }
174 _ => {
175 log::error!("JavaScript eval error: {}", e);
176 }
177 }
178 error_thrown = Some(e);
179 return;
180 }
181 };
182 let filter_evaluated: rquickjs::Function =
183 match ctx.globals().get::<_, rquickjs::Function>("filter") {
184 Ok(value) => value,
185 Err(e) => {
186 log::error!("JavaScript eval get function error: {}", e);
187 return;
188 }
189 };
190
191 nodes.retain_mut(|node| {
192 match filter_evaluated.call::<(Proxy,), bool>((node.clone(),)) {
193 Ok(value) => value,
194 Err(e) => {
195 log::error!("JavaScript eval call function error: {}", e);
196 false
197 }
198 }
199 });
200 });
201 match error_thrown {
202 Some(e) => Err(e.into()),
203 None => {
204 log::info!("Filter function evaluated successfully");
205 Ok(())
206 }
207 }
208 } else {
209 Err("JavaScript context not initialized".into())
210 }
211 }
212
213 pub async fn eval_sort_nodes(
215 &mut self,
216 nodes: &mut Vec<Proxy>,
217 ) -> Result<(), Box<dyn std::error::Error>> {
218 if !self.sort_script.is_empty() {
219 let sort_script;
220 if self.sort_script.starts_with("path:") {
221 sort_script = file_get_async(&self.sort_script[5..], None).await?;
222 } else {
223 sort_script = self.sort_script.clone();
224 }
225 self.init_js_context();
226 let mut error_thrown = None;
227 if let Some(context) = &mut self.js_context {
228 context.with(|ctx| {
229 match ctx.eval::<(), &str>(&sort_script) {
230 Ok(_) => (),
231 Err(e) => match e {
232 rquickjs::Error::Exception => {
233 error_thrown = Some(e);
234 return;
235 }
236 _ => {
237 error_thrown = Some(e);
238 return;
239 }
240 },
241 }
242 let compare = match ctx.globals().get::<_, rquickjs::Function>("compare") {
243 Ok(value) => value,
244 Err(e) => {
245 log::error!("JavaScript eval get function error: {}", e);
246 return;
247 }
248 };
249 nodes.sort_by(|a, b| {
250 match compare.call::<(Proxy, Proxy), i32>((a.clone(), b.clone())) {
251 Ok(value) => {
252 if value > 0 {
253 Ordering::Greater
254 } else if value < 0 {
255 Ordering::Less
256 } else {
257 Ordering::Equal
258 }
259 }
260 Err(e) => {
261 log::error!("JavaScript eval call function error: {}", e);
262 return Ordering::Equal;
263 }
264 }
265 });
266 });
267 }
268 if let Some(e) = error_thrown {
269 return Err(e.into());
270 }
271 } else {
272 nodes.sort_by(|a, b| {
274 if a.proxy_type == ProxyType::Unknown {
275 return Ordering::Greater;
276 }
277 if b.proxy_type == ProxyType::Unknown {
278 return Ordering::Less;
279 }
280 a.remark.cmp(&b.remark)
281 });
282 }
283 Ok(())
284 }
285
286 pub async fn eval_get_rename_node_remark(
287 &self,
288 node: &Proxy,
289 match_script: String,
290 ) -> Result<String, Box<dyn std::error::Error>> {
291 let mut node_name = String::new();
292 if !match_script.is_empty() {
293 let mut error_thrown = None;
294 if let Some(context) = &self.js_context {
295 context.with(|ctx| {
296 match ctx.eval::<(), &str>(&match_script) {
297 Ok(_) => (),
298 Err(e) => {
299 error_thrown = Some(e);
300 return;
301 }
302 }
303 let rename = match ctx.globals().get::<_, rquickjs::Function>("rename") {
304 Ok(value) => value,
305 Err(e) => {
306 log::error!("JavaScript eval get function error: {}", e);
307 error_thrown = Some(e);
308 return;
309 }
310 };
311 match rename.call::<(Proxy,), String>((node.clone(),)) {
312 Ok(value) => {
313 if !value.is_empty() {
314 node_name = value;
315 }
316 }
317 Err(e) => {
318 log::error!("JavaScript eval call function error: {}", e);
319 error_thrown = Some(e);
320 return;
321 }
322 }
323 })
324 }
325 if let Some(e) = error_thrown {
326 return Err(e.into());
327 }
328 }
329 Ok(node_name)
330 }
331
332 pub async fn eval_get_emoji_node_remark(
333 &self,
334 node: &Proxy,
335 match_script: String,
336 ) -> Result<String, Box<dyn std::error::Error>> {
337 let mut node_emoji = String::new();
338 if !match_script.is_empty() {
339 let mut error_thrown = None;
340 if let Some(context) = &self.js_context {
341 context.with(|ctx| {
342 match ctx.eval::<(), &str>(&match_script) {
343 Ok(_) => (),
344 Err(e) => {
345 error_thrown = Some(e);
346 return;
347 }
348 }
349 let get_emoji = match ctx.globals().get::<_, rquickjs::Function>("getEmoji") {
350 Ok(value) => value,
351 Err(e) => {
352 log::error!("JavaScript eval get function error: {}", e);
353 error_thrown = Some(e);
354 return;
355 }
356 };
357 match get_emoji.call::<(Proxy,), String>((node.clone(),)) {
358 Ok(value) => {
359 if !value.is_empty() {
360 node_emoji = value;
361 }
362 }
363 Err(e) => {
364 log::error!("JavaScript eval call function error: {}", e);
365 error_thrown = Some(e);
366 return;
367 }
368 }
369 })
370 }
371 if let Some(e) = error_thrown {
372 return Err(e.into());
373 }
374 }
375 Ok(node_emoji)
376 }
377}
378
379#[cfg(not(feature = "js-runtime"))]
380impl ExtraSettings {
381 pub fn init_js_context(&mut self) {}
382 pub fn eval_filter_function(
383 &mut self,
384 _nodes: &mut Vec<Proxy>,
385 _source_str: &str,
386 ) -> Result<(), Box<dyn std::error::Error>> {
387 Err(
388 "JavaScript is not supported in this build, please enable js-runtime feature in cargo build"
389 .into(),
390 )
391 }
392 pub async fn eval_sort_nodes(
393 &mut self,
394 _nodes: &mut Vec<Proxy>,
395 ) -> Result<(), Box<dyn std::error::Error>> {
396 Err(
397 "JavaScript is not supported in this build, please enable js-runtime feature in cargo build"
398 .into(),
399 )
400 }
401 pub async fn eval_get_rename_node_remark(
402 &self,
403 _node: &Proxy,
404 _match_script: String,
405 ) -> Result<String, Box<dyn std::error::Error>> {
406 Err(
407 "JavaScript is not supported in this build, please enable js-runtime feature in cargo build"
408 .into(),
409 )
410 }
411 pub async fn eval_get_emoji_node_remark(
412 &self,
413 _node: &Proxy,
414 _match_script: String,
415 ) -> Result<String, Box<dyn std::error::Error>> {
416 Err(
417 "JavaScript is not supported in this build, please enable js-runtime feature in cargo build"
418 .into(),
419 )
420 }
421}