clashctl_core/model/
proxy.rs

1use std::collections::HashMap;
2use std::ops::Deref;
3
4use serde::{Deserialize, Serialize};
5
6use super::TimeType;
7
8#[derive(Serialize, Deserialize, Clone, Debug, Default, PartialEq, Eq)]
9pub struct Proxies {
10    pub proxies: HashMap<String, Proxy>,
11}
12
13impl Proxies {
14    pub fn normal(&self) -> impl Iterator<Item = (&String, &Proxy)> {
15        self.iter().filter(|(_, x)| x.proxy_type.is_normal())
16    }
17
18    pub fn groups(&self) -> impl Iterator<Item = (&String, &Proxy)> {
19        self.iter().filter(|(_, x)| x.proxy_type.is_group())
20    }
21
22    pub fn selectors(&self) -> impl Iterator<Item = (&String, &Proxy)> {
23        self.iter().filter(|(_, x)| x.proxy_type.is_selector())
24    }
25
26    pub fn built_ins(&self) -> impl Iterator<Item = (&String, &Proxy)> {
27        self.iter().filter(|(_, x)| x.proxy_type.is_built_in())
28    }
29}
30
31impl Deref for Proxies {
32    type Target = HashMap<String, Proxy>;
33    fn deref(&self) -> &Self::Target {
34        &self.proxies
35    }
36}
37
38#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
39pub struct Proxy {
40    #[serde(rename = "type")]
41    pub proxy_type: ProxyType,
42    pub history: Vec<History>,
43    pub udp: Option<bool>,
44
45    // Only present in ProxyGroups
46    pub all: Option<Vec<String>>,
47    pub now: Option<String>,
48}
49
50#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
51pub struct History {
52    pub time: TimeType,
53    pub delay: u64,
54}
55
56#[derive(Serialize, Deserialize, Debug, PartialEq, PartialOrd, Eq, Ord, Clone, Copy)]
57#[cfg_attr(
58    feature = "enum_ext",
59    derive(strum::EnumString, strum::Display, strum::EnumVariantNames),
60    strum(ascii_case_insensitive)
61)]
62pub enum ProxyType {
63    // Built-In types
64    Direct,
65    Reject,
66    // ProxyGroups
67    Selector,
68    URLTest,
69    Fallback,
70    LoadBalance,
71    // Proxies
72    Shadowsocks,
73    Vmess,
74    ShadowsocksR,
75    Http,
76    Snell,
77    Trojan,
78    Socks5,
79    // Relay
80    Relay,
81    // Unknown
82    #[serde(other)]
83    Unknown,
84}
85
86impl ProxyType {
87    pub fn is_selector(&self) -> bool {
88        matches!(self, ProxyType::Selector)
89    }
90
91    pub fn is_group(&self) -> bool {
92        matches!(
93            self,
94            ProxyType::Selector
95                | ProxyType::URLTest
96                | ProxyType::Fallback
97                | ProxyType::LoadBalance
98                | ProxyType::Relay
99        )
100    }
101
102    pub fn is_built_in(&self) -> bool {
103        matches!(self, ProxyType::Direct | ProxyType::Reject)
104    }
105
106    pub fn is_normal(&self) -> bool {
107        matches!(
108            self,
109            ProxyType::Shadowsocks
110                | ProxyType::Vmess
111                | ProxyType::ShadowsocksR
112                | ProxyType::Http
113                | ProxyType::Snell
114                | ProxyType::Trojan
115                | ProxyType::Socks5
116        )
117    }
118}
119
120#[test]
121fn test_proxies() {
122    let proxy_kv = [
123        (
124            "test_a".to_owned(),
125            Proxy {
126                proxy_type: ProxyType::Direct,
127                history: vec![],
128                udp: Some(false),
129                all: None,
130                now: None,
131            },
132        ),
133        (
134            "test_b".to_owned(),
135            Proxy {
136                proxy_type: ProxyType::Selector,
137                history: vec![],
138                udp: Some(false),
139                all: Some(vec!["test_c".into()]),
140                now: Some("test_c".into()),
141            },
142        ),
143        (
144            "test_c".to_owned(),
145            Proxy {
146                proxy_type: ProxyType::Shadowsocks,
147                history: vec![],
148                udp: Some(false),
149                all: None,
150                now: None,
151            },
152        ),
153        (
154            "test_d".to_owned(),
155            Proxy {
156                proxy_type: ProxyType::Fallback,
157                history: vec![],
158                udp: Some(false),
159                all: Some(vec!["test_c".into()]),
160                now: Some("test_c".into()),
161            },
162        ),
163    ];
164    let proxies = Proxies {
165        proxies: HashMap::from(proxy_kv),
166    };
167    assert_eq!(
168        {
169            let mut tmp = proxies.groups().map(|x| x.0).collect::<Vec<_>>();
170            tmp.sort();
171            tmp
172        },
173        vec!["test_b", "test_d"]
174    );
175    assert_eq!(
176        proxies.built_ins().map(|x| x.0).collect::<Vec<_>>(),
177        vec!["test_a"]
178    );
179    assert_eq!(
180        proxies.normal().map(|x| x.0).collect::<Vec<_>>(),
181        vec!["test_c"]
182    );
183}