refactor: migrate from serde_yaml to serde_yaml_ng for improved YAML handling (#4568)

* refactor: migrate from serde_yaml to serde_yaml_ng for improved YAML handling

* refactor: format code for better readability in DNS configuration
This commit is contained in:
Tunglies
2025-08-30 02:24:47 +08:00
committed by GitHub
parent f86a1816e0
commit 3939741a06
24 changed files with 70 additions and 50 deletions

View File

@@ -38,5 +38,5 @@
} }
], ],
"postUpdateOptions": ["pnpmDedupe"], "postUpdateOptions": ["pnpmDedupe"],
"ignoreDeps": ["serde_yaml", "criterion"] "ignoreDeps": ["criterion"]
} }

15
src-tauri/Cargo.lock generated
View File

@@ -1127,7 +1127,7 @@ dependencies = [
"scopeguard", "scopeguard",
"serde", "serde",
"serde_json", "serde_json",
"serde_yaml", "serde_yaml_ng",
"sha2 0.10.9", "sha2 0.10.9",
"sys-locale", "sys-locale",
"sysinfo", "sysinfo",
@@ -6455,6 +6455,19 @@ dependencies = [
"unsafe-libyaml", "unsafe-libyaml",
] ]
[[package]]
name = "serde_yaml_ng"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b4db627b98b36d4203a7b458cf3573730f2bb591b28871d916dfa9efabfd41f"
dependencies = [
"indexmap 2.11.0",
"itoa",
"ryu",
"serde",
"unsafe-libyaml",
]
[[package]] [[package]]
name = "serialize-to-javascript" name = "serialize-to-javascript"
version = "0.1.2" version = "0.1.2"

View File

@@ -28,7 +28,7 @@ chrono = "0.4.41"
sysinfo = { version = "0.37.0", features = ["network", "system"] } sysinfo = { version = "0.37.0", features = ["network", "system"] }
boa_engine = "0.20.0" boa_engine = "0.20.0"
serde_json = "1.0.143" serde_json = "1.0.143"
serde_yaml = "0.9.34" serde_yaml_ng = "0.10.0"
once_cell = "1.21.3" once_cell = "1.21.3"
port_scanner = "0.1.5" port_scanner = "0.1.5"
delay_timer = "0.11.6" delay_timer = "0.11.6"
@@ -83,6 +83,7 @@ isahc = { version = "1.7.2", default-features = false, features = [
"parking_lot", "parking_lot",
] } ] }
[target.'cfg(windows)'.dependencies] [target.'cfg(windows)'.dependencies]
runas = "=1.2.0" runas = "=1.2.0"
deelevate = "0.2.0" deelevate = "0.2.0"

View File

