libsubconverter/models/
ruleset.rs

1use std::{
2    collections::HashMap,
3    sync::{Arc, RwLock},
4};
5
6/// Enum defining the type of ruleset
7#[derive(Debug, Clone, Copy, PartialEq, Eq)]
8pub enum RulesetType {
9    Surge,
10    Quanx,
11    ClashDomain,
12    ClashIpcidr,
13    ClashClassical,
14}
15
16impl Default for RulesetType {
17    fn default() -> Self {
18        RulesetType::Surge
19    }
20}
21
22/// Mapping from URL prefix to ruleset type
23pub type RulesetMapping = HashMap<String, RulesetType>;
24
25/// Available ruleset types with their prefixes
26pub static RULESET_TYPES: once_cell::sync::Lazy<RulesetMapping> =
27    once_cell::sync::Lazy::new(|| {
28        let mut types = RulesetMapping::new();
29        types.insert("clash-domain:".to_string(), RulesetType::ClashDomain);
30        types.insert("clash-ipcidr:".to_string(), RulesetType::ClashIpcidr);
31        types.insert("clash-classical:".to_string(), RulesetType::ClashClassical);
32        types.insert("quanx:".to_string(), RulesetType::Quanx);
33        types.insert("surge:".to_string(), RulesetType::Surge);
34        types
35    });
36
37/// Find a ruleset type based on a URL
38///
39/// Similar to the C++ implementation, this function looks for a matching
40/// ruleset type based on the prefix of a URL
41pub fn get_ruleset_type_from_url(url: &str) -> Option<RulesetType> {
42    for (prefix, ruleset_type) in RULESET_TYPES.iter() {
43        if url.starts_with(prefix) {
44            return Some(ruleset_type.clone());
45        }
46    }
47    None
48}
49
50#[derive(Debug, Clone, Default, PartialEq)]
51pub struct RulesetConfig {
52    pub group: String,
53    pub url: String,
54    pub interval: u32,
55}
56
57pub type RulesetConfigs = Vec<RulesetConfig>;
58
59/// Represents a ruleset with its metadata and content
60/// Matches the C++ struct RulesetContent:
61/// ```cpp
62/// struct RulesetContent {
63///     std::string rule_group;
64///     std::string rule_path;
65///     std::string rule_path_typed;
66///     int rule_type = RULESET_SURGE;
67///     std::shared_future<std::string> rule_content;
68///     int update_interval = 0;
69/// };
70/// ```
71#[derive(Debug, Clone)]
72pub struct RulesetContent {
73    pub group: String,           // rule_group in C++
74    pub rule_path: String,       // rule_path in C++
75    pub rule_path_typed: String, // rule_path_typed in C++
76    pub rule_type: RulesetType,  // rule_type in C++
77
78    // Similar to std::shared_future<std::string> in C++
79    // Arc provides shared ownership, RwLock provides interior mutability,
80    // Option allows for content to be present or not
81    pub rule_content: Arc<RwLock<Option<String>>>,
82
83    pub update_interval: u32, // update_interval in C++
84}
85
86impl RulesetContent {
87    /// Create a new empty ruleset
88    pub fn new(rule_path: &str, group: &str) -> Self {
89        RulesetContent {
90            group: group.to_string(),
91            rule_path: rule_path.to_string(),
92            rule_path_typed: rule_path.to_string(),
93            rule_type: RulesetType::default(),
94            rule_content: Arc::new(RwLock::new(None)),
95            update_interval: 0,
96        }
97    }
98
99    /// Get rule content - simulates the std::shared_future<std::string> rule_content.get() in C++
100    /// Returns a reference to the actual content or an empty string if not available
101    pub fn get_rule_content(&self) -> String {
102        // Try to read the content, return empty string if lock can't be acquired or content is None
103        match self.rule_content.read() {
104            Ok(guard) => match &*guard {
105                Some(content) => content.clone(),
106                None => String::new(),
107            },
108            Err(_) => String::new(),
109        }
110    }
111
112    /// Set the rule content
113    /// Simulates setting the promise value that would fulfill the future in C++
114    pub fn set_rule_content(&mut self, content: &str) {
115        if let Ok(mut guard) = self.rule_content.write() {
116            *guard = Some(content.to_string());
117        }
118    }
119
120    /// Check if rule content has been set
121    /// Simulates std::shared_future::valid() in C++
122    pub fn has_rule_content(&self) -> bool {
123        match self.rule_content.read() {
124            Ok(guard) => guard.is_some(),
125            Err(_) => false,
126        }
127    }
128}
129
130/// Parse a ruleset file
131pub fn parse_ruleset(content: &str, group: &str) -> RulesetContent {
132    let mut ruleset = RulesetContent::new("", group);
133    ruleset.set_rule_content(content);
134    ruleset
135}