2022-08-12 03:20:55 +08:00
|
|
|
|
use super::use_lowercase;
|
2024-03-15 19:58:22 +08:00
|
|
|
|
use anyhow::{Error, Result};
|
2022-08-12 03:20:55 +08:00
|
|
|
|
use serde_yaml::Mapping;
|
2022-08-11 02:55:10 +08:00
|
|
|
|
|
2024-07-04 23:11:54 +08:00
|
|
|
|
pub fn use_script(
|
|
|
|
|
|
script: String,
|
|
|
|
|
|
config: Mapping,
|
|
|
|
|
|
name: String,
|
|
|
|
|
|
) -> Result<(Mapping, Vec<(String, String)>)> {
|
2024-03-15 19:58:22 +08:00
|
|
|
|
use boa_engine::{native_function::NativeFunction, Context, JsValue, Source};
|
2025-08-01 23:02:11 +08:00
|
|
|
|
use std::{cell::RefCell, rc::Rc};
|
2024-03-15 19:58:22 +08:00
|
|
|
|
let mut context = Context::default();
|
2022-08-11 02:55:10 +08:00
|
|
|
|
|
2025-08-01 23:02:11 +08:00
|
|
|
|
let outputs = Rc::new(RefCell::new(vec![]));
|
2022-08-11 02:55:10 +08:00
|
|
|
|
|
2022-11-12 11:37:23 +08:00
|
|
|
|
let copy_outputs = outputs.clone();
|
2024-03-15 19:58:22 +08:00
|
|
|
|
unsafe {
|
|
|
|
|
|
let _ = context.register_global_builtin_callable(
|
2024-03-15 21:14:05 +08:00
|
|
|
|
"__verge_log__".into(),
|
2024-03-15 19:58:22 +08:00
|
|
|
|
2,
|
|
|
|
|
|
NativeFunction::from_closure(
|
|
|
|
|
|
move |_: &JsValue, args: &[JsValue], context: &mut Context| {
|
2024-06-12 10:00:22 +08:00
|
|
|
|
let level = args.first().unwrap().to_string(context)?;
|
2024-03-15 19:58:22 +08:00
|
|
|
|
let level = level.to_std_string().unwrap();
|
|
|
|
|
|
let data = args.get(1).unwrap().to_string(context)?;
|
|
|
|
|
|
let data = data.to_std_string().unwrap();
|
2025-08-01 23:02:11 +08:00
|
|
|
|
let mut out = copy_outputs.borrow_mut();
|
2024-03-15 19:58:22 +08:00
|
|
|
|
out.push((level, data));
|
|
|
|
|
|
Ok(JsValue::undefined())
|
|
|
|
|
|
},
|
|
|
|
|
|
),
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
let _ = context.eval(Source::from_bytes(
|
|
|
|
|
|
r#"var console = Object.freeze({
|
2025-06-13 22:59:48 +08:00
|
|
|
|
log(data){__verge_log__("log",JSON.stringify(data, null, 2))},
|
|
|
|
|
|
info(data){__verge_log__("info",JSON.stringify(data, null, 2))},
|
|
|
|
|
|
error(data){__verge_log__("error",JSON.stringify(data, null, 2))},
|
|
|
|
|
|
debug(data){__verge_log__("debug",JSON.stringify(data, null, 2))},
|
|
|
|
|
|
warn(data){__verge_log__("warn",JSON.stringify(data, null, 2))},
|
|
|
|
|
|
table(data){__verge_log__("table",JSON.stringify(data, null, 2))},
|
2022-08-11 02:55:10 +08:00
|
|
|
|
});"#,
|
2024-03-15 19:58:22 +08:00
|
|
|
|
));
|
2022-08-11 02:55:10 +08:00
|
|
|
|
|
2024-03-15 19:58:22 +08:00
|
|
|
|
let config = use_lowercase(config.clone());
|
|
|
|
|
|
let config_str = serde_json::to_string(&config)?;
|
2022-08-11 02:55:10 +08:00
|
|
|
|
|
2025-06-16 21:04:40 +08:00
|
|
|
|
// 仅处理 name 参数中的特殊字符
|
|
|
|
|
|
let safe_name = escape_js_string_for_single_quote(&name);
|
2025-06-13 22:59:48 +08:00
|
|
|
|
|
2024-03-15 19:58:22 +08:00
|
|
|
|
let code = format!(
|
|
|
|
|
|
r#"try{{
|
2022-08-12 23:41:25 +08:00
|
|
|
|
{script};
|
2025-06-13 22:59:48 +08:00
|
|
|
|
JSON.stringify(main({config_str},'{safe_name}')||'')
|
2022-08-12 03:20:55 +08:00
|
|
|
|
}} catch(err) {{
|
|
|
|
|
|
`__error_flag__ ${{err.toString()}}`
|
|
|
|
|
|
}}"#
|
2024-03-15 19:58:22 +08:00
|
|
|
|
);
|
2025-06-13 22:59:48 +08:00
|
|
|
|
|
2024-03-15 19:58:22 +08:00
|
|
|
|
if let Ok(result) = context.eval(Source::from_bytes(code.as_str())) {
|
|
|
|
|
|
if !result.is_string() {
|
|
|
|
|
|
anyhow::bail!("main function should return object");
|
|
|
|
|
|
}
|
|
|
|
|
|
let result = result.to_string(&mut context).unwrap();
|
|
|
|
|
|
let result = result.to_std_string().unwrap();
|
2025-06-13 22:59:48 +08:00
|
|
|
|
|
2025-06-16 21:04:40 +08:00
|
|
|
|
// 直接解析JSON结果,不做其他解析
|
|
|
|
|
|
let res: Result<Mapping, Error> = parse_json_safely(&result);
|
2025-06-13 22:59:48 +08:00
|
|
|
|
|
2025-08-01 23:02:11 +08:00
|
|
|
|
let mut out = outputs.borrow_mut();
|
2024-03-15 19:58:22 +08:00
|
|
|
|
match res {
|
|
|
|
|
|
Ok(config) => Ok((use_lowercase(config), out.to_vec())),
|
|
|
|
|
|
Err(err) => {
|
|
|
|
|
|
out.push(("exception".into(), err.to_string()));
|
|
|
|
|
|
Ok((config, out.to_vec()))
|
|
|
|
|
|
}
|
2022-11-12 11:37:23 +08:00
|
|
|
|
}
|
2024-03-15 19:58:22 +08:00
|
|
|
|
} else {
|
|
|
|
|
|
anyhow::bail!("main function should return object");
|
2022-08-11 02:55:10 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-06-13 22:59:48 +08:00
|
|
|
|
fn parse_json_safely(json_str: &str) -> Result<Mapping, Error> {
|
2025-06-16 21:04:40 +08:00
|
|
|
|
let json_str = strip_outer_quotes(json_str);
|
2025-06-13 22:59:48 +08:00
|
|
|
|
|
2025-06-16 21:04:40 +08:00
|
|
|
|
Ok(serde_json::from_str::<Mapping>(json_str)?)
|
2025-06-13 22:59:48 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-06-16 21:04:40 +08:00
|
|
|
|
// 移除字符串外层的引号
|
|
|
|
|
|
fn strip_outer_quotes(s: &str) -> &str {
|
|
|
|
|
|
let s = s.trim();
|
|
|
|
|
|
if (s.starts_with('"') && s.ends_with('"')) || (s.starts_with('\'') && s.ends_with('\'')) {
|
|
|
|
|
|
&s[1..s.len() - 1]
|
|
|
|
|
|
} else {
|
|
|
|
|
|
s
|
2025-06-13 22:59:48 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-06-16 21:04:40 +08:00
|
|
|
|
// 转义单引号和反斜杠,用于单引号包裹的JavaScript字符串
|
|
|
|
|
|
fn escape_js_string_for_single_quote(s: &str) -> String {
|
|
|
|
|
|
s.replace('\\', "\\\\").replace('\'', "\\'")
|
2025-06-13 22:59:48 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2022-08-11 02:55:10 +08:00
|
|
|
|
#[test]
|
|
|
|
|
|
fn test_script() {
|
2022-11-12 11:37:23 +08:00
|
|
|
|
let script = r#"
|
2022-08-11 02:55:10 +08:00
|
|
|
|
function main(config) {
|
|
|
|
|
|
if (Array.isArray(config.rules)) {
|
|
|
|
|
|
config.rules = [...config.rules, "add"];
|
|
|
|
|
|
}
|
|
|
|
|
|
console.log(config);
|
|
|
|
|
|
config.proxies = ["111"];
|
|
|
|
|
|
return config;
|
|
|
|
|
|
}
|
|
|
|
|
|
"#;
|
|
|
|
|
|
|
2022-11-12 11:37:23 +08:00
|
|
|
|
let config = r#"
|
2022-08-11 02:55:10 +08:00
|
|
|
|
rules:
|
|
|
|
|
|
- 111
|
|
|
|
|
|
- 222
|
|
|
|
|
|
tun:
|
|
|
|
|
|
enable: false
|
|
|
|
|
|
dns:
|
|
|
|
|
|
enable: false
|
|
|
|
|
|
"#;
|
|
|
|
|
|
|
2022-11-12 11:37:23 +08:00
|
|
|
|
let config = serde_yaml::from_str(config).unwrap();
|
2024-07-04 23:11:54 +08:00
|
|
|
|
let (config, results) = use_script(script.into(), config, "".to_string()).unwrap();
|
2022-08-11 02:55:10 +08:00
|
|
|
|
|
2024-09-24 20:11:33 +08:00
|
|
|
|
let _ = serde_yaml::to_string(&config).unwrap();
|
2025-06-06 14:49:23 +08:00
|
|
|
|
let yaml_config_size = std::mem::size_of_val(&config);
|
|
|
|
|
|
dbg!(yaml_config_size);
|
|
|
|
|
|
let box_yaml_config_size = std::mem::size_of_val(&Box::new(config));
|
|
|
|
|
|
dbg!(box_yaml_config_size);
|
2022-11-12 11:37:23 +08:00
|
|
|
|
dbg!(results);
|
2025-06-06 14:49:23 +08:00
|
|
|
|
assert!(box_yaml_config_size < yaml_config_size);
|
2022-08-11 02:55:10 +08:00
|
|
|
|
}
|
2025-06-13 22:59:48 +08:00
|
|
|
|
|
|
|
|
|
|
// 测试特殊字符转义功能
|
|
|
|
|
|
#[test]
|
|
|
|
|
|
fn test_escape_unescape() {
|
|
|
|
|
|
let test_string = r#"Hello "World"!\nThis is a test with \u00A9 copyright symbol."#;
|
2025-06-16 21:04:40 +08:00
|
|
|
|
let escaped = escape_js_string_for_single_quote(test_string);
|
2025-06-30 20:48:20 +08:00
|
|
|
|
println!("Original: {test_string}");
|
|
|
|
|
|
println!("Escaped: {escaped}");
|
2025-06-13 22:59:48 +08:00
|
|
|
|
|
|
|
|
|
|
let json_str = r#"{"key":"value","nested":{"key":"value"}}"#;
|
|
|
|
|
|
let parsed = parse_json_safely(json_str).unwrap();
|
|
|
|
|
|
|
|
|
|
|
|
assert!(parsed.contains_key("key"));
|
|
|
|
|
|
assert!(parsed.contains_key("nested"));
|
|
|
|
|
|
|
|
|
|
|
|
let quoted_json_str = r#""{"key":"value","nested":{"key":"value"}}""#;
|
|
|
|
|
|
let parsed_quoted = parse_json_safely(quoted_json_str).unwrap();
|
|
|
|
|
|
|
|
|
|
|
|
assert!(parsed_quoted.contains_key("key"));
|
|
|
|
|
|
assert!(parsed_quoted.contains_key("nested"));
|
|
|
|
|
|
}
|