mirror of
https://github.com/clash-verge-rev/clash-verge-rev.git
synced 2026-04-16 23:40:32 +08:00
feat(proxy): auto-append new proxies to first selector group (#5965)
* feat(proxy): auto-append new proxies to first selector group * docs: Changelog.md
This commit is contained in:
parent
895e54f7ec
commit
a82bcbe86e
@ -29,5 +29,6 @@
|
|||||||
- 在 Linux NVIDIA 显卡环境下尝试禁用 WebKit DMABUF 渲染以规避潜在问题
|
- 在 Linux NVIDIA 显卡环境下尝试禁用 WebKit DMABUF 渲染以规避潜在问题
|
||||||
- Windows 下自启动改为计划任务实现
|
- Windows 下自启动改为计划任务实现
|
||||||
- 改进托盘和窗口操作频率限制实现
|
- 改进托盘和窗口操作频率限制实现
|
||||||
|
- 使用「编辑节点」添加节点时,自动将节点添加到第一个 `select` 类型的代理组末尾
|
||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use serde_yaml_ng::{Mapping, Sequence, Value};
|
use serde_yaml_ng::{Mapping, Sequence, Value};
|
||||||
|
use std::collections::HashSet;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
|
||||||
pub struct SeqMap {
|
pub struct SeqMap {
|
||||||
@ -8,6 +9,27 @@ pub struct SeqMap {
|
|||||||
pub delete: Vec<String>,
|
pub delete: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn collect_proxy_names(seq: &Sequence) -> Vec<String> {
|
||||||
|
seq.iter()
|
||||||
|
.filter_map(|item| match item {
|
||||||
|
Value::Mapping(map) => map.get("name").and_then(Value::as_str).map(str::to_owned),
|
||||||
|
Value::String(name) => Some(name.to_owned()),
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_selector_group(group_map: &Mapping) -> bool {
|
||||||
|
group_map
|
||||||
|
.get("type")
|
||||||
|
.and_then(Value::as_str)
|
||||||
|
.map(|value| {
|
||||||
|
let value = value.to_ascii_lowercase();
|
||||||
|
value == "select" || value == "selector"
|
||||||
|
})
|
||||||
|
.unwrap_or(false)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn use_seq(seq: SeqMap, mut config: Mapping, field: &str) -> Mapping {
|
pub fn use_seq(seq: SeqMap, mut config: Mapping, field: &str) -> Mapping {
|
||||||
let SeqMap {
|
let SeqMap {
|
||||||
prepend,
|
prepend,
|
||||||
@ -15,6 +37,18 @@ pub fn use_seq(seq: SeqMap, mut config: Mapping, field: &str) -> Mapping {
|
|||||||
delete,
|
delete,
|
||||||
} = seq;
|
} = seq;
|
||||||
|
|
||||||
|
let added_proxy_names = if field == "proxies" {
|
||||||
|
let mut names = collect_proxy_names(&prepend);
|
||||||
|
names.extend(collect_proxy_names(&append));
|
||||||
|
let mut seen = HashSet::new();
|
||||||
|
names
|
||||||
|
.into_iter()
|
||||||
|
.filter(|name| seen.insert(name.clone()))
|
||||||
|
.collect::<Vec<String>>()
|
||||||
|
} else {
|
||||||
|
Vec::new()
|
||||||
|
};
|
||||||
|
|
||||||
let mut new_seq = Sequence::new();
|
let mut new_seq = Sequence::new();
|
||||||
new_seq.extend(prepend);
|
new_seq.extend(prepend);
|
||||||
|
|
||||||
@ -48,10 +82,11 @@ pub fn use_seq(seq: SeqMap, mut config: Mapping, field: &str) -> Mapping {
|
|||||||
&& let Some(Value::Sequence(groups)) = config.get_mut("proxy-groups")
|
&& let Some(Value::Sequence(groups)) = config.get_mut("proxy-groups")
|
||||||
{
|
{
|
||||||
let mut new_groups = Sequence::new();
|
let mut new_groups = Sequence::new();
|
||||||
|
let mut appended_to_selector = false;
|
||||||
for group in groups {
|
for group in groups {
|
||||||
if let Value::Mapping(group_map) = group {
|
if let Value::Mapping(group_map) = group {
|
||||||
if let Some(Value::Sequence(proxies)) = group_map.get("proxies") {
|
let mut proxies_seq = group_map.get("proxies").and_then(Value::as_sequence).map(|proxies| {
|
||||||
let filtered_proxies: Sequence = proxies
|
proxies
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|p| {
|
.filter(|p| {
|
||||||
if let Value::String(name) = p {
|
if let Value::String(name) = p {
|
||||||
@ -61,8 +96,27 @@ pub fn use_seq(seq: SeqMap, mut config: Mapping, field: &str) -> Mapping {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
.cloned()
|
.cloned()
|
||||||
.collect();
|
.collect::<Sequence>()
|
||||||
group_map.insert(Value::String("proxies".into()), Value::Sequence(filtered_proxies));
|
});
|
||||||
|
|
||||||
|
if !appended_to_selector && !added_proxy_names.is_empty() && is_selector_group(group_map) {
|
||||||
|
let mut seq = proxies_seq.unwrap_or_else(Sequence::new);
|
||||||
|
let mut existing = seq
|
||||||
|
.iter()
|
||||||
|
.filter_map(Value::as_str)
|
||||||
|
.map(str::to_owned)
|
||||||
|
.collect::<HashSet<String>>();
|
||||||
|
for name in &added_proxy_names {
|
||||||
|
if existing.insert(name.clone()) {
|
||||||
|
seq.push(Value::String(name.clone()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
proxies_seq = Some(seq);
|
||||||
|
appended_to_selector = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(seq) = proxies_seq {
|
||||||
|
group_map.insert(Value::String("proxies".into()), Value::Sequence(seq));
|
||||||
}
|
}
|
||||||
new_groups.push(Value::Mapping(group_map.to_owned()));
|
new_groups.push(Value::Mapping(group_map.to_owned()));
|
||||||
} else {
|
} else {
|
||||||
@ -158,4 +212,74 @@ proxy-groups:
|
|||||||
);
|
);
|
||||||
assert_eq!(group2_proxies.len(), 0);
|
assert_eq!(group2_proxies.len(), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[allow(clippy::unwrap_used)]
|
||||||
|
#[allow(clippy::expect_used)]
|
||||||
|
fn test_add_new_proxies_to_first_selector_group() {
|
||||||
|
let config_str = r#"
|
||||||
|
proxies:
|
||||||
|
- name: "proxy1"
|
||||||
|
type: "ss"
|
||||||
|
proxy-groups:
|
||||||
|
- name: "group1"
|
||||||
|
type: "select"
|
||||||
|
proxies:
|
||||||
|
- "proxy1"
|
||||||
|
- name: "group2"
|
||||||
|
type: "select"
|
||||||
|
proxies:
|
||||||
|
- "proxy1"
|
||||||
|
"#;
|
||||||
|
let mut config: Mapping = serde_yaml_ng::from_str(config_str).expect("Failed to parse test config YAML");
|
||||||
|
|
||||||
|
let prepend: Sequence = serde_yaml_ng::from_str(
|
||||||
|
r#"
|
||||||
|
- name: "proxy3"
|
||||||
|
type: "ss"
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
.expect("Failed to parse prepend proxies");
|
||||||
|
|
||||||
|
let append: Sequence = serde_yaml_ng::from_str(
|
||||||
|
r#"
|
||||||
|
- name: "proxy4"
|
||||||
|
type: "vmess"
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
.expect("Failed to parse append proxies");
|
||||||
|
|
||||||
|
let seq = SeqMap {
|
||||||
|
prepend,
|
||||||
|
append,
|
||||||
|
delete: vec![],
|
||||||
|
};
|
||||||
|
|
||||||
|
config = use_seq(seq, config, "proxies");
|
||||||
|
|
||||||
|
let groups = config
|
||||||
|
.get("proxy-groups")
|
||||||
|
.expect("proxy-groups field should exist")
|
||||||
|
.as_sequence()
|
||||||
|
.expect("proxy-groups should be a sequence");
|
||||||
|
let group1_proxies = groups[0]
|
||||||
|
.as_mapping()
|
||||||
|
.expect("group should be a mapping")
|
||||||
|
.get("proxies")
|
||||||
|
.expect("group should have proxies")
|
||||||
|
.as_sequence()
|
||||||
|
.expect("group proxies should be a sequence");
|
||||||
|
let names: Vec<&str> = group1_proxies.iter().filter_map(Value::as_str).collect();
|
||||||
|
assert_eq!(names, vec!["proxy1", "proxy3", "proxy4"]);
|
||||||
|
|
||||||
|
let group2_proxies = groups[1]
|
||||||
|
.as_mapping()
|
||||||
|
.expect("group should be a mapping")
|
||||||
|
.get("proxies")
|
||||||
|
.expect("group should have proxies")
|
||||||
|
.as_sequence()
|
||||||
|
.expect("group proxies should be a sequence");
|
||||||
|
let names: Vec<&str> = group2_proxies.iter().filter_map(Value::as_str).collect();
|
||||||
|
assert_eq!(names, vec!["proxy1"]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user