Skip to main content

cloudiful_redactor/
types.rs

1use serde::{Deserialize, Serialize};
2use std::ops::Range;
3
4#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
5#[serde(rename_all = "snake_case")]
6pub enum FindingKind {
7    Secret,
8    Domain,
9    Url,
10    Email,
11    Ip,
12    Cidr,
13    Phone,
14    Person,
15    Organization,
16}
17
18#[derive(Debug, Clone, Copy, PartialEq, Eq)]
19struct FindingKindMeta {
20    label: &'static str,
21    token_label: &'static str,
22    priority: u8,
23    containment_priority: u8,
24}
25
26impl FindingKind {
27    const fn meta(self) -> FindingKindMeta {
28        match self {
29            Self::Secret => FindingKindMeta {
30                label: "secret",
31                token_label: "SECRET",
32                priority: 100,
33                containment_priority: 75,
34            },
35            Self::Domain => FindingKindMeta {
36                label: "domain",
37                token_label: "DOMAIN",
38                priority: 70,
39                containment_priority: 80,
40            },
41            Self::Url => FindingKindMeta {
42                label: "url",
43                token_label: "URL",
44                priority: 90,
45                containment_priority: 100,
46            },
47            Self::Email => FindingKindMeta {
48                label: "email",
49                token_label: "EMAIL",
50                priority: 85,
51                containment_priority: 95,
52            },
53            Self::Ip => FindingKindMeta {
54                label: "ip",
55                token_label: "IP",
56                priority: 75,
57                containment_priority: 85,
58            },
59            Self::Cidr => FindingKindMeta {
60                label: "cidr",
61                token_label: "CIDR",
62                priority: 80,
63                containment_priority: 90,
64            },
65            Self::Phone => FindingKindMeta {
66                label: "phone",
67                token_label: "PHONE",
68                priority: 60,
69                containment_priority: 70,
70            },
71            Self::Person => FindingKindMeta {
72                label: "person",
73                token_label: "PERSON",
74                priority: 50,
75                containment_priority: 50,
76            },
77            Self::Organization => FindingKindMeta {
78                label: "organization",
79                token_label: "ORG",
80                priority: 45,
81                containment_priority: 45,
82            },
83        }
84    }
85
86    pub fn label(self) -> &'static str {
87        self.meta().label
88    }
89
90    pub fn token_label(self) -> &'static str {
91        self.meta().token_label
92    }
93
94    pub fn priority(self) -> u8 {
95        self.meta().priority
96    }
97
98    pub fn containment_priority(self) -> u8 {
99        self.meta().containment_priority
100    }
101}
102
103#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
104#[serde(rename_all = "snake_case")]
105pub enum FindingSource {
106    Rule,
107    Llm,
108}
109
110impl FindingSource {
111    pub fn bonus(self) -> u8 {
112        match self {
113            Self::Rule => 10,
114            Self::Llm => 0,
115        }
116    }
117}
118
119#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
120pub struct Finding {
121    pub kind: FindingKind,
122    pub source: FindingSource,
123    pub match_text: String,
124    pub normalized_key: String,
125    pub confidence: u8,
126    pub start: usize,
127    pub end: usize,
128}
129
130impl Finding {
131    pub fn range(&self) -> Range<usize> {
132        self.start..self.end
133    }
134
135    pub fn score(&self) -> u16 {
136        u16::from(self.kind.priority())
137            + u16::from(self.source.bonus())
138            + u16::from(self.confidence)
139    }
140}
141
142#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
143#[serde(rename_all = "snake_case")]
144pub enum ReplacementStrategy {
145    StructuredToken,
146}
147
148#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
149pub struct AppliedReplacement {
150    pub kind: FindingKind,
151    #[serde(skip_serializing)]
152    pub original: String,
153    pub replacement: String,
154    pub strategy: ReplacementStrategy,
155    pub display_value: Option<String>,
156}
157
158#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
159pub struct RedactionStats {
160    pub total_findings: usize,
161    pub applied_replacements: usize,
162    pub dropped_findings: usize,
163    pub llm_configured: bool,
164    pub llm_request_failed: bool,
165    pub llm_candidates_accepted: usize,
166    pub llm_candidates_rejected: usize,
167    pub llm_error: Option<String>,
168}
169
170#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
171pub struct RedactionResult {
172    pub redacted_text: String,
173    pub findings: Vec<Finding>,
174    pub applied_replacements: Vec<AppliedReplacement>,
175    pub stats: RedactionStats,
176}
177
178#[derive(Debug, Clone, PartialEq, Eq)]
179pub struct RedactionArtifact {
180    pub result: RedactionResult,
181    pub session: RedactionSession,
182}
183
184#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
185pub struct RestorationEntry {
186    pub token: String,
187    pub kind: FindingKind,
188    pub original: String,
189    pub replacement_hint: Option<String>,
190    pub occurrences: usize,
191}
192
193#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
194pub struct RedactionSession {
195    pub version: u32,
196    pub session_id: String,
197    pub fingerprint: String,
198    pub redacted_fingerprint: String,
199    pub redacted_text: String,
200    pub entries: Vec<RestorationEntry>,
201}
202
203#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
204pub struct RestoreResult {
205    pub restored_text: String,
206    pub restored_count: usize,
207    pub unresolved_tokens: Vec<String>,
208    pub validation_errors: Vec<String>,
209}
210
211impl RestoreResult {
212    pub fn is_valid(&self) -> bool {
213        self.validation_errors.is_empty() && self.unresolved_tokens.is_empty()
214    }
215}
216
217#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
218pub struct SessionEntrySummary {
219    pub token: String,
220    pub kind: FindingKind,
221    pub replacement_hint: Option<String>,
222    pub occurrences: usize,
223}
224
225#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
226pub struct SessionSummary {
227    pub version: u32,
228    pub session_id: String,
229    pub fingerprint: String,
230    pub redacted_fingerprint: String,
231    pub entry_count: usize,
232    pub entries: Vec<SessionEntrySummary>,
233}