mirror of
https://github.com/clash-verge-rev/clash-verge-rev.git
synced 2026-11-09 11:52:08 +08:00
fix(enhance): replace DNS fields in merge overrides
Replace DNS map fields and hosts instead of retaining old entries, while preserving unspecified DNS fields. Keep DNS override priority and explicit empty values intact. Refs #7993
This commit is contained in:
@@ -1,5 +1,3 @@
|
||||
use clash_verge_logging::{Type, logging};
|
||||
|
||||
use super::field::use_lowercase;
|
||||
use serde_yaml_ng::{self, Mapping, Value};
|
||||
|
||||
@@ -18,20 +16,19 @@ fn deep_merge(a: &mut Value, b: Value) {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn use_merge(merge: &Mapping, config: Mapping) -> Mapping {
|
||||
let mut config = Value::from(config);
|
||||
let merge = use_lowercase(merge);
|
||||
|
||||
deep_merge(&mut config, Value::from(merge));
|
||||
|
||||
config.as_mapping().cloned().unwrap_or_else(|| {
|
||||
logging!(
|
||||
error,
|
||||
Type::Core,
|
||||
"Failed to convert merged config to mapping, using empty mapping"
|
||||
);
|
||||
Mapping::new()
|
||||
})
|
||||
pub fn use_merge(merge: &Mapping, mut config: Mapping) -> Mapping {
|
||||
for (key, value) in use_lowercase(merge) {
|
||||
match (config.get_mut(&key), value) {
|
||||
(Some(Value::Mapping(existing)), Value::Mapping(overlay)) if key.as_str() == Some("dns") => {
|
||||
existing.extend(overlay);
|
||||
}
|
||||
(Some(existing), value) if key.as_str() != Some("hosts") => deep_merge(existing, value),
|
||||
(_, value) => {
|
||||
config.insert(key, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
config
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -54,18 +51,49 @@ fn test_merge() -> anyhow::Result<()> {
|
||||
enable: true
|
||||
dns:
|
||||
enable: true
|
||||
nameserver-policy:
|
||||
new.example: 1.1.1.1
|
||||
fallback-filter:
|
||||
geoip: false
|
||||
hosts:
|
||||
new.example: 192.0.2.2
|
||||
";
|
||||
|
||||
let config = r"
|
||||
rules:
|
||||
- aaaaa
|
||||
script1: test
|
||||
tun:
|
||||
mtu: 1500
|
||||
dns:
|
||||
nameserver: [9.9.9.9]
|
||||
nameserver-policy:
|
||||
old.example: 8.8.8.8
|
||||
fallback-filter:
|
||||
geoip: true
|
||||
domain: [old.example]
|
||||
hosts:
|
||||
old.example: 192.0.2.1
|
||||
";
|
||||
|
||||
let merge = serde_yaml_ng::from_str::<Mapping>(merge)?;
|
||||
let config = serde_yaml_ng::from_str::<Mapping>(config)?;
|
||||
|
||||
let _ = serde_yaml_ng::to_string(&use_merge(&merge, config))?;
|
||||
let result = use_merge(&merge, config);
|
||||
assert_eq!(result["dns"]["nameserver-policy"], merge["dns"]["nameserver-policy"]);
|
||||
assert_eq!(result["dns"]["fallback-filter"], merge["dns"]["fallback-filter"]);
|
||||
assert_eq!(result["hosts"], merge["hosts"]);
|
||||
assert_eq!(result["dns"]["nameserver"], serde_yaml_ng::to_value(["9.9.9.9"])?);
|
||||
assert_eq!(result["tun"]["mtu"], Value::from(1500));
|
||||
assert_eq!(result["rules"], merge["rules"]);
|
||||
assert_eq!(result["script1"], Value::from("test"));
|
||||
|
||||
let cleared = serde_yaml_ng::from_str::<Mapping>(
|
||||
"dns: {enable: false, nameserver-policy: {}, fallback-filter: {}, nameserver: []}\nhosts: {}",
|
||||
)?;
|
||||
let result = use_merge(&cleared, result);
|
||||
assert_eq!(result["dns"], cleared["dns"]);
|
||||
assert_eq!(result["hosts"], cleared["hosts"]);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1433,6 +1433,38 @@ mod tests {
|
||||
serde_yaml_ng::from_str(yaml).expect("test config should be valid")
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn merge_replaces_dns_fields_with_or_without_dns_settings() {
|
||||
let profile = mapping(
|
||||
"dns: {nameserver: [9.9.9.9], nameserver-policy: {profile.example: 9.9.9.9}}\n\
|
||||
hosts: {profile.example: 192.0.2.1}",
|
||||
);
|
||||
let settings = mapping(
|
||||
"dns: {ipv6: true, nameserver: [8.8.8.8], nameserver-policy: {settings.example: 8.8.8.8}}\n\
|
||||
hosts: {settings.example: 192.0.2.2}",
|
||||
);
|
||||
let merge = mapping(
|
||||
"dns: {ipv6: false, nameserver-policy: {merge.example: 1.1.1.1}}\n\
|
||||
hosts: {merge.example: 192.0.2.3}",
|
||||
);
|
||||
|
||||
for enabled in [false, true] {
|
||||
let (config, owns_ipv6) = if enabled {
|
||||
super::merge_dns_config(profile.clone(), settings.clone())
|
||||
} else {
|
||||
(profile.clone(), false)
|
||||
};
|
||||
let authoritative = AuthoritativeFields::capture(&config, &[], owns_ipv6);
|
||||
let result = authoritative.enforce(super::use_merge(&merge, config));
|
||||
|
||||
assert_eq!(result["dns"]["nameserver-policy"], merge["dns"]["nameserver-policy"]);
|
||||
assert_eq!(result["hosts"], merge["hosts"]);
|
||||
let source = if enabled { &settings } else { &profile };
|
||||
assert_eq!(result["dns"]["nameserver"], source["dns"]["nameserver"]);
|
||||
assert_eq!(result["dns"]["ipv6"], serde_yaml_ng::Value::from(enabled));
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn manual_overrides_follow_expected_priority() {
|
||||
let mut config = mapping(
|
||||
|
||||
Reference in New Issue
Block a user