Skip to main content

rswappalyzer_engine/core/
pattern.rs

1use rustc_hash::FxHashMap;
2use serde::{Deserialize, Serialize};
3use crate::{MatchScope, core::cached_rule::CachedScopeRule};
4
5use super::enums::{MatchCondition, MatchType};
6
7/// 单条预处理模式
8#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
9pub struct Pattern {
10    pub pattern: String,
11    pub match_type: MatchType,
12    pub version_template: Option<String>,
13    pub confidence: u8,
14}
15
16/// KV规则结构体(Header/Meta/Cookie专用)
17#[derive(Debug, Clone, PartialEq)]
18pub struct KeyedPattern {
19    pub key: String,      // KV规则的键名
20    pub pattern: Pattern, // 具体的匹配模式
21}
22
23impl From<(String, Pattern)> for KeyedPattern {
24    fn from((key, pattern): (String, Pattern)) -> Self {
25        KeyedPattern { key, pattern }
26    }
27}
28
29/// 匹配规则集合,按作用域聚合的规则组
30#[derive(Debug, Clone, PartialEq, Default)]
31pub struct MatchRuleSet {
32    pub condition: MatchCondition,         // 匹配条件(And/Or)
33    pub list_patterns: Vec<Pattern>,       // 列表型规则(Url/Html/Script/ScriptSrc)
34    pub keyed_patterns: Vec<KeyedPattern>, // KV型规则(Meta/Header/Cookie)
35}
36
37impl MatchRuleSet {
38    pub fn new() -> Self {
39        Self::default()
40    }
41
42    // 添加列表型规则
43    pub fn add_list_pattern(&mut self, pattern: Pattern) {
44        self.list_patterns.push(pattern);
45    }
46
47    // 添加KV型规则
48    pub fn add_keyed_pattern(&mut self, keyed_pattern: KeyedPattern) {
49        self.keyed_patterns.push(keyed_pattern);
50    }
51
52    /// 新建指定条件的 MatchRuleSet
53    pub fn with_condition(condition: MatchCondition) -> Self {
54        Self {
55            condition,
56            list_patterns: Vec::new(),
57            keyed_patterns: Vec::new(),
58        }
59    }
60
61    // 从缓存结构转换为运行时结构(无字符串拆分)
62    pub fn from_cached(scope: &MatchScope, cached: CachedScopeRule) -> Self {
63        let mut rule_set = Self::with_condition(cached.condition);
64        match scope {
65            MatchScope::Url | MatchScope::Html | MatchScope::Script | MatchScope::ScriptSrc => {
66                if let Some(patterns) = cached.list_patterns {
67                    rule_set.list_patterns = patterns;
68                }
69            }
70            MatchScope::Header | MatchScope::Cookie | MatchScope::Meta | MatchScope::Js=> {
71                if let Some(keyed) = cached.keyed_patterns {
72                    // 用 flat_map 替代 map + flatten,减少一层 collect
73                    rule_set.keyed_patterns = keyed.into_iter()
74                        .flat_map(|(k, v)| v.into_iter().map(move |p| KeyedPattern::from((k.clone(), p))))
75                        .collect();
76                }
77            }
78        }
79        rule_set
80    }
81
82    // 从运行时结构转换为缓存结构(无字符串拼接)
83    pub fn to_cached(&self, scope: &MatchScope) -> CachedScopeRule {
84        let mut cached = CachedScopeRule {
85            condition: self.condition.clone(),
86            list_patterns: None,
87            keyed_patterns: None,
88        };
89        match scope {
90            MatchScope::Url | MatchScope::Html | MatchScope::Script | MatchScope::ScriptSrc => {
91                if !self.list_patterns.is_empty() {
92                    cached.list_patterns = Some(self.list_patterns.clone());
93                }
94            }
95            MatchScope::Header | MatchScope::Cookie | MatchScope::Meta | MatchScope::Js => {
96                if !self.keyed_patterns.is_empty() {
97                    // 显式指定 HashMap 类型
98                    let mut keyed: FxHashMap<String, Vec<Pattern>> = FxHashMap::default();
99                    for kp in &self.keyed_patterns {
100                        keyed.entry(kp.key.clone())
101                            .or_default()
102                            .push(kp.pattern.clone());
103                    }
104                    cached.keyed_patterns = Some(keyed);
105                }
106            }
107        }
108        cached
109    }
110}