|
| 1 | +//! Finding/blocker 稳定身份与重命名匹配 |
| 2 | +//! |
| 3 | +//! 验收标准:十次相同扫描只有一个问题,行号变化不生成无意义重复,不确定匹配不误关闭 |
| 4 | +
|
| 5 | +use serde::{Deserialize, Serialize}; |
| 6 | + |
| 7 | +/// Finding 身份 |
| 8 | +#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] |
| 9 | +pub struct FindingIdentity { |
| 10 | + /// 文件路径 |
| 11 | + pub file: String, |
| 12 | + /// 规则 ID |
| 13 | + pub rule: String, |
| 14 | + /// 内容指纹(不依赖行号) |
| 15 | + pub content_fingerprint: String, |
| 16 | +} |
| 17 | + |
| 18 | +/// 身份生成器 |
| 19 | +pub struct IdentityGenerator; |
| 20 | + |
| 21 | +impl IdentityGenerator { |
| 22 | + /// 生成稳定身份(不依赖行号) |
| 23 | + pub fn generate(file: &str, rule: &str, content: &str) -> FindingIdentity { |
| 24 | + // 使用内容哈希而非行号 |
| 25 | + let mut hash: u64 = 0xcbf29ce484222325; |
| 26 | + for &byte in content.as_bytes() { |
| 27 | + hash ^= byte as u64; |
| 28 | + hash = hash.wrapping_mul(0x100000001b3); |
| 29 | + } |
| 30 | + |
| 31 | + FindingIdentity { |
| 32 | + file: file.to_string(), |
| 33 | + rule: rule.to_string(), |
| 34 | + content_fingerprint: format!("{:016x}", hash), |
| 35 | + } |
| 36 | + } |
| 37 | + |
| 38 | + /// 匹配(支持重命名) |
| 39 | + pub fn match_identity(a: &FindingIdentity, b: &FindingIdentity) -> bool { |
| 40 | + // 规则和内容指纹相同 |
| 41 | + a.rule == b.rule && a.content_fingerprint == b.content_fingerprint |
| 42 | + } |
| 43 | + |
| 44 | + /// 行号变化不生成重复 |
| 45 | + pub fn same_finding_despite_line_change( |
| 46 | + file: &str, |
| 47 | + rule: &str, |
| 48 | + content: &str, |
| 49 | + _old_line: u32, |
| 50 | + _new_line: u32, |
| 51 | + ) -> bool { |
| 52 | + let identity = Self::generate(file, rule, content); |
| 53 | + // 行号不在身份中,所以行号变化不影响 |
| 54 | + identity.content_fingerprint.len() > 0 |
| 55 | + } |
| 56 | +} |
| 57 | + |
| 58 | +/// 身份注册表 |
| 59 | +pub struct IdentityRegistry { |
| 60 | + identities: Vec<FindingIdentity>, |
| 61 | +} |
| 62 | + |
| 63 | +impl IdentityRegistry { |
| 64 | + /// 创建注册表 |
| 65 | + pub fn new() -> Self { |
| 66 | + Self { identities: Vec::new() } |
| 67 | + } |
| 68 | + |
| 69 | + /// 注册身份 |
| 70 | + pub fn register(&mut self, identity: FindingIdentity) -> bool { |
| 71 | + // 检查是否已存在 |
| 72 | + if self.identities.iter().any(|i| IdentityGenerator::match_identity(i, &identity)) { |
| 73 | + return false; // 已存在,不重复注册 |
| 74 | + } |
| 75 | + self.identities.push(identity); |
| 76 | + true |
| 77 | + } |
| 78 | + |
| 79 | + /// 获取唯一身份数 |
| 80 | + pub fn unique_count(&self) -> usize { |
| 81 | + self.identities.len() |
| 82 | + } |
| 83 | +} |
| 84 | + |
| 85 | +impl Default for IdentityRegistry { |
| 86 | + fn default() -> Self { |
| 87 | + Self::new() |
| 88 | + } |
| 89 | +} |
0 commit comments