libsubconverter/settings/
ini_bindings.rs

1use crate::models::cron::{CronTaskConfig, CronTaskConfigs};
2use crate::models::proxy_group_config::{ProxyGroupConfig, ProxyGroupConfigs, ProxyGroupType};
3use crate::models::regex_match_config::{RegexMatchConfig, RegexMatchConfigs};
4use crate::models::ruleset::{RulesetConfig, RulesetConfigs};
5use crate::utils::string::starts_with;
6
7/// Parse group times string into interval, timeout, and tolerance values
8/// Similar to the C++ parseGroupTimes function in settings.h
9fn parse_group_times(src: &str, interval: &mut u32, timeout: &mut u32, tolerance: &mut u32) {
10    let parts: Vec<&str> = src.split(',').collect();
11    if parts.len() >= 1 {
12        if let Ok(val) = parts[0].parse::<u32>() {
13            *interval = val;
14        }
15    }
16    if parts.len() >= 2 {
17        if let Ok(val) = parts[1].parse::<u32>() {
18            *timeout = val;
19        }
20    }
21    if parts.len() >= 3 {
22        if let Ok(val) = parts[2].parse::<u32>() {
23            *tolerance = val;
24        }
25    }
26}
27
28/// Trait for parsing types from INI string arrays
29pub trait FromIni<T> {
30    /// Convert from INI string array to the target type
31    fn from_ini(arr: &[String]) -> T;
32}
33
34/// Trait for parsing types from INI string arrays with a delimiter
35pub trait FromIniWithDelimiter<T> {
36    /// Convert from INI string array with a custom delimiter to the target type
37    fn from_ini_with_delimiter(arr: &[String], delimiter: &str) -> T;
38}
39
40/// Implementation for parsing ProxyGroupConfig from INI string lines
41impl FromIni<ProxyGroupConfigs> for ProxyGroupConfigs {
42    fn from_ini(arr: &[String]) -> ProxyGroupConfigs {
43        let mut confs = Vec::new();
44
45        for x in arr {
46            let mut rules_upper_bound;
47            let mut conf = ProxyGroupConfig::default();
48
49            let v_array: Vec<&str> = x.split('`').collect();
50            if v_array.len() < 3 {
51                continue;
52            }
53
54            conf.name = v_array[0].to_string();
55            let type_str = v_array[1];
56
57            rules_upper_bound = v_array.len();
58            conf.group_type = match type_str {
59                "select" => ProxyGroupType::Select,
60                "relay" => ProxyGroupType::Relay,
61                "url-test" => ProxyGroupType::URLTest,
62                "fallback" => ProxyGroupType::Fallback,
63                "load-balance" => ProxyGroupType::LoadBalance,
64                "ssid" => ProxyGroupType::SSID,
65                "smart" => ProxyGroupType::Smart,
66                _ => ProxyGroupType::Select,
67            };
68
69            if conf.group_type == ProxyGroupType::URLTest
70                || conf.group_type == ProxyGroupType::LoadBalance
71                || conf.group_type == ProxyGroupType::Fallback
72            {
73                if rules_upper_bound < 5 {
74                    continue;
75                }
76                rules_upper_bound -= 2;
77                conf.url = v_array[rules_upper_bound].to_string();
78
79                let mut interval = 0;
80                let mut timeout = 5;
81                let mut tolerance = 0;
82                parse_group_times(
83                    v_array[rules_upper_bound + 1],
84                    &mut interval,
85                    &mut timeout,
86                    &mut tolerance,
87                );
88                conf.interval = interval;
89                conf.timeout = timeout;
90                conf.tolerance = tolerance;
91            }
92
93            for i in 2..rules_upper_bound {
94                if starts_with(v_array[i], "!!PROVIDER=") {
95                    let provider_list: Vec<&str> = v_array[i][11..].split(',').collect();
96                    for provider in provider_list {
97                        conf.using_provider.push(provider.to_string());
98                    }
99                } else {
100                    conf.proxies.push(v_array[i].to_string());
101                }
102            }
103
104            confs.push(conf);
105        }
106
107        confs
108    }
109}
110
111/// Implementation for parsing RulesetConfig from INI string lines
112impl FromIni<Vec<RulesetConfig>> for RulesetConfigs {
113    fn from_ini(arr: &[String]) -> Vec<RulesetConfig> {
114        let mut confs = Vec::new();
115
116        for x in arr {
117            let mut conf = RulesetConfig::default();
118
119            let pos = x.find(',');
120            if pos.is_none() {
121                continue;
122            }
123
124            let pos = pos.unwrap();
125            conf.group = x[..pos].to_string();
126
127            // Handle the case where URL starts with "[]"
128            if x.len() > pos + 3 && &x[pos + 1..pos + 3] == "[]" {
129                conf.url = x[pos + 1..].to_string();
130                confs.push(conf);
131                continue;
132            }
133
134            // Check if there's an interval specified
135            let epos = x.rfind(',');
136            if pos != epos.unwrap_or(pos) {
137                let epos = epos.unwrap();
138                if let Ok(interval) = x[epos + 1..].parse::<u32>() {
139                    conf.interval = interval;
140                }
141                conf.url = x[pos + 1..epos].to_string();
142            } else {
143                conf.url = x[pos + 1..].to_string();
144            }
145
146            confs.push(conf);
147        }
148
149        confs
150    }
151}
152
153/// Implementation for parsing RegexMatchConfig from INI string lines with
154/// delimiter
155impl FromIniWithDelimiter<RegexMatchConfigs> for RegexMatchConfigs {
156    fn from_ini_with_delimiter(arr: &[String], delimiter: &str) -> RegexMatchConfigs {
157        let mut confs = Vec::new();
158
159        for x in arr {
160            let mut conf = RegexMatchConfig::new(String::new(), String::new(), String::new());
161
162            // Handle script case
163            if starts_with(x, "script:") {
164                conf.script = x[7..].to_string();
165                confs.push(conf);
166                continue;
167            }
168
169            // Handle match/replace case
170            let pos = x.rfind(delimiter);
171            conf._match = x[..pos.unwrap_or(x.len())].to_string();
172
173            if let Some(p) = pos {
174                if p < x.len() - 1 {
175                    conf.replace = x[p + delimiter.len()..].to_string();
176                }
177            }
178
179            conf.compile();
180
181            confs.push(conf);
182        }
183
184        confs
185    }
186}
187
188/// Implementation for parsing CronTaskConfig from INI string lines
189impl FromIni<CronTaskConfigs> for CronTaskConfigs {
190    fn from_ini(arr: &[String]) -> CronTaskConfigs {
191        let mut confs = Vec::new();
192
193        for x in arr {
194            let mut conf = CronTaskConfig::default();
195
196            let v_array: Vec<&str> = x.split('`').collect();
197            if v_array.len() < 3 {
198                continue;
199            }
200
201            conf.name = v_array[0].to_string();
202            conf.cron_exp = v_array[1].to_string();
203            conf.path = v_array[2].to_string();
204
205            if v_array.len() > 3 {
206                if let Ok(timeout) = v_array[3].parse::<u32>() {
207                    conf.timeout = timeout;
208                }
209            }
210
211            confs.push(conf);
212        }
213
214        confs
215    }
216}