libsubconverter/generator/yaml/
proxy_group_output.rs1use crate::models::{ProxyGroupConfig, ProxyGroupType};
2use serde::ser::SerializeMap;
3use serde::{Serialize, Serializer};
4use std::collections::HashMap;
5
6pub fn serialize_proxy_group<S>(
18 group: &ProxyGroupConfig,
19 proxies: &[String],
20 serializer: S,
21) -> Result<S::Ok, S::Error>
22where
23 S: Serializer,
24{
25 let mut map = serializer.serialize_map(None)?;
26
27 map.serialize_entry("name", &group.name)?;
29
30 let type_str = if group.group_type == ProxyGroupType::Smart {
32 "url-test"
33 } else {
34 group.type_str()
35 };
36 map.serialize_entry("type", &type_str)?;
37
38 match group.group_type {
40 ProxyGroupType::Select | ProxyGroupType::Relay => {
41 }
43 ProxyGroupType::LoadBalance => {
44 map.serialize_entry("strategy", &group.strategy_str())?;
46
47 if !group.lazy {
49 map.serialize_entry("lazy", &group.lazy)?;
50 }
51
52 map.serialize_entry("url", &group.url)?;
54
55 if group.interval > 0 {
56 map.serialize_entry("interval", &group.interval)?;
57 }
58
59 if group.tolerance > 0 {
60 map.serialize_entry("tolerance", &group.tolerance)?;
61 }
62 }
63 ProxyGroupType::Smart | ProxyGroupType::URLTest => {
64 if !group.lazy {
66 map.serialize_entry("lazy", &group.lazy)?;
67 }
68
69 map.serialize_entry("url", &group.url)?;
71
72 if group.interval > 0 {
73 map.serialize_entry("interval", &group.interval)?;
74 }
75
76 if group.tolerance > 0 {
77 map.serialize_entry("tolerance", &group.tolerance)?;
78 }
79 }
80 ProxyGroupType::Fallback => {
81 map.serialize_entry("url", &group.url)?;
83
84 if group.interval > 0 {
85 map.serialize_entry("interval", &group.interval)?;
86 }
87
88 if group.tolerance > 0 {
89 map.serialize_entry("tolerance", &group.tolerance)?;
90 }
91 }
92 ProxyGroupType::SSID => {
93 }
96 }
97
98 if group.disable_udp {
100 map.serialize_entry("disable-udp", &group.disable_udp)?;
101 }
102
103 if group.persistent {
104 map.serialize_entry("persistent", &group.persistent)?;
105 }
106
107 if group.evaluate_before_use {
108 map.serialize_entry("evaluate-before-use", &group.evaluate_before_use)?;
109 }
110
111 if !group.using_provider.is_empty() {
113 let provider_seq: Vec<&String> = group.using_provider.iter().collect();
114 map.serialize_entry("use", &provider_seq)?;
115 } else {
116 if !proxies.is_empty() {
118 map.serialize_entry("proxies", &proxies)?;
119 } else {
120 map.serialize_entry("proxies", &["DIRECT"])?;
122 }
123 }
124
125 map.end()
126}
127
128#[derive(Debug, Serialize)]
134pub struct ClashProxyGroup {
135 pub name: String,
137
138 #[serde(rename = "type")]
140 pub group_type: String,
141
142 #[serde(skip_serializing_if = "Vec::is_empty")]
144 pub proxies: Vec<String>,
145
146 #[serde(rename = "use", skip_serializing_if = "Vec::is_empty")]
148 pub using_provider: Vec<String>,
149
150 #[serde(skip_serializing_if = "String::is_empty")]
152 pub url: String,
153
154 #[serde(skip_serializing_if = "is_zero_u32")]
156 pub interval: u32,
157
158 #[serde(skip_serializing_if = "is_zero_u32")]
160 pub timeout: u32,
161
162 #[serde(skip_serializing_if = "is_zero_u32")]
164 pub tolerance: u32,
165
166 #[serde(skip_serializing_if = "String::is_empty")]
168 pub strategy: String,
169
170 #[serde(skip_serializing_if = "is_true")]
172 pub lazy: bool,
173
174 #[serde(rename = "disable-udp", skip_serializing_if = "is_false")]
176 pub disable_udp: bool,
177
178 #[serde(skip_serializing_if = "is_false")]
180 pub persistent: bool,
181
182 #[serde(rename = "evaluate-before-use", skip_serializing_if = "is_false")]
184 pub evaluate_before_use: bool,
185}
186
187fn is_zero_u32(val: &u32) -> bool {
189 *val == 0
190}
191
192fn is_true(val: &bool) -> bool {
193 *val
194}
195
196fn is_false(val: &bool) -> bool {
197 !*val
198}
199
200impl From<&ProxyGroupConfig> for ClashProxyGroup {
201 fn from(config: &ProxyGroupConfig) -> Self {
202 let type_str = if config.group_type == ProxyGroupType::Smart {
204 "url-test".to_string()
205 } else {
206 config.type_str().to_string()
207 };
208
209 let mut clash_group = ClashProxyGroup {
211 name: config.name.clone(),
212 group_type: type_str,
213 proxies: config.proxies.clone(),
214 using_provider: config.using_provider.clone(),
215 url: String::new(),
216 interval: 0,
217 timeout: 0,
218 tolerance: 0,
219 strategy: String::new(),
220 lazy: true, disable_udp: config.disable_udp,
222 persistent: config.persistent,
223 evaluate_before_use: config.evaluate_before_use,
224 };
225
226 match config.group_type {
228 ProxyGroupType::LoadBalance => {
229 clash_group.strategy = config.strategy_str().to_string();
230 clash_group.lazy = config.lazy;
231 clash_group.url = config.url.clone();
232 clash_group.interval = config.interval;
233 clash_group.tolerance = config.tolerance;
234 }
235 ProxyGroupType::URLTest | ProxyGroupType::Smart | ProxyGroupType::Fallback => {
236 clash_group.url = config.url.clone();
237 clash_group.interval = config.interval;
238 clash_group.tolerance = config.tolerance;
239
240 if matches!(
242 config.group_type,
243 ProxyGroupType::URLTest | ProxyGroupType::Smart
244 ) {
245 clash_group.lazy = config.lazy;
246 }
247 }
248 _ => {} }
250
251 if clash_group.proxies.is_empty() && clash_group.using_provider.is_empty() {
253 clash_group.proxies = vec!["DIRECT".to_string()];
254 }
255
256 clash_group
257 }
258}
259
260pub fn convert_proxy_groups(
262 group_configs: &[ProxyGroupConfig],
263 filtered_nodes_map: Option<&HashMap<String, Vec<String>>>,
264) -> Vec<ClashProxyGroup> {
265 let mut clash_groups = Vec::with_capacity(group_configs.len());
266
267 for group in group_configs {
268 let mut clash_group = ClashProxyGroup::from(group);
269
270 if let Some(filtered_map) = filtered_nodes_map {
272 if let Some(filtered_nodes) = filtered_map.get(&group.name) {
273 clash_group.proxies = filtered_nodes.clone();
274
275 if clash_group.proxies.is_empty() && clash_group.using_provider.is_empty() {
277 clash_group.proxies = vec!["DIRECT".to_string()];
278 }
279 }
280 }
281
282 clash_groups.push(clash_group);
283 }
284
285 clash_groups
286}
287
288pub fn example_clash_groups() -> Vec<ClashProxyGroup> {
330 let mut groups = Vec::new();
332
333 let mut select_group = ProxyGroupConfig::new("Proxy".to_string(), ProxyGroupType::Select);
335 select_group.proxies = vec![
336 "Hong Kong".to_string(),
337 "Singapore".to_string(),
338 "US".to_string(),
339 ];
340 groups.push(select_group);
341
342 let mut urltest_group = ProxyGroupConfig::new("Auto".to_string(), ProxyGroupType::URLTest);
344 urltest_group.url = "http://www.gstatic.com/generate_204".to_string();
345 urltest_group.interval = 300;
346 urltest_group.proxies = vec!["Hong Kong".to_string(), "Singapore".to_string()];
347 groups.push(urltest_group);
348
349 let mut fallback_group =
351 ProxyGroupConfig::new("Fallback".to_string(), ProxyGroupType::Fallback);
352 fallback_group.url = "http://www.gstatic.com/generate_204".to_string();
353 fallback_group.interval = 300;
354 fallback_group.proxies = vec![
355 "Hong Kong".to_string(),
356 "Singapore".to_string(),
357 "US".to_string(),
358 ];
359 groups.push(fallback_group);
360
361 convert_proxy_groups(&groups, None)
363}