mirror of
https://github.com/clash-verge-rev/clash-verge-rev.git
synced 2026-11-14 14:52:08 +08:00
fix: quote wildcard array items in global extension script output (#7414)
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
use super::CmdResult;
|
||||
use crate::feat;
|
||||
use crate::utils::dirs;
|
||||
use crate::utils::{dirs, yaml_emitter};
|
||||
use crate::{
|
||||
cmd::StringifyErr as _,
|
||||
config::{ClashInfo, Config},
|
||||
@@ -136,14 +136,13 @@ pub async fn test_delay(url: String) -> CmdResult<u32> {
|
||||
#[tauri::command]
|
||||
pub async fn save_dns_config(dns_config: Mapping) -> CmdResult {
|
||||
use crate::utils::dirs;
|
||||
use serde_yaml_ng;
|
||||
use tokio::fs;
|
||||
|
||||
// 获取DNS配置文件路径
|
||||
let dns_path = dirs::app_home_dir().stringify_err()?.join(constants::files::DNS_CONFIG);
|
||||
|
||||
// 保存DNS配置到文件
|
||||
let yaml_str = serde_yaml_ng::to_string(&dns_config).stringify_err()?;
|
||||
let yaml_str = yaml_emitter::to_mihomo_config_string(&dns_config).stringify_err()?;
|
||||
fs::write(&dns_path, yaml_str).await.stringify_err()?;
|
||||
logging!(info, Type::Config, "DNS config saved to {dns_path:?}");
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use super::CmdResult;
|
||||
use crate::{cmd::StringifyErr as _, config::Config, core::CoreManager};
|
||||
use crate::{cmd::StringifyErr as _, config::Config, core::CoreManager, utils::yaml_emitter};
|
||||
use anyhow::{Context as _, anyhow};
|
||||
use clash_verge_logging::{Type, logging};
|
||||
use serde_yaml_ng::Mapping;
|
||||
@@ -22,7 +22,7 @@ pub async fn get_runtime_yaml() -> CmdResult<String> {
|
||||
config
|
||||
.ok_or_else(|| anyhow!("failed to parse config to yaml file"))
|
||||
.and_then(|config| {
|
||||
serde_yaml_ng::to_string(config)
|
||||
yaml_emitter::to_mihomo_config_string(config)
|
||||
.context("failed to convert config to yaml")
|
||||
.map(|s| s.into())
|
||||
})
|
||||
@@ -82,7 +82,7 @@ pub async fn get_runtime_proxy_chain_config(proxy_chain_exit_node: String) -> Cm
|
||||
|
||||
config.insert("proxies".into(), proxies_chain);
|
||||
|
||||
serde_yaml_ng::to_string(&config)
|
||||
yaml_emitter::to_mihomo_config_string(&config)
|
||||
.context("YAML generation failed")
|
||||
.map(|s| s.into())
|
||||
.stringify_err()
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use crate::constants::files::DNS_CONFIG;
|
||||
use crate::utils::yaml_emitter;
|
||||
use crate::{config::Config, process::AsyncHandler, utils::dirs};
|
||||
use anyhow::Error;
|
||||
use arc_swap::{ArcSwap, ArcSwapOption};
|
||||
@@ -287,7 +288,7 @@ pub async fn create_backup() -> Result<(String, PathBuf), Error> {
|
||||
obj.remove("webdav_url");
|
||||
}
|
||||
zip.start_file(dirs::VERGE_CONFIG, options)?;
|
||||
zip.write_all(serde_yaml_ng::to_string(&verge_config)?.as_bytes())?;
|
||||
zip.write_all(yaml_emitter::to_mihomo_config_string(&verge_config)?.as_bytes())?;
|
||||
|
||||
let dns_config_path = dirs::app_home_dir()?.join(DNS_CONFIG);
|
||||
if dns_config_path.exists() {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::{config::with_encryption, enhance::seq::SeqMap};
|
||||
use crate::{config::with_encryption, enhance::seq::SeqMap, utils::yaml_emitter};
|
||||
use anyhow::{Context as _, Result, anyhow, bail};
|
||||
use clash_verge_logging::{Type, logging};
|
||||
use nanoid::nanoid;
|
||||
@@ -59,7 +59,7 @@ pub async fn read_seq_map(path: &PathBuf) -> Result<SeqMap> {
|
||||
/// save the data to the file
|
||||
/// can set `prefix` string to add some comments
|
||||
pub async fn save_yaml<T: Serialize + Sync>(path: &PathBuf, data: &T, prefix: Option<&str>) -> Result<()> {
|
||||
let data_str = with_encryption(|| async { serde_yaml_ng::to_string(data) }).await?;
|
||||
let data_str = with_encryption(|| async { yaml_emitter::to_mihomo_config_string(data) }).await?;
|
||||
|
||||
let yaml_str = match prefix {
|
||||
Some(prefix) => format!("{prefix}\n\n{data_str}"),
|
||||
|
||||
@@ -17,3 +17,4 @@ pub mod tmpl;
|
||||
#[cfg(target_os = "macos")]
|
||||
pub mod tray_speed;
|
||||
pub mod window_manager;
|
||||
pub mod yaml_emitter;
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
use serde::{Serialize, ser};
|
||||
use serde_yaml_ng::Result;
|
||||
|
||||
pub fn to_mihomo_config_string<T: Serialize>(data: &T) -> Result<String> {
|
||||
let value = serde_yaml_ng::to_value(data)?;
|
||||
Ok(emit_value(&value, 0, &DEFAULT_OPTIONS)? + "\n")
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
struct Options {
|
||||
checkout_wildcard: bool,
|
||||
}
|
||||
|
||||
const DEFAULT_OPTIONS: Options = Options {
|
||||
checkout_wildcard: false,
|
||||
};
|
||||
|
||||
fn serde_yaml_ng_to_string_without_last<T: ?Sized + ser::Serialize>(value: &T) -> Result<String> {
|
||||
let value = serde_yaml_ng::to_string(value)?;
|
||||
if value.ends_with('\n') {
|
||||
Ok(value[..value.len() - 1].to_string())
|
||||
} else {
|
||||
Ok(value)
|
||||
}
|
||||
}
|
||||
|
||||
fn emit_value(value: &serde_yaml_ng::Value, depth: usize, options: &Options) -> Result<String> {
|
||||
match value {
|
||||
serde_yaml_ng::Value::Null => serde_yaml_ng_to_string_without_last(&value),
|
||||
serde_yaml_ng::Value::Bool(b) => serde_yaml_ng_to_string_without_last(&b),
|
||||
serde_yaml_ng::Value::Number(number) => serde_yaml_ng_to_string_without_last(&number),
|
||||
serde_yaml_ng::Value::String(s) => emit_string(s, options),
|
||||
serde_yaml_ng::Value::Sequence(values) => {
|
||||
if values.is_empty() {
|
||||
return Ok("[]".to_string());
|
||||
}
|
||||
let mut result = String::new();
|
||||
let mut first = true;
|
||||
for value in values {
|
||||
if !first || depth > 0 {
|
||||
result.push('\n');
|
||||
}
|
||||
first = false;
|
||||
result.push_str(&" ".repeat(depth));
|
||||
result.push_str("- ");
|
||||
result.push_str(&emit_value(value, depth + 1, options)?);
|
||||
}
|
||||
Ok(result)
|
||||
}
|
||||
serde_yaml_ng::Value::Mapping(mapping) => {
|
||||
if mapping.is_empty() {
|
||||
return Ok("{}".to_string());
|
||||
}
|
||||
let mut result = String::new();
|
||||
let mut first = true;
|
||||
for (key, value) in mapping {
|
||||
if !first || depth > 0 {
|
||||
result.push('\n');
|
||||
}
|
||||
first = false;
|
||||
result.push_str(&" ".repeat(depth));
|
||||
let new_key = emit_value(key, depth + 1, options)?;
|
||||
result.push_str(&new_key);
|
||||
result.push_str(": ");
|
||||
let mut value_emiter_options = options.clone();
|
||||
if need_checkout_wildcard(key) {
|
||||
value_emiter_options.checkout_wildcard = true;
|
||||
}
|
||||
result.push_str(&emit_value(value, depth + 1, &value_emiter_options)?);
|
||||
}
|
||||
Ok(result)
|
||||
}
|
||||
serde_yaml_ng::Value::Tagged(tagged_value) => {
|
||||
let mut result = String::new();
|
||||
result.push_str(&format!("!{} ", tagged_value.tag));
|
||||
result.push_str(&emit_value(&tagged_value.value, depth, options)?);
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn emit_string(s: &str, options: &Options) -> Result<String> {
|
||||
let mut s = serde_yaml_ng_to_string_without_last(s)?;
|
||||
if !s.starts_with('\"') && !s.starts_with('\'') && options.checkout_wildcard && contains_wildcard(&s) {
|
||||
s = quote_string(&s)
|
||||
}
|
||||
Ok(s)
|
||||
}
|
||||
|
||||
fn quote_string(s: &str) -> String {
|
||||
"\'".to_string() + s.replace('\'', "''").as_str() + "\'"
|
||||
}
|
||||
|
||||
fn need_checkout_wildcard(key: &serde_yaml_ng::Value) -> bool {
|
||||
if let serde_yaml_ng::Value::String(key_str) = key {
|
||||
key_str == "fake-ip-filter"
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn contains_wildcard(value: &str) -> bool {
|
||||
value.contains('*') || value.starts_with('+') || value.starts_with('.')
|
||||
}
|
||||
Reference in New Issue
Block a user