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
Unverified
parent f86a1816e0
commit 3939741a06
24 changed files with 70 additions and 50 deletions

View File

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

15
src-tauri/Cargo.lock generated
View File

@@ -1127,7 +1127,7 @@ dependencies = [
"scopeguard",
"serde",
"serde_json",
"serde_yaml",
"serde_yaml_ng",
"sha2 0.10.9",
"sys-locale",
"sysinfo",
@@ -6455,6 +6455,19 @@ dependencies = [
"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]]
name = "serialize-to-javascript"
version = "0.1.2"

View File

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

View File

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

View File

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

View File

@@ -381,7 +381,7 @@ pub async fn patch_profiles_config(profiles: IProfiles) -> CmdResult<bool> {
match file_read_result {
Ok(Ok(content)) => {
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;

View File

@@ -1,7 +1,7 @@
use super::CmdResult;
use crate::{config::*, wrap_err};
use anyhow::Context;
use serde_yaml::Mapping;
use serde_yaml_ng::Mapping;
use std::collections::HashMap;
/// 获取运行时配置
@@ -19,7 +19,7 @@ pub async fn get_runtime_yaml() -> CmdResult<String> {
wrap_err!(config
.ok_or(anyhow::anyhow!("failed to parse config to yaml file"))
.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 anyhow::Result;
use serde::{Deserialize, Serialize};
use serde_yaml::{Mapping, Value};
use serde_yaml_ng::{Mapping, Value};
use std::{
net::{IpAddr, Ipv4Addr, SocketAddr},
str::FromStr,

View File

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

View File

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

View File

@@ -1,6 +1,6 @@
use crate::enhance::field::use_keys;
use serde::{Deserialize, Serialize};
use serde_yaml::{Mapping, Value};
use serde_yaml_ng::{Mapping, Value};
use std::collections::HashMap;
#[derive(Default, Debug, Clone, Deserialize, Serialize)]
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())?;
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() {
obj.remove("webdav_username");
obj.remove("webdav_password");
obj.remove("webdav_url");
}
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.write_all(fs::read(dirs::profiles_path()?)?.as_slice())?;

View File

@@ -317,7 +317,7 @@ impl CoreManager {
};
// 对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(_) => {
logging!(info, Type::Config, true, "YAML语法检查通过");
Ok((true, String::new()))

View File

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

View File

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

View File

@@ -1,5 +1,5 @@
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) {
match (a, b) {
@@ -54,10 +54,10 @@ fn test_merge() -> anyhow::Result<()> {
script1: test
";
let merge = serde_yaml::from_str::<Mapping>(merge)?;
let config = serde_yaml::from_str::<Mapping>(config)?;
let merge = serde_yaml_ng::from_str::<Mapping>(merge)?;
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(())
}

View File

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

View File

@@ -1,6 +1,6 @@
use super::use_lowercase;
use anyhow::{Error, Result};
use serde_yaml::Mapping;
use serde_yaml_ng::Mapping;
pub fn use_script(
script: String,
@@ -149,11 +149,11 @@ fn test_script() {
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())
.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 box_yaml_config_size = std::mem::size_of_val(&Box::new(config));
assert!(box_yaml_config_size < yaml_config_size);

View File

@@ -1,5 +1,5 @@
use serde::{Deserialize, Serialize};
use serde_yaml::{Mapping, Sequence, Value};
use serde_yaml_ng::{Mapping, Sequence, Value};
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct SeqMap {
@@ -86,7 +86,7 @@ pub fn use_seq(seq: SeqMap, mut config: Mapping, field: &str) -> Mapping {
mod tests {
use super::*;
#[allow(unused_imports)]
use serde_yaml::Value;
use serde_yaml_ng::Value;
#[test]
#[allow(clippy::unwrap_used)]
@@ -110,7 +110,7 @@ proxy-groups:
- "proxy1"
"#;
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 {
prepend: Sequence::new(),

View File

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

View File

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

View File

@@ -6,7 +6,7 @@ use crate::{
utils::logging::Type,
};
use anyhow::Result;
use serde_yaml::Mapping;
use serde_yaml_ng::Mapping;
/// Patch Clash configuration
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 nanoid::nanoid;
use serde::{de::DeserializeOwned, Serialize};
use serde_yaml::Mapping;
use serde_yaml_ng::Mapping;
use std::{path::PathBuf, str::FromStr};
/// 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?;
Ok(serde_yaml::from_str::<T>(&yaml_str)?)
Ok(serde_yaml_ng::from_str::<T>(&yaml_str)?)
}
/// 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()))?;
// 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) => {
val.apply_merge()
.with_context(|| format!("failed to apply merge \"{}\"", path.display()))?;
@@ -66,7 +66,7 @@ pub async fn save_yaml<T: Serialize + Sync>(
data: &T,
prefix: Option<&str>,
) -> Result<()> {
let data_str = serde_yaml::to_string(data)?;
let data_str = serde_yaml_ng::to_string(data)?;
let yaml_str = match prefix {
Some(prefix) => format!("{prefix}\n\n{data_str}"),

View File

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