@@ -12,7 +12,7 @@ use crate::{
utils::logging::Type, utils::logging::Type,
wrap_err, wrap_err,
}; };
use serde_yaml::Mapping; use serde_yaml_ng::Mapping;
use std::time::Duration; use std::time::Duration;
const CONFIG_REFRESH_INTERVAL: Duration = Duration::from_secs(60); const CONFIG_REFRESH_INTERVAL: Duration = Duration::from_secs(60);
@@ -143,7 +143,7 @@ pub async fn test_delay(url: String) -> CmdResult<u32> {
#[tauri::command] #[tauri::command]
pub async fn save_dns_config(dns_config: Mapping) -> CmdResult { pub async fn save_dns_config(dns_config: Mapping) -> CmdResult {
use crate::utils::dirs; use crate::utils::dirs;
use serde_yaml; use serde_yaml_ng;
use tokio::fs; use tokio::fs;
// 获取DNS配置文件路径 // 获取DNS配置文件路径
@@ -152,7 +152,7 @@ pub async fn save_dns_config(dns_config: Mapping) -> CmdResult {
.join("dns_config.yaml"); .join("dns_config.yaml");
// 保存DNS配置到文件 // 保存DNS配置到文件
let yaml_str = serde_yaml::to_string(&dns_config).map_err(|e| e.to_string())?; let yaml_str = serde_yaml_ng::to_string(&dns_config).map_err(|e| e.to_string())?;
fs::write(&dns_path, yaml_str) fs::write(&dns_path, yaml_str)
.await .await
.map_err(|e| e.to_string())?; .map_err(|e| e.to_string())?;
@@ -187,15 +187,16 @@ pub async fn apply_dns_config(apply: bool) -> CmdResult {
})?; })?;
// 解析DNS配置 // 解析DNS配置
let patch_config = serde_yaml::from_str::<serde_yaml::Mapping>(&dns_yaml).map_err(|e| { let patch_config =
logging!(error, Type::Config, "Failed to parse DNS config: {e}"); serde_yaml_ng::from_str::<serde_yaml_ng::Mapping>(&dns_yaml).map_err(|e| {
e.to_string() logging!(error, Type::Config, "Failed to parse DNS config: {e}");
})?; e.to_string()
})?;
logging!(info, Type::Config, "Applying DNS config from file"); logging!(info, Type::Config, "Applying DNS config from file");
// 创建包含DNS配置的patch // 创建包含DNS配置的patch
let mut patch = serde_yaml::Mapping::new(); let mut patch = serde_yaml_ng::Mapping::new();
patch.insert("dns".into(), patch_config.into()); patch.insert("dns".into(), patch_config.into());
// 应用DNS配置到运行时配置 // 应用DNS配置到运行时配置

View File

@@ -3,7 +3,7 @@ use crate::core::{async_proxy_query::AsyncProxyQuery, EventDrivenProxyManager};
use crate::process::AsyncHandler; use crate::process::AsyncHandler;
use crate::wrap_err; use crate::wrap_err;
use network_interface::NetworkInterface; use network_interface::NetworkInterface;
use serde_yaml::Mapping; use serde_yaml_ng::Mapping;
/// get the system proxy /// get the system proxy
#[tauri::command] #[tauri::command]

View File

@@ -381,7 +381,7 @@ pub async fn patch_profiles_config(profiles: IProfiles) -> CmdResult<bool> {
match file_read_result { match file_read_result {
Ok(Ok(content)) => { Ok(Ok(content)) => {
let yaml_parse_result = AsyncHandler::spawn_blocking(move || { let yaml_parse_result = AsyncHandler::spawn_blocking(move || {
serde_yaml::from_str::<serde_yaml::Value>(&content) serde_yaml_ng::from_str::<serde_yaml_ng::Value>(&content)
}) })
.await; .await;

View File

@@ -1,7 +1,7 @@
use super::CmdResult; use super::CmdResult;
use crate::{config::*, wrap_err}; use crate::{config::*, wrap_err};
use anyhow::Context; use anyhow::Context;
use serde_yaml::Mapping; use serde_yaml_ng::Mapping;
use std::collections::HashMap; use std::collections::HashMap;
/// 获取运行时配置 /// 获取运行时配置
@@ -19,7 +19,7 @@ pub async fn get_runtime_yaml() -> CmdResult<String> {
wrap_err!(config wrap_err!(config
.ok_or(anyhow::anyhow!("failed to parse config to yaml file")) .ok_or(anyhow::anyhow!("failed to parse config to yaml file"))
.and_then( .and_then(
|config| serde_yaml::to_string(config).context("failed to convert config to yaml") |config| serde_yaml_ng::to_string(config).context("failed to convert config to yaml")
)) ))
} }

View File

@@ -3,7 +3,7 @@ use crate::utils::dirs::{ipc_path, path_to_str};
use crate::utils::{dirs, help}; use crate::utils::{dirs, help};
use anyhow::Result; use anyhow::Result;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use serde_yaml::{Mapping, Value}; use serde_yaml_ng::{Mapping, Value};
use std::{ use std::{
net::{IpAddr, Ipv4Addr, SocketAddr}, net::{IpAddr, Ipv4Addr, SocketAddr},
str::FromStr, str::FromStr,

View File

@@ -5,7 +5,7 @@ use crate::utils::{
}; };
use anyhow::{bail, Context, Result}; use anyhow::{bail, Context, Result};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use serde_yaml::Mapping; use serde_yaml_ng::Mapping;
use std::{fs, time::Duration}; use std::{fs, time::Duration};
#[derive(Debug, Clone, Deserialize, Serialize, Default)] #[derive(Debug, Clone, Deserialize, Serialize, Default)]
@@ -355,7 +355,7 @@ impl PrfItem {
let data = data.trim_start_matches('\u{feff}'); let data = data.trim_start_matches('\u{feff}');
// check the data whether the valid yaml format // check the data whether the valid yaml format
let yaml = serde_yaml::from_str::<Mapping>(data) let yaml = serde_yaml_ng::from_str::<Mapping>(data)
.context("the remote profile data is invalid yaml")?; .context("the remote profile data is invalid yaml")?;
if !yaml.contains_key("proxies") && !yaml.contains_key("proxy-providers") { if !yaml.contains_key("proxies") && !yaml.contains_key("proxy-providers") {

View File

@@ -6,7 +6,7 @@ use crate::{
}; };
use anyhow::{bail, Context, Result}; use anyhow::{bail, Context, Result};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use serde_yaml::Mapping; use serde_yaml_ng::Mapping;
use std::collections::HashSet; use std::collections::HashSet;
use tokio::fs; use tokio::fs;

View File

@@ -1,6 +1,6 @@
use crate::enhance::field::use_keys; use crate::enhance::field::use_keys;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use serde_yaml::{Mapping, Value}; use serde_yaml_ng::{Mapping, Value};
use std::collections::HashMap; use std::collections::HashMap;
#[derive(Default, Debug, Clone, Deserialize, Serialize)] #[derive(Default, Debug, Clone, Deserialize, Serialize)]
pub struct IRuntime { pub struct IRuntime {

View File

@@ -264,14 +264,14 @@ pub fn create_backup() -> Result<(String, PathBuf), Error> {
zip.write_all(fs::read(dirs::clash_path()?)?.as_slice())?; zip.write_all(fs::read(dirs::clash_path()?)?.as_slice())?;
let mut verge_config: serde_json::Value = let mut verge_config: serde_json::Value =
serde_yaml::from_str(&fs::read_to_string(dirs::verge_path()?)?)?; serde_yaml_ng::from_str(&fs::read_to_string(dirs::verge_path()?)?)?;
if let Some(obj) = verge_config.as_object_mut() { if let Some(obj) = verge_config.as_object_mut() {
obj.remove("webdav_username"); obj.remove("webdav_username");
obj.remove("webdav_password"); obj.remove("webdav_password");
obj.remove("webdav_url"); obj.remove("webdav_url");
} }
zip.start_file(dirs::VERGE_CONFIG, options)?; zip.start_file(dirs::VERGE_CONFIG, options)?;
zip.write_all(serde_yaml::to_string(&verge_config)?.as_bytes())?; zip.write_all(serde_yaml_ng::to_string(&verge_config)?.as_bytes())?;
zip.start_file(dirs::PROFILE_YAML, options)?; zip.start_file(dirs::PROFILE_YAML, options)?;
zip.write_all(fs::read(dirs::profiles_path()?)?.as_slice())?; zip.write_all(fs::read(dirs::profiles_path()?)?.as_slice())?;

View File

@@ -317,7 +317,7 @@ impl CoreManager {
}; };
// 对YAML文件尝试解析只检查语法正确性 // 对YAML文件尝试解析只检查语法正确性
logging!(info, Type::Config, true, "进行YAML语法检查"); logging!(info, Type::Config, true, "进行YAML语法检查");
match serde_yaml::from_str::<serde_yaml::Value>(&content) { match serde_yaml_ng::from_str::<serde_yaml_ng::Value>(&content) {
Ok(_) => { Ok(_) => {
logging!(info, Type::Config, true, "YAML语法检查通过"); logging!(info, Type::Config, true, "YAML语法检查通过");
Ok((true, String::new())) Ok((true, String::new()))

View File

@@ -3,7 +3,7 @@ use crate::{
config::PrfItem, config::PrfItem,
utils::{dirs, help}, utils::{dirs, help},
}; };
use serde_yaml::Mapping; use serde_yaml_ng::Mapping;
use std::fs; use std::fs;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]

View File

@@ -1,4 +1,4 @@
use serde_yaml::{Mapping, Value}; use serde_yaml_ng::{Mapping, Value};
use std::collections::HashSet; use std::collections::HashSet;
pub const HANDLE_FIELDS: [&str; 12] = [ pub const HANDLE_FIELDS: [&str; 12] = [

View File

@@ -1,5 +1,5 @@
use super::use_lowercase; use super::use_lowercase;
use serde_yaml::{self, Mapping, Value}; use serde_yaml_ng::{self, Mapping, Value};
fn deep_merge(a: &mut Value, b: &Value) { fn deep_merge(a: &mut Value, b: &Value) {
match (a, b) { match (a, b) {
@@ -54,10 +54,10 @@ fn test_merge() -> anyhow::Result<()> {
script1: test script1: test
"; ";
let merge = serde_yaml::from_str::<Mapping>(merge)?; let merge = serde_yaml_ng::from_str::<Mapping>(merge)?;
let config = serde_yaml::from_str::<Mapping>(config)?; let config = serde_yaml_ng::from_str::<Mapping>(config)?;
let _ = serde_yaml::to_string(&use_merge(merge, config))?; let _ = serde_yaml_ng::to_string(&use_merge(merge, config))?;
Ok(()) Ok(())
} }

View File

@@ -7,7 +7,7 @@ mod tun;
use self::{chain::*, field::*, merge::*, script::*, seq::*, tun::*}; use self::{chain::*, field::*, merge::*, script::*, seq::*, tun::*};
use crate::{config::Config, utils::tmpl}; use crate::{config::Config, utils::tmpl};
use serde_yaml::Mapping; use serde_yaml_ng::Mapping;
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
type ResultLog = Vec<(String, String)>; type ResultLog = Vec<(String, String)>;
@@ -386,7 +386,9 @@ pub async fn enhance() -> (Mapping, Vec<String>, HashMap<String, ResultLog>) {
if dns_path.exists() { if dns_path.exists() {
if let Ok(dns_yaml) = fs::read_to_string(&dns_path) { if let Ok(dns_yaml) = fs::read_to_string(&dns_path) {
if let Ok(dns_config) = serde_yaml::from_str::<serde_yaml::Mapping>(&dns_yaml) { if let Ok(dns_config) =
serde_yaml_ng::from_str::<serde_yaml_ng::Mapping>(&dns_yaml)
{
// 处理hosts配置 // 处理hosts配置
if let Some(hosts_value) = dns_config.get("hosts") { if let Some(hosts_value) = dns_config.get("hosts") {
if hosts_value.is_mapping() { if hosts_value.is_mapping() {

View File

@@ -1,6 +1,6 @@
use super::use_lowercase; use super::use_lowercase;
use anyhow::{Error, Result}; use anyhow::{Error, Result};
use serde_yaml::Mapping; use serde_yaml_ng::Mapping;
pub fn use_script( pub fn use_script(
script: String, script: String,
@@ -149,11 +149,11 @@ fn test_script() {
enable: false enable: false
"; ";
let config = serde_yaml::from_str(config).expect("Failed to parse test config YAML"); let config = serde_yaml_ng::from_str(config).expect("Failed to parse test config YAML");
let (config, results) = use_script(script.into(), config, "".to_string()) let (config, results) = use_script(script.into(), config, "".to_string())
.expect("Script execution should succeed in test"); .expect("Script execution should succeed in test");
let _ = serde_yaml::to_string(&config).expect("Failed to serialize config to YAML"); let _ = serde_yaml_ng::to_string(&config).expect("Failed to serialize config to YAML");
let yaml_config_size = std::mem::size_of_val(&config); let yaml_config_size = std::mem::size_of_val(&config);
let box_yaml_config_size = std::mem::size_of_val(&Box::new(config)); let box_yaml_config_size = std::mem::size_of_val(&Box::new(config));
assert!(box_yaml_config_size < yaml_config_size); assert!(box_yaml_config_size < yaml_config_size);

View File

@@ -1,5 +1,5 @@
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use serde_yaml::{Mapping, Sequence, Value}; use serde_yaml_ng::{Mapping, Sequence, Value};
#[derive(Debug, Clone, Default, Serialize, Deserialize)] #[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct SeqMap { pub struct SeqMap {
@@ -86,7 +86,7 @@ pub fn use_seq(seq: SeqMap, mut config: Mapping, field: &str) -> Mapping {
mod tests { mod tests {
use super::*; use super::*;
#[allow(unused_imports)] #[allow(unused_imports)]
use serde_yaml::Value; use serde_yaml_ng::Value;
#[test] #[test]
#[allow(clippy::unwrap_used)] #[allow(clippy::unwrap_used)]
@@ -110,7 +110,7 @@ proxy-groups:
- "proxy1" - "proxy1"
"#; "#;
let mut config: Mapping = let mut config: Mapping =
serde_yaml::from_str(config_str).expect("Failed to parse test config YAML"); serde_yaml_ng::from_str(config_str).expect("Failed to parse test config YAML");
let seq = SeqMap { let seq = SeqMap {
prepend: Sequence::new(), prepend: Sequence::new(),

View File

@@ -1,4 +1,4 @@
use serde_yaml::{Mapping, Value}; use serde_yaml_ng::{Mapping, Value};
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
use crate::process::AsyncHandler; use crate::process::AsyncHandler;

View File

@@ -6,7 +6,7 @@ use crate::{
process::AsyncHandler, process::AsyncHandler,
utils::{logging::Type, resolve}, utils::{logging::Type, resolve},
}; };
use serde_yaml::{Mapping, Value}; use serde_yaml_ng::{Mapping, Value};
/// Restart the Clash core /// Restart the Clash core
pub async fn restart_clash_core() { pub async fn restart_clash_core() {

View File

@@ -6,7 +6,7 @@ use crate::{
utils::logging::Type, utils::logging::Type,
}; };
use anyhow::Result; use anyhow::Result;
use serde_yaml::Mapping; use serde_yaml_ng::Mapping;
/// Patch Clash configuration /// Patch Clash configuration
pub async fn patch_clash(patch: Mapping) -> Result<()> { pub async fn patch_clash(patch: Mapping) -> Result<()> {

View File

@@ -2,7 +2,7 @@ use crate::{enhance::seq::SeqMap, logging, utils::logging::Type};
use anyhow::{anyhow, bail, Context, Result}; use anyhow::{anyhow, bail, Context, Result};
use nanoid::nanoid; use nanoid::nanoid;
use serde::{de::DeserializeOwned, Serialize}; use serde::{de::DeserializeOwned, Serialize};
use serde_yaml::Mapping; use serde_yaml_ng::Mapping;
use std::{path::PathBuf, str::FromStr}; use std::{path::PathBuf, str::FromStr};
/// read data from yaml as struct T /// read data from yaml as struct T
@@ -13,7 +13,7 @@ pub async fn read_yaml<T: DeserializeOwned>(path: &PathBuf) -> Result<T> {
let yaml_str = tokio::fs::read_to_string(path).await?; let yaml_str = tokio::fs::read_to_string(path).await?;
Ok(serde_yaml::from_str::<T>(&yaml_str)?) Ok(serde_yaml_ng::from_str::<T>(&yaml_str)?)
} }
/// read mapping from yaml /// read mapping from yaml
@@ -27,7 +27,7 @@ pub async fn read_mapping(path: &PathBuf) -> Result<Mapping> {
.with_context(|| format!("failed to read the file \"{}\"", path.display()))?; .with_context(|| format!("failed to read the file \"{}\"", path.display()))?;
// YAML语法检查 // YAML语法检查
match serde_yaml::from_str::<serde_yaml::Value>(&yaml_str) { match serde_yaml_ng::from_str::<serde_yaml_ng::Value>(&yaml_str) {
Ok(mut val) => { Ok(mut val) => {
val.apply_merge() val.apply_merge()
.with_context(|| format!("failed to apply merge \"{}\"", path.display()))?; .with_context(|| format!("failed to apply merge \"{}\"", path.display()))?;
@@ -66,7 +66,7 @@ pub async fn save_yaml<T: Serialize + Sync>(
data: &T, data: &T,
prefix: Option<&str>, prefix: Option<&str>,
) -> Result<()> { ) -> Result<()> {
let data_str = serde_yaml::to_string(data)?; let data_str = serde_yaml_ng::to_string(data)?;
let yaml_str = match prefix { let yaml_str = match prefix {
Some(prefix) => format!("{prefix}\n\n{data_str}"), Some(prefix) => format!("{prefix}\n\n{data_str}"),

View File

@@ -142,10 +142,10 @@ pub async fn delete_log() -> Result<()> {
/// 初始化DNS配置文件 /// 初始化DNS配置文件
async fn init_dns_config() -> Result<()> { async fn init_dns_config() -> Result<()> {
use serde_yaml::Value; use serde_yaml_ng::Value;
// 创建DNS子配置 // 创建DNS子配置
let dns_config = serde_yaml::Mapping::from_iter([ let dns_config = serde_yaml_ng::Mapping::from_iter([
("enable".into(), Value::Bool(true)), ("enable".into(), Value::Bool(true)),
("listen".into(), Value::String(":53".into())), ("listen".into(), Value::String(":53".into())),
("enhanced-mode".into(), Value::String("fake-ip".into())), ("enhanced-mode".into(), Value::String("fake-ip".into())),
@@ -197,7 +197,7 @@ async fn init_dns_config() -> Result<()> {
("fallback".into(), Value::Sequence(vec![])), ("fallback".into(), Value::Sequence(vec![])),
( (
"nameserver-policy".into(), "nameserver-policy".into(),
Value::Mapping(serde_yaml::Mapping::new()), Value::Mapping(serde_yaml_ng::Mapping::new()),
), ),
( (
"proxy-server-nameserver".into(), "proxy-server-nameserver".into(),
@@ -211,7 +211,7 @@ async fn init_dns_config() -> Result<()> {
("direct-nameserver-follow-policy".into(), Value::Bool(false)), ("direct-nameserver-follow-policy".into(), Value::Bool(false)),
( (
"fallback-filter".into(), "fallback-filter".into(),
Value::Mapping(serde_yaml::Mapping::from_iter([ Value::Mapping(serde_yaml_ng::Mapping::from_iter([
("geoip".into(), Value::Bool(true)), ("geoip".into(), Value::Bool(true)),
("geoip-code".into(), Value::String("CN".into())), ("geoip-code".into(), Value::String("CN".into())),
( (
@@ -234,9 +234,12 @@ async fn init_dns_config() -> Result<()> {
]); ]);
// 获取默认DNS和host配置 // 获取默认DNS和host配置
let default_dns_config = serde_yaml::Mapping::from_iter([ let default_dns_config = serde_yaml_ng::Mapping::from_iter([
("dns".into(), Value::Mapping(dns_config)), ("dns".into(), Value::Mapping(dns_config)),
("hosts".into(), Value::Mapping(serde_yaml::Mapping::new())), (
"hosts".into(),
Value::Mapping(serde_yaml_ng::Mapping::new()),
),
]); ]);
// 检查DNS配置文件是否存在 // 检查DNS配置文件是否存在