libsubconverter/models/
proxy_group_config.rs

1/// Type of proxy group
2#[derive(Debug, Clone, PartialEq, Eq)]
3pub enum ProxyGroupType {
4    Select,
5    URLTest,
6    Fallback,
7    LoadBalance,
8    Relay,
9    SSID,
10    Smart,
11}
12
13impl ProxyGroupType {
14    /// Get string representation of the proxy group type
15    pub fn as_str(&self) -> &'static str {
16        match self {
17            ProxyGroupType::Select => "select",
18            ProxyGroupType::URLTest => "url-test",
19            ProxyGroupType::LoadBalance => "load-balance",
20            ProxyGroupType::Fallback => "fallback",
21            ProxyGroupType::Relay => "relay",
22            ProxyGroupType::SSID => "ssid",
23            ProxyGroupType::Smart => "smart",
24        }
25    }
26}
27
28/// Load balancing strategy
29#[derive(Debug, Clone, PartialEq, Eq)]
30pub enum BalanceStrategy {
31    ConsistentHashing,
32    RoundRobin,
33}
34
35impl BalanceStrategy {
36    /// Get string representation of the balance strategy
37    pub fn as_str(&self) -> &'static str {
38        match self {
39            BalanceStrategy::ConsistentHashing => "consistent-hashing",
40            BalanceStrategy::RoundRobin => "round-robin",
41        }
42    }
43}
44
45/// Configuration for a proxy group
46#[derive(Debug, Clone)]
47pub struct ProxyGroupConfig {
48    /// Name of the proxy group
49    pub name: String,
50    /// Type of the proxy group
51    pub group_type: ProxyGroupType,
52    /// List of proxy names in this group
53    pub proxies: Vec<String>,
54    /// List of provider names used by this group
55    pub using_provider: Vec<String>,
56    /// URL for testing
57    pub url: String,
58    /// Interval in seconds between tests
59    pub interval: u32,
60    /// Timeout in seconds for tests
61    pub timeout: u32,
62    /// Tolerance value for tests
63    pub tolerance: u32,
64    /// Strategy for load balancing
65    pub strategy: BalanceStrategy,
66    /// Whether to use lazy loading
67    pub lazy: bool,
68    /// Whether to disable UDP support
69    pub disable_udp: bool,
70    /// Whether to persist connections
71    pub persistent: bool,
72    /// Whether to evaluate before use
73    pub evaluate_before_use: bool,
74}
75
76impl Default for ProxyGroupConfig {
77    fn default() -> Self {
78        Self {
79            name: String::new(),
80            group_type: ProxyGroupType::Select,
81            proxies: Vec::new(),
82            using_provider: Vec::new(),
83            url: String::new(),
84            interval: 0,
85            timeout: 0,
86            tolerance: 0,
87            strategy: BalanceStrategy::ConsistentHashing,
88            lazy: false,
89            disable_udp: false,
90            persistent: false,
91            evaluate_before_use: false,
92        }
93    }
94}
95
96impl ProxyGroupConfig {
97    /// Create a new proxy group config
98    pub fn new(name: String, group_type: ProxyGroupType) -> Self {
99        Self {
100            name,
101            group_type,
102            ..Default::default()
103        }
104    }
105
106    /// Get string representation of the group type
107    pub fn type_str(&self) -> &'static str {
108        self.group_type.as_str()
109    }
110
111    /// Get string representation of the balance strategy
112    pub fn strategy_str(&self) -> &'static str {
113        self.strategy.as_str()
114    }
115}
116
117/// A collection of proxy group configurations
118pub type ProxyGroupConfigs = Vec<ProxyGroupConfig>;
119
120use serde::ser::SerializeStruct;
121use serde::{Serialize, Serializer};
122
123impl Serialize for ProxyGroupConfig {
124    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
125    where
126        S: Serializer,
127    {
128        // Figure out how many fields we'll have based on the proxy group type
129        let mut field_count = 2; // name and type always present
130
131        // Count conditional fields based on group type
132        match self.group_type {
133            ProxyGroupType::LoadBalance => {
134                field_count += 4; // strategy, url, interval, tolerance
135                if !self.lazy {
136                    field_count += 1; // lazy
137                }
138            }
139            ProxyGroupType::URLTest | ProxyGroupType::Smart => {
140                field_count += 2; // url, interval
141                if !self.lazy {
142                    field_count += 1; // lazy
143                }
144                if self.tolerance > 0 {
145                    field_count += 1; // tolerance
146                }
147            }
148            ProxyGroupType::Fallback => {
149                field_count += 2; // url, interval
150                if self.tolerance > 0 {
151                    field_count += 1; // tolerance
152                }
153            }
154            _ => {}
155        }
156
157        // Add count for other optional fields
158        if self.disable_udp {
159            field_count += 1;
160        }
161        if self.persistent {
162            field_count += 1;
163        }
164        if self.evaluate_before_use {
165            field_count += 1;
166        }
167
168        // Add fields for proxies and provider
169        if !self.proxies.is_empty() {
170            field_count += 1;
171        }
172        if !self.using_provider.is_empty() {
173            field_count += 1;
174        }
175
176        // Create serialization struct
177        let mut state = serializer.serialize_struct("ProxyGroup", field_count)?;
178
179        // Always include name
180        state.serialize_field("name", &self.name)?;
181
182        // Handle type (with special case for Smart)
183        let type_str = if self.group_type == ProxyGroupType::Smart {
184            "url-test"
185        } else {
186            self.type_str()
187        };
188        state.serialize_field("type", &type_str)?;
189
190        // Add fields based on type
191        match self.group_type {
192            ProxyGroupType::LoadBalance => {
193                // Load balancing specific fields
194                state.serialize_field("strategy", &self.strategy_str())?;
195                if !self.lazy {
196                    state.serialize_field("lazy", &self.lazy)?;
197                }
198                state.serialize_field("url", &self.url)?;
199                if self.interval > 0 {
200                    state.serialize_field("interval", &self.interval)?;
201                }
202                if self.tolerance > 0 {
203                    state.serialize_field("tolerance", &self.tolerance)?;
204                }
205            }
206            ProxyGroupType::URLTest | ProxyGroupType::Smart => {
207                // URL-test specific fields
208                if !self.lazy {
209                    state.serialize_field("lazy", &self.lazy)?;
210                }
211                state.serialize_field("url", &self.url)?;
212                if self.interval > 0 {
213                    state.serialize_field("interval", &self.interval)?;
214                }
215                if self.tolerance > 0 {
216                    state.serialize_field("tolerance", &self.tolerance)?;
217                }
218            }
219            ProxyGroupType::Fallback => {
220                // Fallback specific fields
221                state.serialize_field("url", &self.url)?;
222                if self.interval > 0 {
223                    state.serialize_field("interval", &self.interval)?;
224                }
225                if self.tolerance > 0 {
226                    state.serialize_field("tolerance", &self.tolerance)?;
227                }
228            }
229            _ => {}
230        }
231
232        // Add optional common fields
233        if self.disable_udp {
234            state.serialize_field("disable-udp", &self.disable_udp)?;
235        }
236        if self.persistent {
237            state.serialize_field("persistent", &self.persistent)?;
238        }
239        if self.evaluate_before_use {
240            state.serialize_field("evaluate-before-use", &self.evaluate_before_use)?;
241        }
242
243        // Add proxies list if not empty
244        if !self.proxies.is_empty() {
245            state.serialize_field("proxies", &self.proxies)?;
246        }
247
248        // Add provider via "use" field if present
249        if !self.using_provider.is_empty() {
250            state.serialize_field("use", &self.using_provider)?;
251        }
252
253        state.end()
254    }
255}