libsubconverter/settings/external/
external_struct.rs

1use serde_yaml;
2use std::collections::HashMap;
3use toml;
4
5use crate::models::{ProxyGroupConfig, RegexMatchConfig, RulesetConfig};
6use crate::settings::Settings;
7use crate::utils::file::load_content_async;
8// TODO: Implement template rendering module similar to C++ render_template function
9
10use super::ini_external::IniExternalSettings;
11use super::toml_external::TomlExternalSettings;
12use super::yaml_external::YamlExternalSettings;
13
14/// External configuration structure with flattened fields
15#[derive(Debug, Clone, Default)]
16pub struct ExternalSettings {
17    // Rule bases
18    pub clash_rule_base: String,
19    pub surge_rule_base: String,
20    pub surfboard_rule_base: String,
21    pub mellow_rule_base: String,
22    pub quan_rule_base: String,
23    pub quanx_rule_base: String,
24    pub loon_rule_base: String,
25    pub sssub_rule_base: String,
26    pub singbox_rule_base: String,
27
28    // Rule generation options
29    pub enable_rule_generator: Option<bool>,
30    pub overwrite_original_rules: Option<bool>,
31
32    // Emoji settings
33    pub add_emoji: Option<bool>,
34    pub remove_old_emoji: Option<bool>,
35    pub emojis: Vec<RegexMatchConfig>,
36
37    // Filtering
38    pub include_remarks: Vec<String>,
39    pub exclude_remarks: Vec<String>,
40    // #[serde(default, deserialize_with = "deserialize_rulesets")]
41    pub custom_rulesets: Vec<RulesetConfig>,
42    pub custom_proxy_groups: Vec<ProxyGroupConfig>,
43
44    // Node operations
45    pub rename_nodes: Vec<RegexMatchConfig>,
46
47    // Template arguments
48    pub tpl_args: Option<HashMap<String, String>>,
49}
50
51impl ExternalSettings {
52    pub fn new() -> Self {
53        Self::default()
54    }
55
56    /// Load external configuration from file or URL
57    // pub fn load_from_file_sync(path: &str) -> Result<Self, Box<dyn std::error::Error>> {
58    //     // Load content from file or URL
59    //     let _content = load_content(path)?;
60
61    //     Self::parse_content(&_content)
62    // }
63
64    /// Load external configuration from file or URL asynchronously
65    pub async fn load_from_file(path: &str) -> Result<Self, Box<dyn std::error::Error>> {
66        // Load content from file or URL asynchronously
67        let _content = load_content_async(path).await?;
68
69        Self::parse_content(&_content).await
70    }
71
72    /// Parse the content and return an ExternalSettings object
73    async fn parse_content(content: &str) -> Result<Self, Box<dyn std::error::Error>> {
74        // TODO: Implement template rendering here
75        // In C++: if(render_template(config, *ext.tpl_args, base_content, global.templatePath) != 0)
76        //           base_content = config;
77
78        // Try YAML format first
79        if content.contains("custom:") {
80            let mut yaml_settings: YamlExternalSettings = serde_yaml::from_str(content)?;
81            yaml_settings.process_imports().await?;
82            // Convert to ExternalSettings
83            let config = Self::from(yaml_settings);
84            return Ok(config);
85        }
86
87        if toml::from_str::<toml::Value>(content).is_ok() {
88            let mut toml_settings: TomlExternalSettings = toml::from_str(content)?;
89            toml_settings.process_imports().await?;
90            // Convert to ExternalSettings
91            let config = Self::from(toml_settings);
92            return Ok(config);
93        }
94
95        // Fall back to INI format
96        let mut ini_settings = IniExternalSettings::new();
97        match ini_settings.load_from_ini(content) {
98            Ok(_) => {
99                // Process any imports
100                ini_settings.process_imports().await?;
101                // Convert to ExternalSettings
102                let config = Self::from(ini_settings);
103                return Ok(config);
104            }
105            Err(e) => Err(format!("Failed to parse external config as INI: {}", e).into()),
106        }
107    }
108
109    /// Validate rulesets count
110    pub fn validate_rulesets(&self) -> Result<(), Box<dyn std::error::Error>> {
111        let settings = Settings::current();
112        if settings.max_allowed_rulesets > 0
113            && self.custom_rulesets.len() > settings.max_allowed_rulesets
114        {
115            return Err("Ruleset count in external config has exceeded limit.".into());
116        }
117        Ok(())
118    }
119}